设计模式之策略模式
《Head First 设计模式》,好书!
策略模式经典的例子:鸭子模型。
假设我们主要研究鸭子(DUCK)的"叫(quack)"和鸭子的飞(fly)。
public abstract class Duck{ //呱呱叫 quack(); //飞 fly(); }
1.假如,我们想要很多种鸭子在屏幕上飞和叫,想到了设计一个DUCK父类,让许许多多的鸭子子类继承fly()和quack()。这样鸭子们就可以自由的在屏幕飞和叫了。
2.问题来了,屏幕上有个RubberDuck(橡皮鸭)在飞和呱呱叫,这怎么行!RubberDuck不会飞并且叫声是"吱吱"而不是"呱呱"。于是灵机一动,把RubberDuck
的fly()和quack()重写,覆盖父类方法。屏幕看上去舒服多了~
3.屏幕上怎么又多出来了一个DecoyDuck(诱饵鸭,木头制作),再重写父类方法,让它不会飞也不会叫。
4.....不一会就感到厌烦了,老是重写了父类方法怎么行。
直接继承有太多的缺点:
- 代码在多个子类中重复(一直重写父类方法有木有)
- 运行时行为不容易改变(我的DecoyDuck现在会飞了,能不能在运行的时候改变它)
- 很难知道所有鸭子的全部行为(总不会把所有子类数一遍吧)
- 牵一发而动全身
于是,我们要改变设计方法。
这些行为(fly和quack)继承起来太难控制,用接口把。嗯~把它们从DUCK父类中提取出来,分别定义一个接口。
设计原则:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混合在一起。
鸭子发出的声音不一样,可以"呱呱Quack"、"吱吱Squack"、"无声MuteQuack",分别实现fly的接口(FlyBehavior)
这样,鸭子发出quack的时候就可以调用特定的方法。
因为分离出来,这些行为和鸭子完全无关。实现青蛙的时候也可以拿走"呱呱叫"的方法
设计原则:针对接口编程,不针对实现编程。
/* Dog继承Animal */ //针对实现编程 Dog d = new Dog; d.bark(); //针对接口编程,这里就是多态。 Animal animal = new Dog(); animal.bark(); //用animal调用bark()方法,至于animal是什么,我们并不关心。
那么还有一个问题,每个鸭子都去实现特定的行为方法(如:Quack),这些行为是固定的。可是我想在运行时动态的改变鸭子的行为怎么办?
比如,DecoyDuck突然可以飞了。
貌似可以用多态,针对接口编程。"叫"定义了接口QuackBehavior,"飞"定义了接口FlyBehavior。
然后Quack(呱呱叫),SQuack(吱吱叫),MuteQuack(无声)实现了QuackBehavior。
同理,FlyBehavior也有具体实现。
那么,父类Duck:
public abstract class Duck{ //声明QuackBehavior的接口 QuackBehavior quackBehavior; //鸭子对象不直接执行叫的行为,委托给quackBehavior对象。 public void performQuack(){ quackBehavior.quack(); } //可以动态的修改行为,用quackBehavior来什么行为要什么行为。
//比如传入MuteQuack,然后在调用performQuack,是不是就相当于 MuteQuack.quack(),不发声了。
public void setQuackBehavior(QuackBehavior qb){
quackBehavior = qb;
} }
有一个performQuack()方法,这个方法代替被删除的quack方法(直接继承的quack方法,在最上面)。
作用就是去使用接口对象 quackBehavior调用quack方法。(或许画上类图更清楚)
//叫的接口 public interface QuackBehacior{ public void quack(); } ------------------------------------具体实现-------------------------------------- public class Quack implements QuackBehavior{ public void quack(){ System.out.println("我会呱呱叫"); } } ---------- ------------ public class Squack implements QuackBehavior{ public void quack(){ System.out.println("我会吱吱叫"); } } ---------- ------------ public class MuteQuack implements QuackBehavior{ public void quack(){ System.out.println("我不会发声"); } }
像不像上面anmial和dog的调用,不需要知道具体是谁,委托给接口对象(可以这样理解吧)。
那么,开始了
写一个鸭子类继承Duck
public MallardDuck extends Duck{ public MallardDuck(){ //quackBehavior是继承下来的 quackBehavior = new Quack(); } }
开始测试:
public static void main(String [] args){ Duck mallard = new MallardDuck(); mallard.performQuack(); //输出呱呱叫 mallard.setQuackBehavior(new MuteQuack()); mallard.performQuack();//输出无声 } /* 可能你还有些迷糊,那逐一解释代码 1.Duck mallard = new MallardDuck() 执行: quackBehavior = new Quack()//就像实例化了quackBehavior 2.mallard.peiformQuack(); 执行: quackBehavior.quack();//该对象调用Quack类里的quack()方法 3.mallard.setQuackBehavior(new MuteQuack()); quackBehavior = qb; //qb是不是 new MuteQuack(); 是不是所有的都是围绕QuackBehavior 接口,针对接口编程。 */
设计原则:多用组合(composition),少用继承
上文中的fly(实现方式和quack相同)、quack行为就是组合。鸭子的行为不是继承来的,而是多个行为 组合 来的。
这些行为完全可以被青蛙(呱呱),麻雀(飞)使用,提升了代码的复用程度。
把"行为"抽象一下,不再叫做行为而是算法族。然后把他们封装起来(嗯,封装),可以动态的相互替换(鸭子的行为动态替换)。并且这些算法和要使用这些算法的鸭子没有任何关系。
总结:
策略模式定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
这样看起来是不是很高大上~
转载请注明出处。http://www.cnblogs.com/yuhanghzsd/p/5357981.html