当你还是一个码农的时候,每天都要编写多少行代码每次都要创建很多类的时候,每建立一次给你一点经验值,累计到十级的时候会发现我写了这很多代码为什么不能复用呢?
这时恭喜您进入转职阶段,由码农转成会放魔法的码农。。。(多么吊炸天技能)
觉醒吧!少年。
以王者荣耀为示例题材,写代码就是打游戏!
代码示例裢接:https://github.com/ruanch/Design-Pattern-For-Swift
1.奥义第一式·简单工厂模式(我在游戏中有一个英雄库,我想买啥就买啥)
在游戏里都有一个商店,商店里各种物品,今天以英雄库为例,写模式先从抽象开始,毕竟现在都以面向接口或面向协议开发了。
把英雄抽象成NERole协议,英雄有名字、血量、魔法量、价格,另外有获取该英雄信息方法。
protocol NERole {
var name:String{ get set }
var healthPoint:CGFloat{ get set } //血量值
var magicPoint:CGFloat{ get set } //魔法值
var price:CGFloat{ get set } //购买价格
func getRoleInfo() -> String //获取角色属性
}
接下来英雄按这个协议生成,这里分别召唤出阿珂、关羽、小乔和吕布四位大将。
//阿珂角色
class NERoleAKe: NSObject,NERole {
var name: String = ""
var price: CGFloat = 0.0
var magicPoint: CGFloat = 0.0
var healthPoint: CGFloat = 0.0
func getRoleInfo() -> String {
return "角色名称:\(name) ==> 血量: \(healthPoint) 魔法量:\(magicPoint) 购买价格:\(price)"
}
}
//关羽角色
class NERoleGuanYu: NSObject,NERole {
//类似代码
}
//小乔角色
class NERoleXiaoQiao: NSObject,NERole {
//类似代码
}
//吕布角色
class NERoleLvBu: NSObject,NERole {
//类似代码
}
接下来,我们需要一个工厂来创造出英雄
class NERoleFactory: NSObject {
//创建角色
static func createRole(roleType : String) -> NERole {
let roles = ["阿珂","关羽","小乔","吕布"]
let type = roles.index(of: roleType)!
switch type {
case 0:
return NERoleAKe()
case 1:
return NERoleGuanYu()
case 2:
return NERoleXiaoQiao()
case 3:
return NERoleLvBu()
default:
return NERoleAKe()
}
}
}
客户端代码:
var role:NERole = NERoleFactory .createRole(roleType: "阿珂")
role.name = "阿珂"
role.healthPoint = 100.0
role.magicPoint = 50.0
role.price = 13888.0
print(role.getRoleInfo())
var role1:NERole = NERoleFactory .createRole(roleType: "关羽")
role1.name = "关羽"
role1.healthPoint = 100.0
role1.magicPoint = 50.0
role1.price = 18888.0
print(role1.getRoleInfo())
打印结果:
角色名称:阿珂 ==> 血量: 100.0 魔法量:50.0 购买价格:13888.0
角色名称:关羽 ==> 血量: 100.0 魔法量:50.0 购买价格:18888.0
总结一下:传入相应的英雄名称就可以获取英雄实例出来,看下简单工厂的关系图
2.奥义第二式·工厂方法模式(再造一个英雄库)
写这个模式的目的是为什么,简单工厂和工厂方法的区别是什么。
简单工厂模式最大的优点在于工厂类包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。但是违背了开放-封闭原则(在文章最后给出解释)
看一下工厂方法的实现英雄库实例
首先角色库的实例代码都不变,NSRole、NSAkeRole、NSGuanYuRole、NSXiaoQiaoRole、NELvBuRole
现在扩展一下工厂类,将工厂抽出变成一个抽象工厂然后根据要生成的角色生成不同的角色工厂
抽象类:
//角色工厂
class NERoleFactory_FM: NSObject {
func createFactory() -> NERole_FM{
return NERoleAKe_FM()
}
}
具体实现类:
//阿珂角色工厂
class NERoleAKeFactory_FM: NERoleFactory_FM {
override func createFactory() -> NERole_FM{
return NERoleAKe_FM() //返回具体角色实例
}
}
//关羽角色工厂
class NERoleGuanYuFactory_FM: NERoleFactory_FM {
override func createFactory() -> NERole_FM{
return NERoleGuanYu_FM()
}
}
......
接下来看下,如何在客户端里调用:
//创建阿珂工厂
let akeFactory = NERoleAKeFactory_FM()
//工厂造角色
var akeRole = akeFactory.createFactory()
//给角色一此基础信息
akeRole.name = "阿珂"
akeRole.healthPoint = 100.0
akeRole.magicPoint = 50.0
akeRole.price = 13888.0
//执行具体动作
print(akeRole .getRoleInfo())
//创建关羽工厂
let guanYufactory = NERoleGuanYuFactory_FM()
//工厂造角色
var guanYuRole = guanYufactory.createFactory()
//给角色一此基础信息
guanYuRole.name = "关羽"
guanYuRole.healthPoint = 150.0
guanYuRole.magicPoint = 30.0
guanYuRole.price = 18888.0
//执行具体动作
print(guanYuRole .getRoleInfo())
如果你跟上面简单工厂对比一下,客户端的实现,会发现创建的实例的代码已经从工厂里移到客户端上面,所以相比简单工厂,增加一个新的实体角色只需要增加相应工厂类和实体类,以及这边的增加代码增加,达到不用修改工厂类。但是还是有一个问题能不能不改客户端代码,答案当然是:可以!利用反射机制来处理。
上一下工厂方法模式关系图
3.奥义第三式·抽象工厂模式(手机和电脑都可以操作英雄、防御塔、小兵)
现在平台越来越多,PC、手机、手表等等都可以运行同一款游戏,那么如果在起初没有考虑到扩展是不是这些只能运行在一个平台。如何在不动原来逻辑下,增加一个新的平台。抽象工厂就是干这个的。
首先把不同的平台抽象成一个抽象工厂类,里面存放创建角色、防御塔、小兵方法。
//抽象工厂
@objc protocol NEAbstractFactory {
@objc optional func createRole() -> NEOperationRole
@objc optional func createXiaoBin() -> NEOperationXiaoBin
@objc optional func createTower() -> NEOperationTower
}
接下来手机和PC去实现这个工厂
class NEMobileFactory: NEAbstractFactory {
func createRole() -> NEOperationRole {
return NEMobileOperationRole()
}
func createXiaoBin() -> NEOperationXiaoBin {
return NEMobileOperationXiaoBin()
}
func createTower() -> NEOperationTower {
return NEMobileOperationTower()
}
}
class NEPCFactory: NEAbstractFactory {
func createRole() -> NEOperationRole {
return NEPCOperationRole()
}
func createXiaoBin() -> NEOperationXiaoBin {
return NEPCOperationXiaoBin()
}
func createTower() -> NEOperationTower {
return NEPCOperationTower()
}
}
接下来每个平台都尊循创建协议,那么返回什么好?答案是实体类的操作, 所以抽象出来操作协议,这个应该是平台核心的一个接口,只要按照这一套接口可以转换成一个新平台。
@objc protocol NEOperationRole {
@objc optional func addRole(role:NERole_AF)
@objc optional func getRole() -> NERole_AF
}
那么不同平台实现不同操作方式
class NEMobileOperationRole: NEOperationRole {
func addRole(role: NERole_AF) {
print("在{手机}上面添加一个的《角色》对象")
}
func getRole() -> NERole_AF{
print("在{手机}上面获取一个的《角色》对象")
return NERole_AF()
}
}
class NEPCOperationRole: NEOperationRole {
func addRole(role: NERole_AF) {
print("在{电脑}上面添加一个的《角色》对象")
}
func getRole() -> NERole_AF{
print("在{电脑}上面获取一个的《角色》对象")
return NERole_AF()
}
}
防御塔操作类和小兵操作类,类似代码
.......
接下来就是平台模型,也就是操作的对象
//角色商品
class NERole_AF: NSObject {
var name: String = ""
var price: CGFloat = 0.0
var magicPoint: CGFloat = 0.0
var healthPoint: CGFloat = 0.0
func getRoleInfo() -> String {
return "角色名称:\(name) ==> 血量: \(healthPoint) 魔法量:\(magicPoint) 购买价格:\(price)"
}
}
防御塔实体类和小兵实体类,类似代码
.......
看一下客户端实现代码
//电脑工厂
let pcFactory:NEAbstractFactory = NEPCFactory()
//手机工厂
let mobileFactory:NEAbstractFactory = NEMobileFactory()
print("//======================角色=========================")
//造出操作角色对象
let pcOperationRole: NEOperationRole = pcFactory.createRole!()
//操作对象对角色进行操作
//添加一个角色
let role = NERole_AF()
pcOperationRole.addRole!(role: role)
//获取一个角色
let newRole = pcOperationRole.getRole!()
newRole.name = "电脑上的任意角色名称"
newRole.healthPoint = 100.0
newRole.magicPoint = 80.0
newRole.price = 18888.0
print(newRole.getRoleInfo())
//造出操作角色对象
let mobileOperationRole: NEOperationRole = mobileFactory.createRole!()
//操作对象对角色进行操作
//添加一个角色
let role1 = NERole_AF()
mobileOperationRole.addRole!(role: role1)
//获取一个角色
let newRole1 = mobileOperationRole.getRole!()
newRole1.name = "手机上的任意角色名称"
newRole1.healthPoint = 100.0
newRole1.magicPoint = 80.0
newRole1.price = 18888.0
print(newRole.getRoleInfo())
print("//======================小兵=========================")
//电脑造出操作小兵对象
let pcOperationXiaoBin: NEOperationXiaoBin = pcFactory.createXiaoBin!()
//操作对象对角色进行操作
//添加一个小兵
let xiaoBin = NEXiaoBin_AF()
pcOperationXiaoBin .addXiaoBin!(xiaoBin: xiaoBin)
//获取一个小兵
let newXiaoBin = pcOperationXiaoBin.getXiaoBin!()
newXiaoBin.name = "电脑上的步兵、弓箭手、炮兵"
newXiaoBin.healthPoint = 100.0
newXiaoBin.magicPoint = 80.0
print(newXiaoBin.getXaioBinInfo())
//手机造出操作小兵对象
let mobileOperationXiaoBin: NEOperationXiaoBin = mobileFactory.createXiaoBin!()
//操作对象对小兵进行操作
//添加一个小兵
let xiaoBin1 = NEXiaoBin_AF()
mobileOperationXiaoBin .addXiaoBin!(xiaoBin: xiaoBin1)
//获取一个小兵
let newXiaoBin1 = mobileOperationXiaoBin.getXiaoBin!()
newXiaoBin1.name = "手机上的任意角色名称"
newXiaoBin1.healthPoint = 100.0
newXiaoBin1.magicPoint = 80.0
print(newXiaoBin.getXaioBinInfo())
print("//======================塔=========================")
//电脑造出操作塔对象
let pcOperationTower: NEOperationTower = pcFactory.createTower!()
//操作对象对塔进行操作
//添加一个塔
let tower = NETower_AF()
pcOperationTower .addTower!(tower: tower)
//获取一个塔
let newtower = pcOperationTower.getTower!()
newtower.name = "电脑上的塔"
newtower.healthPoint = 100.0
newtower.defenseValue = 999.0
print(newtower.getTowerInfo())
//手机造出操作塔对象
let mobileOperationTower: NEOperationTower = mobileFactory.createTower!()
//操作对象对塔进行操作
//添加一个塔
let tower1 = NETower_AF()
mobileOperationTower .addTower!(tower: tower1)
//获取一个小兵
let newTower1 = mobileOperationTower.getTower!()
newTower1.name = "手机上的塔"
newTower1.healthPoint = 100.0
newTower1.defenseValue = 9999.0
print(newTower1.getTowerInfo())
输出信息:
//======================角色=========================
在{电脑}上面添加一个的《角色》对象
在{电脑}上面获取一个的《角色》对象
角色名称:电脑上的任意角色名称 ==> 血量: 100.0 魔法量:80.0 购买价格:18888.0
在{手机}上面添加一个的《角色》对象
在{手机}上面获取一个的《角色》对象
角色名称:电脑上的任意角色名称 ==> 血量: 100.0 魔法量:80.0 购买价格:18888.0//======================小兵=========================
在{电脑}上面添加一个的《小兵》对象
在{电脑}上面获取一个的《小兵》对象
小兵名称:电脑上的步兵、弓箭手、炮兵 ==> 血量: 100.0 魔法量:80.0
在{手机}上面添加一个的《小兵》对象
在{手机}上面获取一个的《小兵》对象
小兵名称:电脑上的步兵、弓箭手、炮兵 ==> 血量: 100.0 魔法量:80.0//======================塔=========================
在{电脑}上面添加一个的《塔》对象
在{电脑}上面获取一个的《塔》对象
塔名称:电脑上的塔 ==> 血量: 100.0 防御值:999.0
在{手机}上面添加一个的《塔》对象
在{手机}上面获取一个的《塔》对象
塔名称:手机上的塔 ==> 血量: 100.0 防御值:9999.0
总结一下:
工厂方法VS抽象工厂
可以对比下如果这个场景利工厂方法去实现会出现什么问题?答案就是你会发现很多地方需不断创建是手机平台实例还是PC平台实例。所以说:
如果商品和商品操作类只有一种的情况下,利用工厂方法很方便的实现。
如果出现多种商品和多种操作类的时候,这时候就必须再次抽象出去,以达到方便扩展(开放-封闭原则)
最后上一下抽象工厂关系图:
4.奥义第四式·策略模式(商店的优惠策略)
场景:
玩游戏你会发现,商店里部分英雄会有打九折英雄,有18888金币立减到13888金币这样的活动。活动过后又恢复正常了。
首先我们先考虑如何抽象,每一个活动当成一种策略。
class GoldStrategy: NSObject {
func acceptGold(gold: Float) -> Float{
return 0
}
}
接下来我们三种策略:正常价格、打九折、18888立减5000
class GoldStrategyNormal: GoldStrategy {
override func acceptGold(gold: Float) -> Float {
return gold
}
}
class GoldStrategyRobate: GoldStrategy {
override func acceptGold(gold: Float) -> Float {
return gold * 0.9
}
}
class GoldStrategyReturn: GoldStrategy {
override func acceptGold(gold: Float) -> Float {
return gold - 5000.0
}
}
接下来如何应用三种策略切换,用一个Context类来处理,此处你会发现这个模式跟简单工厂类很像。
enum GoldType: Int{
case GoldTypeNormal = 0
case GoldTypeRobate
case GoldTypeReturn
}
class GoldContext: NSObject {
var stragy: GoldStrategy //相比简单工厂区别,不在客户端里产生实例,在类的属性。
init(type: GoldType){
switch type {
case .GoldTypeNormal:
stragy = GoldStrategyNormal()
case .GoldTypeRobate:
stragy = GoldStrategyRobate()
case .GoldTypeReturn:
stragy = GoldStrategyReturn()
}
}
func getResult(gold: Float) -> Float {
return stragy .acceptGold(gold: gold)
}
}
客户端的调用:
//正常的英雄价格
let context: GoldContext = GoldContext(type: .GoldTypeNormal)
let resultNormal = context.getResult(gold: 18888.0)
print("策略一:\(resultNormal)")
//打过九折的价格
let context1: GoldContext = GoldContext(type: .GoldTypeRobate)
let resultRobate = context1.getResult(gold: 18888.0)
print("策略二:\(resultRobate)")
//18888立减5000到13888
let context2: GoldContext = GoldContext(type: .GoldTypeReturn)
let resultReturn = context2.getResult(gold: 18888.0)
print("策略三:\(resultReturn)")
输出信息:
策略一:18888.0
策略二:16999.2
策略三:13888.0
简单工厂VS策略模式的区别
简单工厂重在实例的生成,在客户端里会有工厂类和实例类相对耦合高
策略模式客户端只认识context实例,耦合降低
策略工厂关系图:
5.奥义第五式·代理模式(我将代替小兵惩罚你)
场景:
其实有没有发现,你在打游戏的时候,每次打怪升级过程,就是一种代理模式,你(伟大的召唤师)代替小兵打败对方的小兵,获取金币和经验。
来看一下在代码里如何实现。写代理模式先搞清楚三个东西,代理场景、代理人、被代理人。
那么在这个案例里,代理场景就是游戏攻击敌人,里面有攻击、放技能、获取金币。所以抽象类应该这样写:
@objc protocol NEAttackEnemy {
@objc optional func attack() //攻击
@objc optional func skill() //放技能
@objc optional func getGold() //获取金币
}
然后谁是真的去做这件事情的人是游戏,所以游戏实现这个协议
//代理去做的一个场景
class NEGame: NSObject,NEAttackEnemy {
var xiaoBin: NEXiaoBin
init(xiaoBin: NEXiaoBin) {
self.xiaoBin = xiaoBin
}
func attack() {
print("\(xiaoBin.name) 攻击了对面敌人")
}
func skill() {
print("\(xiaoBin.name) 放出了技能")
}
func getGold() {
print("\(xiaoBin.name) 从敌人身获取了金币")
}
}
接下来,代理人(选择的英雄)需要干这些件事
//代理人:英雄 代替小兵去攻击对面敌人
class NEHeroProxy: NSObject,NEAttackEnemy {
var game: NEGame
init(xiaoBin: NEXiaoBin) {
game = NEGame(xiaoBin: xiaoBin)
}
func attack() {
self.game .attack()
}
func skill() {
self.game .skill()
}
func getGold() {
self.game .getGold()
}
}
最后被代理人的实体:
//小兵是一个被代理人,英雄代理小兵去攻击敌人
class NEXiaoBin: NSObject {
var name: String = ""
}
好的,到这里实现完毕。看下客户端如何调用:
//被代理人
let xiaoBin: NEXiaoBin = NEXiaoBin()
xiaoBin.name = "我的名字叫:爱打战的小兵"
//代理人
let proxy: NEHeroProxy = NEHeroProxy(xiaoBin: xiaoBin)
proxy.attack()
proxy.skill()
proxy.getGold()
输出信息:
我的名字叫:爱打战的小兵 攻击了对面敌人
我的名字叫:爱打战的小兵 放出了技能
我的名字叫:爱打战的小兵 从敌人身获取了金币
代理模式关系图:
6.奥义第六式·装饰模式(给你的英雄装配超弦时装)
场景:
在选择英雄时,游戏给人物角色装配一些武器、上衣、裤子等等,如果更换这些部件该如何优雅替换呢。
首先先以这些部件进行抽象,展示这些部件,抽象类叫Component,里面有一个展示函数
class NEHeroComponent: NSObject {
var name: String = ""
func show() {
}
}
接下来,如何给阿珂打扮各个组件。
//阿珂具体类
class NEAKeHeroComponent: NEHeroComponent {
override func show() {
print("\(self.name)英雄 展示时装")
}
}
接下来一个比较关键的一步,实现组件后引用自身抽象组件做为成员变量,装饰抽象类,建立依赖关系,可以先穿衣服再穿裤子再配匕首
//装饰配件抽象类
class NEDecorator: NEHeroComponent {
var component: NEHeroComponent?
func setComponent(c: NEHeroComponent) {
self.component = c
}
override func show() {
self.component! .show()
}
}
接下来比较简单了,每个饰件继承这个装饰抽象
匕首类:
class NEDaggerDecorator: NEDecorator {
override func show() {
print("我装备上了超弦的匕首")
super.show()
}
}
紧身衣类:
class NETightsDecorator: NEDecorator {
override func show() {
print("我装备上了紧身衣")
super.show()
}
}
短裤类:
//短裤
class NEShortsDecorator: NEDecorator {
override func show() {
print("我装备上了短裤")
super.show()
}
}
最后看下,客户端如何配出一个超弦时装的阿珂
//没有穿时装的阿珂
let akeHero:NEAKeHeroComponent = NEAKeHeroComponent()
//装饰配件
let dagger = NEDaggerDecorator()
let tights = NETightsDecorator()
let shorts = NEShortsDecorator()
//装配中。。。。
dagger .setComponent(c: akeHero)
tights .setComponent(c: dagger)
shorts .setComponent(c: tights)
//展示成品
shorts.show()
输入信息:
我装备上了短裤
我装备上了紧身衣
我装备上了超弦的匕首
英雄 展示时装
当然顺序你可以根据自己需要更改,程序代码顺序。上一下装饰模式的关系图
7.奥义第七式·模板方法模式(我有一套百战百胜铭文)
场景:
在游戏中,每个人都有一套铭文,每个铭文都固定10个位置,以红色、绿色、蓝色区分。那么游戏提供了这一套模板,怎么搭配就交给召唤师(你)了。
首先来个铭文抽象,把红色、绿色、蓝色的10个坑写成函数
class NEMingWen: NSObject {
func redMingWen() {
print("总共有10个红色位置可以装配红色铭文")
print("开始搭配:\(redDistribution())")
}
func redDistribution() -> String {
return "空搭配"
}
func greenMinWen() {
print("总共有10个绿色位置可以装配绿色铭文")
print("开始搭配:\(greenDistribution())")
}
func greenDistribution() -> String {
return "空搭配"
}
func blueMinWen() {
print("总共有10个蓝色色位置可以装配蓝色铭文")
print("开始搭配:\(blueDistribution())")
}
func blueDistribution() -> String {
return "空搭配"
}
}
那么把最重要坑的填充位置留出来,给实现类来填。是不是很熟悉的一个模式。
填坑一号:
class NEDistributionMingWenA: NEMingWen {
override func redDistribution() -> String{
return "我搭配了5个五级的异变铭文和5个四级的暴戾铭文"
}
override func greenDistribution() -> String{
return "我搭配了5个五级的虚空铭文和5个三级的风怒铭文"
}
override func blueDistribution() -> String{
return "我搭配了8个五级的隐匿铭文和2个四级的刹那铭文"
}
}
填坑二号:
class NEDistributionMingWenB: NEMingWen {
override func redDistribution() -> String{
return "我搭配了2个五级的异变铭文和8个四级的暴戾铭文"
}
override func greenDistribution() -> String {
return "我搭配了1个五级的虚空铭文和9个三级的风怒铭文"
}
override func blueDistribution() -> String {
return "我搭配了4个五级的隐匿铭文和6个四级的刹那铭文"
}
}
客户端实现代码:
print("=============第一套铭文方案=============")
let aMingWen: NEDistributionMingWenA = NEDistributionMingWenA()
aMingWen.redMingWen()
aMingWen.blueMinWen()
aMingWen.greenMinWen()
print("=============第二套铭文方案=============")
let bMingWen: NEDistributionMingWenB = NEDistributionMingWenB()
bMingWen.redMingWen()
bMingWen.blueMinWen()
bMingWen.greenMinWen()
输出信息:
=============第一套铭文方案=============
总共有10个红色位置可以装配红色铭文
开始搭配:我搭配了5个五级的异变铭文和5个四级的暴戾铭文
总共有10个蓝色色位置可以装配蓝色铭文
开始搭配:我搭配了8个五级的隐匿铭文和2个四级的刹那铭文
总共有10个绿色位置可以装配绿色铭文
开始搭配:我搭配了5个五级的虚空铭文和5个三级的风怒铭文
=============第二套铭文方案=============
总共有10个红色位置可以装配红色铭文
开始搭配:我搭配了2个五级的异变铭文和8个四级的暴戾铭文
总共有10个蓝色色位置可以装配蓝色铭文
开始搭配:我搭配了4个五级的隐匿铭文和6个四级的刹那铭文
总共有10个绿色位置可以装配绿色铭文
开始搭配:我搭配了1个五级的虚空铭文和9个三级的风怒铭文
这个模式应该叫占坑模式比较好记点。哈哈,来张关系图
8.奥义第八式·外观模式(游戏开始前和游戏结束后)
场景:
开始游戏等待界面是激动的,游戏结束弹出胜利是欢喜的,那么这两个时刻游戏系统干了些什么。
答案是:开始的时候,游戏要加载角色层、地图层、操作层....等等,游戏结束的时候,就开始销毁角色层、地图层、操作层.....等等,这些是不是期待一个统一管理类
首先来个这个抽象,里面就是游戏开始和游戏结束,然后里面装的是所有层
class NEGameFacade: NSObject {
var role: NERoleLayer
var map: NEMapLayer
var operation: NEOperationLayer
override init() {
self.role = NERoleLayer()
self.map = NEMapLayer()
self.operation = NEOperationLayer()
}
func gameStart() {
self.map .load()
self.role .load()
self.operation .load()
}
func gameOver() {
self.map .destroy()
self.role .destroy()
self.operation .destroy()
}
}
人物层:
class NERoleLayer: NSObject {
func load() {
print("加载角色成功")
}
func destroy() {
print("销毁角色成功")
}
}
地图层:
class NEMapLayer: NSObject {
func load() {
print("加载地图成功")
}
func destroy() {
print("销毁地图成功")
}
}
操作层:
class NEOperationLayer: NSObject {
func load() {
print("加载操作成功")
}
func destroy() {
print("销毁操作成功")
}
}
客户端代码调用:
let game: NEGameFacade = NEGameFacade()
print("============正在进入游戏等待界面,游戏马上就要开始了============")
game .gameStart() //开始游戏了
print("============您已占领敌方水晶,游戏胜利============")
game .gameOver() //游戏结束了
这个模式很常用但是说不出他的名字,外观模式也叫门面模式,关系图如下:
9.奥义第九式·建造者模式(我要选择一个很强的角色)
场景:
在游戏里,每次选择角色,系统就会给你生成一个角色,这个角色有漂亮脸蛋的旦妃、有胸肌外露的程咬金、有脚下踩云的姜子牙。在程序中如何实现?
先来建造器抽象,造头、造身体、造左胳膊、造右胳膊、造左腿、造右腿
@objc protocol NERoleBuilder {
func buildHeader()
func buildBody()
func buildArmLeft()
func buildArmRight()
func buildLegLeft()
func buildLegRight()
}
接下来,给一个具体的角色,关羽英雄和阿珂角色
class NEGuanYu: NSObject, NERoleBuilder {
func buildHeader(){
print("创建一个红色的关羽头")
}
func buildBody(){
print("创建胸肌威猛的关羽身体")
}
func buildArmLeft(){
print("创建肌肉发达的左胳膊")
}
func buildArmRight(){
print("创建肌肉发达的右胳膊")
}
func buildLegLeft(){
print("创建孔武有力的左腿")
}
func buildLegRight(){
print("创建孔武有力的右腿")
}
}
class NEAKe:NSObject, NERoleBuilder {
func buildHeader(){
print("创建一个五官精致的阿珂头")
}
func buildBody(){
print("创建纤体美胸的阿珂身体")
}
func buildArmLeft(){
print("创建皮肤嫩水的左胳膊")
}
func buildArmRight(){
print("创建皮肤嫩水的右胳膊")
}
func buildLegLeft(){
print("创建小脚轻盈的左腿")
}
func buildLegRight(){
print("创建小脚轻盈的右腿")
}
}
在这个模式里,最重要的一个位置出现了,指挥者也就是you(伟大的召唤师),你想选择哪个角色就造哪个角色
class NESummonerDirector: NSObject {
var roleBuilder: NERoleBuilder
init(builder: NERoleBuilder) {
self.roleBuilder = builder
}
func buildRole() {
self.roleBuilder .buildHeader()
self.roleBuilder .buildBody()
self.roleBuilder .buildArmLeft()
self.roleBuilder .buildArmRight()
self.roleBuilder .buildLegLeft()
self.roleBuilder .buildLegRight()
}
}
客户端代码:
print("=======我选择了关羽角色,游戏正在建造=======")
//建造对象
let guanYu: NEGuanYu = NEGuanYu()
//指挥者
let director: NESummonerDirector = NESummonerDirector(builder: guanYu)
//创建一个关羽
director .buildRole()
print("=======我选择了阿珂角色,游戏正在建造=======")
//建造对象
let aKe: NEAKe = NEAKe()
//指挥者
let director1: NESummonerDirector = NESummonerDirector(builder: aKe)
//创建一个阿珂
director1 .buildRole()
输出信息:
=======我选择了关羽角色,游戏正在建造=======
创建一个红色的关羽头
创建胸肌威猛的关羽身体
创建肌肉发达的左胳膊
创建肌肉发达的右胳膊
创建孔武有力的左腿
创建孔武有力的右腿
=======我选择了阿珂角色,游戏正在建造=======创
建一个五官精致的阿珂头
创建纤体美胸的阿珂身体
创建皮肤嫩水的左胳膊
创建皮肤嫩水的右胳膊
创建小脚轻盈的左腿
创建小脚轻盈的右腿
就是这么简单,建造者模式关系图奉上:
10.奥义第十式·观察者模式(百里守约我怎么杀不死你)
场景:
相信玩个游戏的人,会把百里守约这个英雄禁掉,不让他出场,他有几个特点隐身、3技能位移最关键是他会放一个2技能静谧之眼,可以侦查敌人的视野。一发现敌人过来,立刻逃跑或攻击
写这个模式关键两个问题,就是了解两个东西,订阅的主题和通知的对象。主题是敌人来了,通知对象:百里守约或者辅助等等
订阅主题抽象:添加一个监听者和删除一个监听者,并通知他干一件事情
class NESubject: NSObject {
var observers: NSMutableArray = []
func attachObserver(observer: NEObserver){
self.observers .add(observer)
}
func detachObserver(observer: NEObserver){
for o in self.observers {
if (o as AnyObject).isEqual(observer) {
self.observers .remove(o)
}
}
}
func notify() {
}
}
通知对象抽象类:更新自己的行为
class NEObserver: NSObject {
func update() {
}
}
接下来就是具体实现:
敌人来了主题:
class NEEnemyCamingSubject: NESubject {
override func notify() {
print("百里守约放了一个静谧之眼,侦查有敌人过来了,通知大家")
for observer in self.observers {
(observer as! NEObserver) .update()
}
}
}
通知百里守约和辅助具体实现:
class NEBaiLiShouYue: NEObserver {
override func update() {
print("百里守约说:有一大波敌人来了,快走啊!")
}
}
class NEFuZhu: NEObserver {
override func update() {
print("辅助说:我掩护,百里守约你快走!")
}
}
客户端代码:
//发布一个主题
let subject: NEEnemyCamingSubject = NEEnemyCamingSubject()
//订阅者
let bailishouyue = NEBaiLiShouYue()
let fuzhu = NEFuZhu()
//添加订阅者
subject .attachObserver(observer: bailishouyue)
subject .attachObserver(observer: fuzhu)
//删除订阅者
//subject .detachObserver(observer: fuzhu)
//主题触发
subject.notify()
输出内容:
百里守约放了一个静谧之眼,侦查有敌人过来了,通知大家
百里守约说:有一大波敌人来了,快走啊!
辅助说:我掩护,百里守约你快走!
观察者模式,一个强大侦查模式,关系图奉上:
11.奥义第十一式·状态模式(关羽你的秘密好多啊)
场景:
一个玩的6的关羽,那简直是天下无敌,遇神杀神、见佛杀佛的厉害角色。想成为这个的关羽,首先要了解关羽几种状态。关羽 行走时会增加能量,当能量达到100时,赤兔马处于奔跑状态,随时可以放大招,这时123技能各不相同,1可以向前大刀顶,2可以踩晕敌人,3可以推倒对面一群人。所以关羽的状态这么多,程序如何实现呢?
状态模式可以单独分为两部分来看,一个是谁发出的状态(关羽),另一个是表现出来的状态(抽象)
我们先写谁发出的状态
class NEGuanYuContext: NSObject {
var state: NEState?
var energy = 0
var skill = 0 //(1:顶 2:踩 3:推)
override init(){
self.state = NENormalState()
}
func walk() {
state?.walk!(context: self)
}
}
关羽的状态(抽象),注意里引用了关羽的对象,这样子类不断可以传承下去。
@objc protocol NEState {
@objc optional func walk(context: NEGuanYuContext)
}
实现不同状态:
正常状态:
class NENormalState: NSObject,NEState {
func walk(context: NEGuanYuContext) {
if context.energy < 100 {
print("我的能量没满,我只能处于行走状态")
}else{
context.state = NERunningState()
context.walk()
}
}
}
奔跑状态:
class NERunningState: NSObject,NEState {
func walk(context: NEGuanYuContext) {
if context.energy == 100 && context.skill == 0 {
print("我现在飞起的状态,你可以顶、踩、推了")
}else if context.energy == 100 && context.skill == 1{
context.state = NEDingState()
context.walk()
context.state = NENormalState()
}else if context.energy == 100 && context.skill == 2{
context.state = NECaiState()
context.walk()
context.state = NENormalState()
}else if context.energy == 100 && context.skill == 3{
context.state = NETuiState()
context.walk()
context.state = NENormalState()
}
}
}
1技能顶的状态
class NEDingState: NSObject,NEState {
func walk(context: NEGuanYuContext) {
print("我顶,把你送入塔下")
}
}
2技能踩状态
class NECaiState: NSObject,NEState {
func walk(context: NEGuanYuContext) {
print("我踩,让你晕一秒")
}
}
3技能推倒状态
class NETuiState: NSObject,NEState {
func walk(context: NEGuanYuContext) {
print("我推,全部推到我军包围圈")
}
}
客户端代码调用:
//创建一个是谁发出的状态
let guanYu = NEGuanYuContext()
guanYu .walk()
//能量100了
guanYu.energy = 100
guanYu .walk()
//能量满时,按下1技能
guanYu .skill = 1
guanYu .walk()
//能量满时,按下2技能
guanYu.energy = 100
guanYu .skill = 2
guanYu .walk()
//能量满时,按下3技能
guanYu.energy = 100
guanYu .skill = 3
guanYu .walk()
状态模式关系图:
12.奥义第十二式·适配器模式(老外想玩游戏看不懂中文啊)
场景:
王者荣耀在中国排行榜,遥遥领先,现在大街小巷都在玩,外国朋友也开始玩。可以在中国只有中文版本,选择英雄时或者释放技能时都是中文提示,老外入门看不懂中文,这时候有个翻译就好了。
先抽象一个游戏,里面选择英雄、放技能函数
class NEChineseGame: NSObject {
var name: String = ""
func initWithName(name: String) -> NEChineseGame{
self.name = name
return self
}
//选择英雄
func chooseHero() {
}
//释放技能
func releaseSkills() {
}
}
中国玩家玩中国游戏,么问题拉!
class NEChinesesPlayer: NEChineseGame {
override func chooseHero() {
print("中文提示界面,一看就知道如何选择英雄,中国玩家:\(name)")
}
override func releaseSkills() {
print("中文提示技能信息,一看就知道它是怎么释放的效果,中国玩家:\(name)")
}
}
外国人玩中国游戏,看不懂,我需要一个翻译
class NEAdapter: NSObject {
var name: String = ""
func initWithName(foreignName: String) -> NEAdapter{
self.name = foreignName
return self
}
func chooseHeroWithForeignLanguages(){
print("这里用外语方式解释如何选择英雄,外国玩家:\(name)")
}
func releaseSkillsWithForeignLanguages(){
print("这里用外语方式解释如何释放技能,外国玩家:\(name)")
}
}
翻译来了,外国玩家马上就懂了
class NEForeignPlayer: NEChineseGame {
var adapter: NEAdapter = NEAdapter()
override func initWithName(name: String) -> NEChineseGame {
self.adapter .initWithName(foreignName: name)
return self
}
override func chooseHero() {
self.adapter .chooseHeroWithForeignLanguages()
}
override func releaseSkills() {
self.adapter .releaseSkillsWithForeignLanguages()
}
}
客户端代码:
print("============中文界面============")
//中国玩家
let chinesePlayer = NEChinesesPlayer()
chinesePlayer .initWithName(name: "中路杀神")
chinesePlayer.chooseHero()
chinesePlayer.releaseSkills()
print("============翻译成外语============")
//外国玩家
let foreignPlayer = NEForeignPlayer()
foreignPlayer .initWithName(name: "lucy")
foreignPlayer .chooseHero()
foreignPlayer .releaseSkills()
输出信息:
============中文界面============
中文提示界面,一看就知道如何选择英雄,中国玩家:中路杀神
中文提示技能信息,一看就知道它是怎么释放的效果,中国玩家:中路杀神
============翻译成外语============
这里用外语方式解释如何选择英雄,外国玩家:lucy
这里用外语方式解释如何释放技能,外国玩家:lucy
适配模式,先继承抽象后翻译动作。关系图如下:
13.奥义第十三式·备忘录模式(血王宫的回忆!太难了,我玩不过去啊)
场景:
玩过冒险游戏的人都会知道,里面有一关血王宫的回忆,很难过去(我是后面找到攻略才过去的),就是保护两个角色不能死亡,死亡就代表失败。每次都要重头在来,而且里面可以随便搭配。
备忘录模式是一个平级横向模式,没有上下纵向继承。所以没有抽象。
Memento = 游戏关卡,我们先来关卡实例
class NEGameCard: NSObject {
var name = ""
var process: NSInteger = 0 //关卡进度
var chooseHeroNumber: NSInteger = 0 //选择英雄人数
var finish: NSInteger = 0 //是否挑战过
}
Caretaker = 再来负责存储关卡的实例,里面引用关卡实例
class NECardManager: NSObject {
var card: NEGameCard
init(card: NEGameCard){
self.card = card
}
}
Originator = 记录关卡保存、打boss、重新开始
class NEGameRoll: NSObject {
var name = ""
var process: NSInteger = 100
var chooseHeroNumber: NSInteger = 100
var finish: NSInteger = 100
func getState(){
print("关卡名称:\(self.name),关卡的进度:\(process)%,英雄人物数量:\(chooseHeroNumber),是否挑战成功过:\(finish)")
}
//负责创建一个备忘录
func saveProcess() -> NEGameCard{
let card = NEGameCard()
card.process = self.process
card.chooseHeroNumber = self.chooseHeroNumber
card.finish = self.finish
return card
}
//打boss了
func fightBoss(){
self.process = 0
self.chooseHeroNumber = 0
self.finish = 0
}
//重新开始挑战关卡
func restart(card: NEGameCard) {
self.chooseHeroNumber = card.chooseHeroNumber
self.process = card.process
self.finish = card.finish
}
}
好了,看下客户端代码调用:
let roll = NEGameRoll()
roll.name = "血王宫的回忆"
roll.getState()
//负责创建一个关卡
let card = roll.saveProcess()
//负责保存这个关卡状态
let manager = NECardManager(card: card)
//打boss
roll.fightBoss()
roll.getState()
//打不过,回到最初挑战
roll.restart(card: manager.card)
roll.getState()
输出信息:
关卡名称:血王宫的回忆,关卡的进度:100%,英雄人物数量:100,是否挑战成功过:100
关卡名称:血王宫的回忆,关卡的进度:0%,英雄人物数量:0,是否挑战成功过:0
关卡名称:血王宫的回忆,关卡的进度:100%,英雄人物数量:100,是否挑战成功过:100
奉上关系图:
14.奥义第十四式·组合模式(一场游戏需要多少场景)
场景:
一场游戏有很多场景组成,比如角色场景、技能场景、操作场景等等,那么角色场景里面有各个角色组成,比如阿珂、关羽、小乔等等,而技能场景就只是一个场景没有下一级称为Leaf。像这样不断扩展,最终形成了一个大的游戏世界。
首先我抽象一个组件对象,里面有添加、移除、展示、场景的工作原理等方法操作
@objc protocol NEComponent {
var name: String{get set}
@objc optional func display()
@objc optional func add(component: NEComponent)
@objc optional func remove(component: NEComponent)
@objc optional func work()
}
接下来游戏场景 包含技能场景、角色场景,但他们都实现上面的抽象类
游戏场景:
class NEGameSceneComposite: NSObject,NEComponent {
var compoents: NSMutableArray = []
var name = ""
init(name: String){
self.name = name
}
func add(component: NEComponent) {
compoents .add(component)
}
func remove(component: NEComponent) {
self.compoents .remove(component)
}
func display() {
print("\(name)的子场景")
for c in self.compoents {
(cas! NEComponent).display!()
}
}
func work() {
print("\(name)的子场景工作")
for c in self.compoents {
(cas! NEComponent).work!()
}
}
}
角色场景:
class NERoleSceneComposite: NSObject,NEComponent {
var name = ""
var compoents: NSMutableArray = []
init(name: String){
self.name = name
}
func add(component: NEComponent) {
compoents .add(component)
}
func remove(component: NEComponent) {
self.compoents .remove(component)
}
func display() {
print("\(name)的子场景")
for c in self.compoents {
(cas! NEComponent).display!()
}
}
func work() {
print("\(name)的子场景工作")
for c in self.compoents {
(cas! NEComponent).work!()
}
}
}
技能场景:
//技能场景
class NESkillSceneComponent: NSObject,NEComponent {
var name:String
init(name: String){
self.name = name
}
func display() {
print("这是一个放技能的场景")
}
func work() {
print("我的工作就是制作出所有技能")
}
}
然后来角色场景里面有个阿珂角色,这个也是Leaf
class NEAkeRoleComposite: NSObject,NEComponent {
var name = ""
init(name: String){
self.name = name
}
func display() {
print("阿珂角色信息")
}
func work() {
print("我的工作就是画出阿珂模型出来")
}
}
客户端代码调用:
//顶结点
let gameRoot = NEGameSceneComposite(name: "王者荣耀游戏场景")
let skillScene = NESkillSceneComponent(name: "技能场景")
let roleScene = NERoleSceneComposite(name: "角色场景")
gameRoot .add(component: skillScene)
gameRoot .add(component: roleScene)
let ake = NEAkeRoleComposite(name: "阿珂模型")
roleScene .add(component: ake)
gameRoot.display()
gameRoot.work()
输出信息:
王者荣耀游戏场景的子场景
这是一个放技能的场景
角色场景的子场景
阿珂角色信息
王者荣耀游戏场景的子场景工作
我的工作就是制作出所有技能
角色场景的子场景工作
我的工作就是画出阿珂模型出来
基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去,所以任何用到基本对象的地方都可以使用组合对象了。
15.奥义第十五式·迭代器模式(英雄商店金币的排序)
场景:
在商店里英雄越出越多,有时买个18888英雄得拉到最下面才行,这时能不能来个金币从高到低的排序呢?
迭代器,两部分组成集合抽象和遍利抽象。集合抽象包含一个遍利对象成员。
集合抽象:创建一个迭代器。
迭代器抽象:下一个、第一个、当前项、是否结束
@objc protocol NEAggregate {
@objc optional func createIteator() -> NEIterator
}
@objc protocol NEIterator {
@objc optional func first() -> Any
@objc optional func next() -> Any
@objc optional func currentItem() -> Any
@objc optional func isDone() -> Bool
}
具体集合实现:(包含一个集合类)
class NEStoreAggregate: NSObject,NEAggregate {
var roles: NSMutableArray = []
func createIteator() -> NEIterator {
return NERoleIterator(aggregate: self)
}
func objectIndex(index: NSInteger) -> Any{
return self.roles[index]
}
func count() -> NSInteger{
return self.roles.count
}
func addObject(object: Any) {
self.roles .add(object)
}
func sortAsc(){
print("======按升序排序=====")
}
}
具体迭代器实现:
class NERoleIterator: NSObject,NEIterator {
var storeAggregate: NEStoreAggregate
var index: NSInteger = 0
init(aggregate: NEStoreAggregate){
self.storeAggregate = aggregate
}
func first() -> Any {
return self.storeAggregate .objectIndex(index: 0)
}
func next() -> Any {
index = index + 1
if index < self.storeAggregate.count() {
return self.storeAggregate .objectIndex(index: index)
}
return ""
}
func currentItem() -> Any {
if index < self.storeAggregate.count() {
return self.storeAggregate .objectIndex(index: index)
}
return ""
}
func isDone() -> Bool {
return index >= self.storeAggregate.count() ? true : false
}
}
客户端调用:
创建一个商店集合类,添加后羿、狄仁杰、阿珂、关羽。然后排下序,迭代英雄展示。
//商店集合类
let storeAggregate = NEStoreAggregate()
storeAggregate .addObject(object: "6888-后羿")
storeAggregate .addObject(object: "8888-狄仁杰")
storeAggregate .addObject(object: "13888-阿珂")
storeAggregate .addObject(object: "18888-关羽")
//可以自行排序
storeAggregate .sortAsc()
//商店迭代器
let roleIterator = NERoleIterator(aggregate: storeAggregate)
let firstItem = roleIterator.first() as! String
print("第一项:\(firstItem)")
while !roleIterator.isDone() {
print("当前项:\(roleIterator.currentItem() as! String)")
let nextItem = roleIterator.next() as! String
print("下一项:\(nextItem)")
}
输出信息:
======按升序排序=====
第一项:6888-后羿
当前项:6888-后羿
下一项:8888-狄仁杰
当前项:8888-狄仁杰
下一项:13888-阿珂
当前项:13888-阿珂
下一项:18888-关羽
当前项:18888-关羽
迭代器关系图:
16.奥义第十六式·单例模式(设置操作界面,我只要设置一次)
场景:
刚下载游戏的时候,需要配置一下自己的常用操作设置,比如出装要在右边,滑出取消技能等等设置,这样一次设置,以后都不用配置了,整个游戏生命周期都可以应用。
这个模式老生常谈了,写几种实现方式
let _SingletonSharedInstance = NEGameSetting()
class NEGameSetting: NSObject {
//第一种写法
// class var sharedInstance : NEGameSetting {
// struct Static{
// static var onceToken: dispatch_once_t = 0
// static var instance: NEGameSetting? = nil
// }
// dispatch_once(&Static.onceToken){
// Static.instance = NEGameSetting()
// }
// return Static.instance!
// }
//第二种写法
class var sharedInstance1 : NEGameSetting{
struct Static{
static var instance: NEGameSetting = NEGameSetting()
}
return Static.instance
}
//第三种写法
class var sharedInstance2 : NEGameSetting{
return _SingletonSharedInstance
}
//第四种写法
//class let sharedInstance3 = NEGameSetting()
}
客户端调用:
//单例:游戏设置界面一次设置,工程通用
print("我叫单例模式,一生只能生一次")
let gameSetting = NEGameSetting .sharedInstance1
单例关系图:
17.奥义第十七式·桥接模式(角色技能&位置&类型)
场景:
每次买英雄的时候,都会考虑几个因素的问题,这个英雄属于哪一路英雄(上路、中路、下路等)、英雄的技能是四个栏还是三个栏、英雄类型是战士、法师、辅助、刺客、坦克。所以考虑这么多因素。如何实现?
首先把角色抽象出来,角色有三个属性,走哪一路、技能栏数、什么类型。
class NERoleAbstraction: NSObject {
var walkRoute: NEWalkRoute? = nil
var skillNumber: NESkillNumber? = nil
var roleType: NERoleType? = nil
override init() {
self.walkRoute = NEWalkRoute()
self.skillNumber = NESkillNumber()
self.roleType = NERoleType()
}
//行走路线
func walkingRoute() {
}
//技能数量
func skillCount() {
}
//角色类型
func type() {
}
女娲具体角色实现:
class NENvWaoRole: NERoleAbstraction {
override func walkingRoute() {
self.walkRoute? .zhongLu()
}
override func skillCount() {
self.skillNumber? .fourSkills()
}
override func type() {
self.roleType? .fashi()
}
}
角色属性抽象,包含具体路分支、技能栏分支、类型分支。统一一个抽象接口
@objc protocol NEAttribute {
@objc optional func zhongLu()
@objc optional func shangLu()
@objc optional func xiaLu()
@objc optional func threeSkills()
@objc optional func fourSkills()
@objc optional func shangshi()
@objc optional func fashi()
@objc optional func chike()
@objc optional func tangke()
@objc optional func zheshou()
@objc optional func fuzhu()
}
拆分成属性具体类
位置:
class NEWalkRoute: NSObject,NEAttribute {
func shangLu() {
print("我走上路")
}
func zhongLu() {
print("我走中路")
}
func xiaLu() {
print("我走下路")
}
}
技能栏:
class NESkillNumber: NSObject,NEAttribute {
func threeSkills() {
print("我会放三个技能")
}
func fourSkills() {
print("我会放四个技能")
}
}
类型:
class NERoleType: NSObject,NEAttribute {
func shangshi() {
print("我是一名战士")
}
func fashi() {
print("我是一名法师")
}
func chike() {
print("我是一名刺客")
}
}
客户端代码:
//造出角色
let nvwaoRole = NENvWaoRole()
//走哪一路线
nvwaoRole.walkingRoute()
//有多少栏技能
nvwaoRole.skillCount()
//属于哪种类型角色
nvwaoRole.type()
输出信息:
我走中路
我会放四个技能
我是一名法师
桥接关系图:
18.奥义第十八式·命令模式(命令角色,释放1技能、释放2技能、取消2技能、释放大招)
场景:
召唤师控制角色放技能,撤退等,这就是一个典型的命令模式,命令角色实现各种操作。
命令抽象,包含被命令人和执行操作
class NECommand: NSObject {
var wangZhaoJunRole: NEWangZhaoJunRole
init(role: NEWangZhaoJunRole){
self.wangZhaoJunRole = role
}
func execute() {
}
}
被命人角色(王昭君):
class NEWangZhaoJunRole: NSObject {
func retreatOpration(){
print("执行撤退动画")
}
func releaseSkill() {
print("执行放技能动画")
}
}
具体命令操作:
放技能命令:
class NEReleaseSkillCommand: NECommand {
override func execute() {
self.wangZhaoJunRole.releaseSkill()
}
}
撤退命令:
class NERetreatCommand: NECommand {
override func execute() {
self.wangZhaoJunRole.retreatOpration()
}
}
命令人(召唤师)发出很多命令:
class NESummoner: NSObject {
var commands: NSMutableArray = []
func addOpration(command: NECommand) {
if NSStringFromClass(command.classForCoder) == "Design_Pattern_For_Swift.NEReleaseSkillCommand" {
print("刚放过技能,正在cd中")
}else{
//print("添加个命令")
self.commands .add(command)
}
}
func removeOpration(command: NECommand){
self.commands .remove(command)
}
func notify() {
for command in self.commands {
(command as! NECommand) .execute()
}
}
}
客户端调用:
召唤师叫王昭君释放技能并做撤退动作!
//王昭君角色
let wangzhaojunRole = NEWangZhaoJunRole()
//来个释放技能命令
let command1 = NEReleaseSkillCommand(role:wangzhaojunRole)
//来个撤退命令
let command2 = NERetreatCommand(role: wangzhaojunRole)
//召唤师
let you = NESummoner()
you .addOpration(command: command1)
you .addOpration(command: command2)
//开始执行
you .notify()
命令模式关系图:
19.奥义第十九式·职责裢模式(我用金币买英雄)
场景:
玩个排位的朋友都知道,里面区分角色熟练等级就是段位,青铜、白银、黄金等等。那么统计这个单位是星星。三个星进一个段。
段位等级抽象:包含一个自身实例以及一个评段根据星星个数操作
class NEGradeHandler: NSObject {
var name: String
var handler: NEGradeHandler? = nil
init(name: String){
self.name = name
}
func evaluateGrade(star: NEStar) {
}
}
青铜段位:
class NEQingTongHandler: NEGradeHandler {
override func evaluateGrade(star: NEStar) {
if star.number < 10 {
print("我就是一个\(name)")
}else{
handler? .evaluateGrade(star: star)
}
}
}
白银段位:
class NEBaiYingHandler: NEGradeHandler {
override func evaluateGrade(star: NEStar) {
if star.number < 25 {
print("我就是一个\(name)")
}else{
handler? .evaluateGrade(star: star)
}
}
}
黄金段位:
class NEHuangJinHandler: NEGradeHandler {
override func evaluateGrade(star: NEStar) {
if star.number < 40 {
print("我就是一个\(name)")
}
}
}
打胜一局是一星,星星类:
class NEStar: NSObject {
var number:NSInteger = 0
}
客户端代码:
//青铜段位
let qingTongHandler = NEQingTongHandler(name:"青铜段位")
//白银段位
let baiYingHandler = NEBaiYingHandler(name:"白银段位")
//黄金段位
let huangJinHandler = NEHuangJinHandler(name:"黄金段位")
//设置上一级关系
qingTongHandler.handler = baiYingHandler
baiYingHandler.handler = huangJinHandler
//自己获取的星星数量
let star = NEStar()
star .number = 35
//开始评定自己段位
qingTongHandler.evaluateGrade(star: star)
输出信息:
我就是一个黄金段位
职责裢模式关系图:
20.奥义第二十式·中介者模式(我需要一个辅助)
场景:
每次打输一把排位,我就问自己队友这么坑,这游戏怎么玩。我从心里面发出呐喊来个强有力的队友吧。就会聊天栏输入这么一句话:“我是打ADC,来个加力辅助,加我好友一起CP”,然后辅助看到回了一句话:“我就是那个给力辅助,来吧!”
那么这里面就是一个中介者模式,聊天栏充当ADC和辅助中间者。
中间者抽象:引用两个对象的实例,如果有多个全部引用
class NEMediator: NSObject {
var adc: NEADCSummonerColleague? = nil
var fuzhu: NEFuZhuSummonerColleague? = nil
func send(msg: String,colleague: NESummonerColleague){
}
}
聊天栏(具体的中间者实现)
class NEChatMediator: NEMediator {
override func send(msg: String,colleague: NESummonerColleague){
if adc == colleague {
adc? .notify(msg: msg)
}else if fuzhu == colleague{
fuzhu? .notify(msg: msg)
}
}
}
召唤师抽象:
class NESummonerColleague: NSObject {
var mediator: NEMediator
init(mediator: NEMediator){
self.mediator = mediator
}
}
打ADC的召唤师:
class NEADCSummonerColleague: NESummonerColleague {
func send(msg: String){
mediator.send(msg: msg, colleague: self)
}
func notify(msg: String){
print("adc发出信息:\(msg)")
}
}
打辅助的召唤师:
class NEFuZhuSummonerColleague: NESummonerColleague {
func send(msg: String){
mediator.send(msg: msg, colleague: self)
}
func notify(msg: String){
print("辅助发出信息:\(msg)")
}
}
客户端调用:
//聊天窗口
let chatMediator: NEChatMediator = NEChatMediator()
//打adc的召唤师
let adc: NEADCSummonerColleague = NEADCSummonerColleague(mediator: chatMediator)
//打辅助的召唤师
let fuzhu: NEFuZhuSummonerColleague = NEFuZhuSummonerColleague(mediator: chatMediator)
chatMediator.adc = adc
chatMediator.fuzhu = fuzhu
adc.send(msg: "想找一个强力的辅助打下路")
fuzhu.send(msg: "我就是那个专业、强力辅助,来组队!")
输出信息:
adc发出信息:想找一个强力的辅助打下路
辅助发出信息:我就是那个专业、强力辅助,来组队!
中介者模式关系图:
21.奥义第二十一式·享元模式(这个游戏的模式好多)
场景:
现在游戏很火,玩家据说有10亿在玩,游戏里面有匹配模式、排位模式、还有娱乐模式,那10亿玩家玩一个游戏,就要开放一个空间给玩家?这样服务器是不是受不了。
这个模式相当给力,可以让你内存复用,不必申请额外的。
创建一个享元工厂,里面有一个集合,如果没有就添加一个模式,如果有直接返回该模式
class NEFlyweightFactory: NSObject {
let flyweights: NSMutableDictionary = NSMutableDictionary()
func getMode(key: String) -> NEMode {
if (flyweights .object(forKey: key) == nil) {
flyweights .setObject(NEGameMode(name: key), forKey: key as NSCopying)
}
return flyweights .object(forKey: key) as! NEMode
}
func count() -> NSInteger{
return self.flyweights.count
}
}
模式抽象:
class NEMode: NSObject {
func use(player: String) {
}
}
娱乐、匹配、排位模式
class NEGameMode: NEMode {
var name: String
init(name: String){
self.name = name
}
}
客户端调用:
//共享工厂
let flyweightFactory: NEFlyweightFactory = NEFlyweightFactory()
//玩家一号,玩一盘匹配模式
let mode = flyweightFactory.getMode(key: "匹配模式")
mode.use(player: "玩家一号")
//玩家二号,玩一盘娱乐模式
let mode2 = flyweightFactory.getMode(key: "娱乐模式")
mode2.use(player: "玩家二号")
//玩家三号,玩一盘排位模式
let mode3 = flyweightFactory.getMode(key: "排位模式")
mode3.use(player: "玩家三号")
//玩家四号,玩一盘娱乐模式
let mode4 = flyweightFactory.getMode(key: "娱乐模式")
mode4.use(player: "玩家四号")
//.......N个玩家后
print("总共游戏有几个模式实例:\(flyweightFactory.count())")
输出信息:
总共游戏有几个模式实例:3
享元模式关系图:
22.奥义第二十二式·解释器模式(在游戏中声音解释角色死亡、胜利、动作表情的解释器)
场景:
游戏中每个角色都有一段语言声音,比如死亡,女英雄会发出妩媚、呻吟的声音,男英雄会发出撕喊、爆裂的声音。那么同一种状态发出不同英雄发出不同声音。如何解释呢?
解释的内容,比如0、1、2不同英雄有着不同的解释。比如1,关羽是跳跃声音、阿珂则是胜利声音
class NERoleSoundContext: NSObject {
var soundText: String
init(text: String){
self.soundText = text
}
解释器抽象:包含解释内容,解释动作留给子类实现,有点像模板方法模式
class NEExpression: NSObject {
func interpret(context: NERoleSoundContext){
if context.soundText == "" {
return
}else{
let text = NSString(string: context.soundText)
let sounds = text.components(separatedBy: ",")
if sounds.count > 0 {
excute(sounds: sounds as NSArray)
}
return
}
}
func excute(sounds: NSArray) {}
}
}
阿珂解释器:
class NEAkeExpression: NEExpression {
override func excute(sounds: NSArray) {
for key in sounds {
switch key as! String {
case "0":
print("播放阿珂被杀死的动画")
case "1":
print("播放阿珂胜利的动画")
case "2":
print("播放阿珂难过的动画")
//.....
default:
print("不知道该播放什么动画")
break
}
}
}
}
关羽解释器:
class NEGuanYuExpression: NEExpression {
override func excute(sounds: NSArray) {
for key in sounds {
switch key as! String {
case "0":
print("播放关羽被杀死的动画")
case "1":
print("播放关羽跳跃的动画")
case "2":
print("播放关羽放大招的动画")
//.....
default:
print("不知道该播放什么动画")
break
}
}
}
}
客户端代码:
//播放内容
let text = "0,1,2,2,1,1,0,4,1,2"
let context = NERoleSoundContext(text: text)
print("===========阿珂开始解释这段内容===========")
//阿珂解释器
let akeExpression = NEAkeExpression()
akeExpression.interpret(context: context)
print("===========关羽开始解释这段内容===========")
//关羽解释器
let guanyuExpression = NEGuanYuExpression()
guanyuExpression.interpret(context: context)
输出信息:
===========阿珂开始解释这段内容===========
播放阿珂被杀死的动画
播放阿珂胜利的动画
播放阿珂难过的动画
播放阿珂难过的动画
播放阿珂胜利的动画
播放阿珂胜利的动画
播放阿珂被杀死的动画
不知道该播放什么动画
播放阿珂胜利的动画
播放阿珂难过的动画
===========关羽开始解释这段内容===========
播放关羽被杀死的动画
播放关羽跳跃的动画
播放关羽放大招的动画
播放关羽放大招的动画
播放关羽跳跃的动画
播放关羽跳跃的动画
播放关羽被杀死的动画
不知道该播放什么动画
播放关羽跳跃的动画
播放关羽放大招的动画
解释器关系图:
23.奥义第二十三式·访问者模式(男玩家和女玩家玩游戏时的状态反应)
场景:
如果在一个聚会里,大家玩同一个游戏时,男玩家和女玩家玩游戏的情绪状态很不一样,
比如男玩胜利的时候,就是一副"老子天下第一,所向无敌",而女玩家胜利的时候,就是一副"耶!胜利咯,我感觉我还是蛮厉害的!".
男玩家失败的时候,“我靠,我这么强力还会输,不行!再来一盘”就是这样的,而女玩家失败的时候,"我晕,想要胜利一场,好难啊!"就是这样的。
男玩家被杀死的时候,"我要杀回去,不然就不是男人",女玩家被杀死的时候,"真讨厌,我这么可爱也被杀!".
先抽象玩家,包含男玩家和女玩家,有着不同的情绪变化.
class NEPlayer: NSObject {
func vistorAction(action: NEAction){}
}
具体实现:
男玩家:
class NEBoyPlayer: NEPlayer {
override func vistorAction(action: NEAction) {
action.getManAction(player: self)
}
}
女玩家:
class NEGirlPlayer: NEPlayer {
override func vistorAction(action: NEAction) {
action.getWomanAction(player: self)
}
}
情绪抽象:包含两个,一个男玩家情绪,一个女玩家情绪
class NEAction: NSObject {
func getManAction(player: NEPlayer){}
func getWomanAction(player: NEPlayer){}
}
各种情绪实现:
失败时候:
class NEFailAction: NEAction {
override func getManAction(player: NEPlayer) {
print("我靠,我这么强力还会输,不行!再来一盘")
}
override func getWomanAction(player: NEPlayer) {
print("我晕,想要胜利一场,好难啊!")
}
}
胜利的时候:
class NESuccessAction: NEAction {
override func getManAction(player: NEPlayer) {
print("老子天下第一,所向无敌")
}
override func getWomanAction(player: NEPlayer) {
print("耶!胜利咯,我感觉我还是蛮厉害的!")
}
}
被杀死的时候:
class NEKillAction: NEAction {
override func getManAction(player: NEPlayer) {
print("我要杀回去,不然就不是男人")
}
override func getWomanAction(player: NEPlayer) {
print("真讨厌,我这么可爱也被杀!")
}
}
最后来个管理者,管理很多男玩家和女玩家。
class NEObjectStucture: NSObject {
var players = NSMutableArray()
func addPlayer(player: NEPlayer){
self.players .add(player)
}
func removePlayer(player: NEPlayer){
self.players .remove(player)
}
func action(action: NEAction){
for player in self.players {
(player as! NEPlayer).vistorAction(action: action)
}
}
}
客户端代码:
//管家
let objectStucture = NEObjectStucture()
//男玩家
let boy = NEBoyPlayer()
//女玩家
let girl = NEGirlPlayer()
//加入管家内
objectStucture .addPlayer(player: boy)
objectStucture .addPlayer(player: girl)
print("============胜利时男玩家和女玩家的状态============")
//胜利时状态
let success = NESuccessAction()
objectStucture.action(action: success)
print("============失败时男玩家和女玩家的状态============")
//失败时状态
let fail = NEFailAction()
objectStucture.action(action: fail)
print("============被杀时男玩家和女玩家的状态============")
//被杀时状态
let kill = NEKillAction()
objectStucture.action(action: kill)
输出信息:
============胜利时男玩家和女玩家的状态============
老子天下第一,所向无敌
耶!胜利咯,我感觉我还是蛮厉害的!
============失败时男玩家和女玩家的状态============
我靠,我这么强力还会输,不行!再来一盘
我晕,想要胜利一场,好难啊!
============被杀时男玩家和女玩家的状态============
我要杀回去,不然就不是男人
真讨厌,我这么可爱也被杀!
访问者关系图:
24.奥义第二十四式·原型模式(玩个克隆大战?)
场景:
游戏里面有个模式,叫做克隆大战,双方五个人都是一个英雄,也是同个英雄复制五份。所以这个英雄技能、动作、效果都是一样的
角色类型抽象:
@objc protocol NERolePrototype {
@objc optional func clone() -> NERolePrototype
}
孙悟空角色
class NESuiWuKuRole: NSObject,NERolePrototype {
var name: String?
init(name: String){
self.name = name
}
func clone() -> NERolePrototype {
return NESuiWuKuRole(name: "孙悟空")
}
}
钟馗角色:
class NEZhongKuiRole: NSObject,NERolePrototype {
var name: String?
init(name: String){
self.name = name
}
func clone() -> NERolePrototype {
return NEZhongKuiRole(name: "钟馗")
}
}
客户端代码:
//友方
print("==============友方================")
let suiwuku = NESuiWuKuRole(name: "孙悟空")
let suiwuku2 = suiwuku.clone()
let suiwuku3 = suiwuku.clone()
let suiwuku4 = suiwuku.clone()
let suiwuku5 = suiwuku.clone()
print("1号:\(suiwuku),2号:\(suiwuku2),3号:\(suiwuku3),4号:\(suiwuku4),5号:\(suiwuku5)")
print("==============敌方================")
//敌方
let zhongkui = NEZhongKuiRole(name: "钟馗")
let zhongkui2 = zhongkui.clone()
let zhongkui3 = zhongkui.clone()
let zhongkui4 = zhongkui.clone()
let zhongkui5 = zhongkui.clone()
print("1号:\(zhongkui),2号:\(zhongkui2),3号:\(zhongkui3),4号:\(zhongkui4),5号:\(zhongkui5)")
输出信息:
原型模式关系图:
补上设计中几个原则:
/* 设计模式基本原则:↓↓↓↓↓↓↓↓↓↓↓↓↓--如下--↓↓↓↓↓↓↓↓↓↓↓↓↓ */
/* • 开放-封闭原则(OCP),是说软件实体(类、模块、函数等等)应该可以拓展,但是不可修改。 对于扩展是开放的,对于更改是封闭的。 • 开放-封闭原则是说软件实体(类、模块、函数等)应该是可扩展但不可修改,或者说对于扩展是开放的,对于修改是封闭的。这样可以使得设计在面对需求的改变保持相对稳定,不断迭代。 • 无论模块多么封闭,都会存在一些无法对之封闭的变化。既然不可能完全封闭,设计人员必须对于他设计的模块应该对那种变化进行封闭做出选择。他必须先猜测出最有可能发生的变化的种类,然后构造抽象来隔离这些变化。 • 在最初写代码时,假设变化不会发生。当变化发生时,我们就创建抽象来隔离以后发生的同类变化。 • 面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码。 • 我们希望的是在开发工作展开不久就知道可能发生的变化,查明可能发生的变化等待的时间越长,要创建正确的抽象就越困难。 • 开放-封闭原则是面向对象设计的核心所在,遵循这个原则可以带来面向对象技术所声称的巨大好处,就是可维护、可扩展、可复用、灵活性好。开发人员应该仅对程序中呈现出频繁变化的部分进行抽象,然而,对于应用程序中的每个部分都刻意地进行抽象同样不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。 */
/* • 单一职责原则(SRP)就是一个类而言,应该仅有一个引起它变化的原因。 • 一个类承担的职责过多,就等于把这些职责耦合在一起。一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力,这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏(个人理解,不管是类、还是方法,都需要做到单一职责原则)。 软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责,就应该考虑职责的分离。 */
/* • 依赖倒转原则(DIP),A. 高层模块不应该依赖低层模块,两个都应该依赖抽象。B. 抽象不应该依赖细节,细节应该依赖抽象。针对接口编程,而不是针对实现编程。 • 依赖倒转可以说是面向对象的标志,用哪种语言来编写程序不重要,如果编写时考虑的都是如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或接口,那就是面向对象的设计,反之那就是过程化的设计了。 */
/* • 里氏代换原则就是一个软件实体如果使用一个父类的话。那么一定适用其子类,而且它察觉不出父类对象和子类对象的区别。也就是说,在软件中把父类替换成它的子类,程序的行为没有变化。简单地说,子类型必须能够替换到他们的父类型(继承)。 • 只有当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正被复用,而子类也能在父类的基础上增加新的功能。——例如:在“面向对象”设计时,企鹅类是不能继承自鸟类的,子类拥有父类非private的行为和属性,鸟会飞,而企鹅不会,所以在编程世界,企鹅不能继承鸟类。正是因为子类型的可替换性才使得使用父类类型的模块在无需修改的情况下就可以扩展。 */
/* • 迪米特法则(LoD),又称最少知识原则,如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三方转发这个调用。 首先是在类的结构设计上,每一个类都应当尽量降低成员的访问权限,也就是不需要让别人知道的就不要公开。 迪米特法则的根本思想是强调了类之间的松耦合,类之间的耦合越弱,越有利于复用,一个处在松耦合的类被修改不会对有关系的类造成波及。 */
/* • 合成/聚合复用原则(CARP),尽量使用合成/聚合,尽量不要使用类继承。(聚合(Aggregation)表示一种‘弱’的拥有关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分。——例如:每只大雁都属于一个雁群,但一个雁群可以包含多只大雁。合成(Composition,也有叫组合的)是一种强的‘拥有’关系,体现了严格的部分和整体关系,部分和整体的声明周期一样。——例如:鸟和翅膀。) */
最后致谢大家,写的很辛苦,只为大家看得爽,大家有任何意见,留下你建议。