[关闭]
@Catyee 2017-05-23T13:52:06.000000Z 字数 2829 阅读 442

Head First设计模式学习笔记

java 设计模式 策略模式


设计原则:

举例:

世界上有各种鸭子,它们外观不同,但是都会游泳,也都会叫。用传统的面向对象的思路来设计,首先有一个Duck的抽象超类,并让各种鸭子继承这个超类。

Duck
quack()//鸭子叫的方法
swim()//鸭子游泳的方法
display()//鸭子外观的方法

如果是传统的面向对象思想,所有的鸭子都会叫,也都会游泳,所以quack()方法和swim()方法放在超类Duck中实现,而由于每只鸭子的外观不同,所以display()方法在Duck类中设计成抽象方法,由具体的鸭子继承超类后自己去实现。比如现在有绿头鸭MallardDuck和红头鸭ReaheadDuck,它们继承超类Duck,所以有了quack和swim的功能,同时实现display方法从而有了不同的外观。

但是这样设计有一个问题:
需求开始增加,经过分析,发现鸭子都会飞,所以要给鸭子加上飞的功能。最普通的方法是在Duck超类中加一个fly()的方法并实现。这样每一个鸭子继承Duck之后就有了fly()的功能。但是发现需求有误,实际上并不是所有鸭子都会飞。比如尖叫鸭(类似于尖叫鸡),会叫也会游泳,但是尖叫鸭的叫声是“吱吱”,而不是普通鸭子的“咕咕”。所以尖叫鸭继承Duck之后首先要覆盖掉超类中的quack()方法。最严重的问题是尖叫鸭不会飞,但是继承Duck后它有了飞的功能,这肯定不对。同样的方法就是子类继承超类之后覆盖掉Duck中的fly()方法。但是,如果以后还要加入另一种诱饵鸭子,它其实是一个木头假鸭,不会叫也不会飞。难道又要覆盖掉quack()方法和fly()方法吗?这样太不优雅了。可以看到用继承的方式来提供Duck的行为,会使代码牵一发动全身,不便于维护,也不便于扩展。

所以需要一个更清晰的方法,让“某些”而不是全部鸭子可飞或者可叫。
很自然的一种想法是把飞好叫的行为从Duck超类拿出来,另外设计成接口:Flyable和Quackable。

Duck//抽象类 Flyable//接口 Quackable//接口
swim()//普通方法 fly()//接口中的抽象方法 quack()//接口中的抽象方法
display()//抽象方法

这样尖叫鸭可以继承Duck超类,同时实现Quackable接口,而普通鸭子可以继承Duck超类并同时实现Flyable和Quack接口,但是可以看到,每实现一次接口就要重写一此接口中的方法,重复的代码就会急剧增加。显然这不是一种好的方法。

可以看出造成目前这种困境的原因是需求一直在改变。而现实中需求的改变又恰恰是经常会出现的问题。这个时候就要找出应用中可能需要经常变化的地方,把他们独立出来,不要和那些不需要变化的代码混在一起。也就是把会变化的部分取出并封装起来,好让其他部分不会受到影响。
那么具体到这个例子中要怎么做呢?首先在原始的Duck类中把fly()和quack()拿掉,然后建立两组类,一类与fly相关,一类与quack相关,它们分别实现FlyBehavior和QuackBehavior接口。每一组类将实现各自的动作,可能有一个类实现Quackbehhavior接口完成了“咕咕叫”(Quack类),另一个类实现Quackbehhavior接口完成了“吱吱叫”(SQuack类),还有一个类实现Quackbehhavior接口完成了“安静”(MuteQuack类)。另外还有一个类实现FlyBehavior接口完成了“飞”的功能(FlyWithSwing类),还有一个类实现FlyBehavior接口完成了“不能飞”(FlyNoWay类)。
但仅仅是这样还不够,因为Java中并没有多继承,如果继承了Duck超类,就不能继承那些实现了FlyBehavior或QuackBehavior的具体实现类。所以要想一种办法把Duck类和我们分离出来的行为整合起来。其实就是在Duck类中添加FlyBehavior和QuackBehavior的属性:

Duck
FlyBehavior flyBehavior//飞属性
QuackBehavior quackBehavior//叫属性
swim()//鸭子游泳的方法
display()//鸭子外观的方法
performFly()//飞行方法
performQuack()//叫方法
  1. public class Duck{
  2. FlyBehavior flyBehavior;
  3. QuackBehavior quackBehavior;
  4. //set、get方法
  5. ...
  6. public abstract void display();
  7. public void swim(){
  8. ...
  9. }
  10. public void performFly(){
  11. flyBehavior.fly();
  12. }
  13. public void performQuack(){
  14. quackBehavior.quack();
  15. }
  16. }

这样当我们实现尖叫鸭的时候:

  1. public class ScreamDuck extends Duck{
  2. public ScreamDuck(){
  3. quackBehavior = new SQuack();
  4. flyBehavior = new FlyNoWay();
  5. }
  6. @override
  7. public void display(){
  8. ...
  9. }
  10. }

这样就实现了一个尖叫鸭。而普通绿头鸭子就是:

  1. public class MallardDuck extends Duck{
  2. public MallardDuck(){
  3. quackBehavior = new Quack();
  4. flyBehavior = new FlyWithSwing();
  5. }
  6. @override
  7. public void display(){
  8. ...
  9. }
  10. }

这样维护和扩展都容易多了。

这就是策略模式
**

策略模式的定义:

**
定义了算法簇,分别封装起来,让它们之间可以互相替换。这个模式可以让算法的变化独立于使用算法的客户。

策略模式的结构:

策略模式的结构
**

策略模式的组成:

环境类(Context):用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。可定义一个接口来让Strategy访问它的数据。
抽象策略类(Strategy):定义所有支持的算法的公共接口。Context使用这个接口来调用某ConcreteStrategy定义的算法。
具体策略类(ConcreteStrategy):以Strategy接口实现某具体算法。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注