最近在读 《Header First -- 设计模式》这本书。由于其使用的是Java来演示的案列,所以我打算边读边以OC来演示其中的案列。这样既可以加深印象,也可以跟他人用来OC来交流设计模式。这本书写得很好,图文并茂,简单易学,想学习设计模式的人应该买一本!!
入门篇讲得是 策略模式
首先模拟以下应用场景!
你的项目是一款战略游戏。游戏主角拥有众多的鸭子作为其随从。这些鸭子有这样几个行为:
- (void)display; // 外貌 外表为鸭子的形象
- (void)swim; // 会游泳 鸭子都会游泳
- (void)quack; // 会呱呱叫
- (void)adorable; // 可爱 都是可爱的形象
一开始时,鸭子的形象都是统一的。你只有一个Duck的基类,里面声明并实现了这几个方法,众多的鸭子类NormalDuck都继承自该类。突然有一天,产品经理走过来对你说,要给鸭子们增加一种橡皮鸭子的形象。于是你又继承Duck类新建了一个RubberDuck类,并在该类中t重写了- (void)display 的方法,实现成为橡皮鸭子的形象,问题完美解决,并没有感觉什么不适。可又过了几天,产品又要为鸭子添加飞行的行为。你觉得很简单,给Duck基类增加一个飞行的方法就好,这样所有的鸭子就都可以飞行了。像这样
- (void)fly;
可程序运行成功的时候,你就发现了问题:连橡皮鸭子也跟着飞起来了。于是你去跟产品确认,产品也承认这是一个疏忽——橡皮鸭不能飞。这时,你又在RubberDuck类中重写了- (void)fly 方法,使得橡皮鸭子失去了飞行的能力。
现在让我们来看一下以上同一个需求变更对我们的代码所带来的变化——通过继承来拥有一些行为,并重写了相关方法来实现自己的特殊行为。这里有如下的问题:
1、对基类方法的修改会,会使所有继承并使用基类方法的子类都受到影响,那些你不希望受到影响的子类只能去挨个的复写相应方法(可能只是需要基类方法的实现代码的一部分,这样又造成了代码的冗余及不能复用)
2、当基类中有许多的属性与方法时,我们在创建一个新类时,就要去挨个的考虑基类中哪些属性与方法是需要重写。试想一下,你一次性要新建多个新类,这简直跟梦魇一样。
当你意识到这些问题时,你在考虑,我可心将这些方法放在不同的协议类中,像这样:
@protocol WYXFlyProtocol <NSObjec>
- (void)fly;
@end
让拥有飞行行为的鸭子类都遵守该协议,实现各自的- (void)fly 方法。可是这样一来,fly方法的代码实现就没有办法复用了!
来下面的设计原则:
『找出程序中的可能需要变化之处,把它们独立出来,不要各那些不需要变化的代码混在一起』
『针对接口编程,而不理针对实现编程』
在上述内容中,dispaly 、fly、quack方法都是有可能变化的,所以将这些方法独立出来。
我们考虑使用多态 来实现具体代码的独立。
以fly方法为例,新建WYXFlyBehavior类作为抽象类,声明 - (void)fly ,但具体实现为空:
- (void)fly{
// do nothing !
}
然后新建WYXCannotFly类,继承自WYXFlyBehavior类。重写fly方法
- (void)fly{
NSLog(@"I can not fly !");
}
继而为基类Duck增加WYXCannotFly类型的属性及performFlyBehavior方法
@property (nonatomic, strong) WYXFlyBehavior *flyBehavior;
- (void)performFlyBehavior;
performFlyBehavior方法的实现为
- (void)performFlyBehavior{
[self.flyBehavior fly];
}
现在,让我们来新建一个继承Duck自的WYXRubberDuck的子类。并重写其构造方法
- (instancetype)init{
self = [super init];
if (self) {
self.flyBehavior = [[WYXCannotFly alloc] init];
}
return self;
}
当我们需要创建一个WYXRubberDuck对象并调用其fly方法时,就可以这样:
WYXRubberDuck *rubberDk = [[WYXRubberDuck alloc] init];
[rubberDk performFlyBehavior];
这样,利用多态,我们继可以享受继承为我们带来的便利,又去除了继承所会产生的麻烦。
这也就是 策略模式:将会生变化的部分定义为算法族,分别封装起来,让它们之间可以相互替换,可以让算法的变化独立于使用算法的客户。
我们的代码现在变得有弹性,会减少很多的重复工作。但你可能会发现,上述的两条设计原则,我们只是实现了把变化的部分提取出来,并没有做到 『面向接口编程』。不要急,这只是入门篇,并不是我们的最终方案。
代码传送门:
网友评论