设计模式_第二篇_策略模式

+++ 本文的内容是我通过学习《Head First设计模式》这本书而写的。
+++ 作为我要描述的第一个模式,首先要说说什么是设计模式,然后,用一个实例,并在对这个实例的不断改进中,引出策略模式。与其空泛地给出一堆描述,倒不如给出一个实例,一个情景,最后引出你要说的东西。因为,人们对于事物的理解是,越是具体的东西,就越容易理解,而但凡理论性、思想性的东西,你无论怎样描述它,也只是用一个概念去解释另一个概念而已。对于一个没有多少项目经验的人来说,着实不容易理解。《Head First设计模式》这本书做得就比较好。
+++ 在上面这句话中,有三个关键词:情境、问题和解决方案。所谓情境是应用某个设计模式的情况;所谓问题是你在某个情境下达到的目标,但也可以是某个情境下的约束;解决方案是你所追求的,一个通用的设计,用来解决约束,达到目标。
+++ 需要明确以下几点。首先,在项目中,不一定非要使用设计模式。就想书中说的,使用设计模式只是为了让程序更加灵活、更加容易维护,尤其是当需求变化的时候。但模式会无形中增加程序的复杂性,这是我们所不期望的。我们期望,用简单的方法来解决复杂的问题,使程序更容易让人理解。
++ 第二点,设计模式的初学者的误区,包括我自己,是总是希望为自己的程序找到一个模式,虽然你的初衷是想练习这些模式。有一句比较经典的话:“我要为‘Hello World’找一个设计模式。
++ 第三点,你可以不会设计模式,但你不能不知道它的存在。
++ 最后,模式本身是不存在,它只是在我们长期的项目中,得出的经验。你可以有自己的模式,并发布出去给别人使用。但是你的模式必须有三次成功的案例,并经过其他人的评价后才可以被列入模式目录。
+++ 下面,通过一个实例,来说明策略模式。
+++ 假如,你所在公司要求你制作一个模拟各种鸭子的飞行程序,你立刻就会想到,所有的鸭子都会游泳、都会叫,那么,我可以设计一个鸭子的抽象类(Duck),然后让所有的鸭子(如绿头鸭MallardDuck或是红头鸭RedHeadDuck)都继承这个抽象类。静态结构图如图1所示:


++ 图中,Duck类是一个抽象类。MallardDuck类和RedHeadDuck继承Duck类。因为,所有的鸭子都会游泳、都会叫,所以由Duck类来实现quack()和swim()方法,而每个鸭子都有自己的display()方法,所以Duck类中的display()是抽象方法。
++ 这个看似不错的设计,有什么缺点呢?现在公司要求——鸭子要能飞。你将很容易想到,只要在Duck类中,加入fly()方法,那么继承此类的类就都可以拥有这个方法。静态结构图如图2所示:


++ 但是问题也就来了——不会飞的橡皮鸭RubberDuck到处飞。因此,在抽象类Duck中加入fly()方法后,会导致其所有的子类都具备fly()方法,连不该具备fly()方法的子类也无法避免。解决的方法也很容易想到,在子类中覆盖掉fly()方法即可。
++ 但是这样做也有问题啊!如果以后再加入诱饵鸭DecoyDuck呢?诱饵鸭即不会飞也不会叫,所以,除了要覆盖fly()方法,还要覆盖quack()方法。有一点很容易想到,一个公司的产品,每过一段时间会更新,比如加入其他种类的鸭子。这样,你不得不每次都要检查fly()和quack()方法。因此,这也不是一个好的解决方案。
++ 那么采用接口总可以吧!静态结构图如图3所示:


++ 对于这个设计,所有Duck的子类必须实现Flyable和Quackable接口中的fly()和quack()方法。虽然不会出现,橡皮鸭RubberDuck到乱处飞的情况,但是代码无法达到复用。这只是从一个恶梦跳到另一个恶梦而已。
++ 那么究竟应该怎么做呢?找出应用中可能需要变化的地方,把它们独立出来,不要和那些不需要变化的代码混在一起。也就是说,将鸭子的“飞行”和“叫”的行为从抽象类Duck中分离出来。静态结构图如图4所示:

+++ 这个设计究竟好在哪里?我觉得有如下几点:
1) 将鸭子“飞”和“叫”的行为(方法)从抽象类Duck中分离出来,这样,Duck类以及其子类就不需要再知道“飞”和“叫”是如何实现的,有利于加入新的鸭子子类,而完全不会影响现有的代码;
2) 当需要添加新的鸭子“飞”的行为或是新的“叫”的行为时,只要继承相应的接口即可,完全不会影响现有的代码;
3) 为了使程序能在运行时改变鸭子“飞行”和“叫”的状态,让程序更加灵活,在Duck类中添加两个set方法和perform方法,分别设置鸭子“飞行”和鸭子“叫”的状态,然后再让perform方法执行这些鸭子的行为;
4) 另外,通常情况下,在实际的项目中,当我们需要添加新类型的鸭子时,不会直接继承Duck,而是用一个基类先继承这个抽象类,比如用ModleDuck类继承Duck类,再让新的鸭子类继承这个基类,这样,会使程序变得更加灵活。
+++ 从以上对策略模式的描述中,可以得到如下经验和结论:
1) 如果为了代码复用而使用继承,结局往往并不完美;
2) 针对接口编程;
3) 将程序变化的部分和不变化的部分分离。
+++ 所谓策略模式,就是它定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值