标签:影响 display 代码 巩固 image ted 更改 需要 改变
《Head First设计模式》看了一部分才对设计模式有了初步的了解:它其实是开发过程中很多前人的经验与智慧的总结,帮助你在开发时采取更好的方式去设计各个类、方法、以及它们之间的调用、实现方式,让代码保持灵活性的同时又能更好地复用。基于学过一块知识一定要用文字记录、总结、巩固,而不是走马观花的原则,趁最近终于有空,特将前一段时间看的关于“策略模式”的内容总结于此。
A公司要做一套模拟鸭子的游戏,游戏中会出现各种鸭子,一边游泳戏水,一边呱呱叫,还有一些会飞。
设计一个超类Duck,包含方法quack()、swim()、fly() 分别模拟鸭子的叫、游泳、飞行等行为,再包含一个抽象类 display() ,用于展示各个鸭子不同的外观,让每个鸭子子类继承父类时实现display();
弊端:
这样做虽然每个鸭子子类继承父类时就同时拥有了父类的方法,可以达到代码复用的目的,但是这样会使某些并不适合该行为的子类也具有该行为。如果某些子类鸭子,如“橡皮鸭”,它不具备某些功能(如飞行),它就不应该拥有这个飞行的功能。当然,你可以在子类中通过 @Override 覆盖这个方法。但是当各个不同的子类都需要通过覆盖修改不同的方法时,就会非常繁琐,而且容易出现纰漏,且这些新覆盖的方法不能被复用。如“橡皮鸭”只会叫不会飞,“木头鸭”不会叫也不会飞。以后每当有新的鸭子子类出现,你都要去检查并可能需要覆盖这些方法,想想都让人抓狂。
在超类 Duck 中将quack()、fly() 等可变的方法用接口 QuackBehavior 接口中 Quackable(),FlyBehavior 接口中 Flyable() 来代替,然后在每个鸭子子类中,如果具有“飞行”或“叫”这个功能就实现“飞行“或”叫“这个接口。
弊端:
代码无法复用,如果有100个子类,都具有飞行的行为,你就需要重复100次代码。
由于 fly() 和 quack() 会随着鸭子的不同而改变,所以把这两个行为从 Duck 类中分开,建一组新类来代表各个行为。
利用多态,针对超类型编程,执行时根据实际对象执行到真正的行为,不会被绑死在超类型的行为上。以前的做法是:行为来自超类的具体实现或是继承某个接口并由子类自行实现。这两种方法都捆绑于”实现“,无法方便地更改行为。现在我们利用接口代表每个行为,比如FlyBehavior,QuackBehavior,然后让各个行为类实现这些接口,然后在 Duck 类中只要定义这个接口的实例变量即可,这样在各个鸭子子类中如果想拥有某种特定的行为,只要用这个接口实例变量去引用具体的行为类即可。
飞行和叫这两种不同的行为,我们分别为其建立两组不同的行为类,然后在 Duck 类中通过接口实例变量结合起来,这就是”组合“。使得系统具有很大的弹性,还可以”在运行时动态地改变行为“。
(1)目录结构
(2)接口 FlyBehavior 和 QuackBehavior
public interface FlyBehavior { void fly(); } public interface QuackBehavior { void quack(); }
(3)FlyBehavior 实现类【QuackBehavior 实现类和 FlyBehavior 类似】
public class FlyNotWay implements FlyBehavior { @Override public void fly() { System.out.println("I can not fly"); } } public class FlyWithSwings implements FlyBehavior { @Override public void fly() { System.out.println("I can fly"); } } public class FlyRocketPowered implements FlyBehavior { @Override public void fly() { System.out.println("I can fly with rocket"); } }
(4)鸭子外观实现类 MallardDuck 野鸭子和 ModelDuck 模型鸭子
public class MallardDuck extends Duck { public MallardDuck() { flyBehavior = new FlyWithSwings(); quackBehavior = new QuackWithGaGa(); } @Override public void display() { System.out.println("I am a MallardDuck"); } } public class ModelDuck extends Duck { public ModelDuck() { flyBehavior = new FlyNotWay(); quackBehavior = new QuackNotWay(); } @Override public void display() { System.out.println("I am a ModelDuck"); } }
(5)Duck 抽象类
public abstract class Duck { protected FlyBehavior flyBehavior; protected QuackBehavior quackBehavior; /** * 鸭子外观方法 */ public abstract void display(); public void performFly() { flyBehavior.fly(); } public void performQuack() { quackBehavior.quack(); } public void setFlyBehavior(FlyBehavior fb) { this.flyBehavior = fb; } public void setQuackBehavior(QuackBehavior qb) { this.quackBehavior = qb; } }
(6)执行结果
I am a MallardDuck
I can fly
I can quack
I am a ModelDuck
I can not fly
I can not quack
I can fly with rocket
策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。这是书里给出的策略模式的定义,依我个人的理解,这种模式的关键就是要将系统中的可变行为抽象出来,单独进行封装,用一组行为类(算法族)来实现该特定的接口,这样任何类(如 Duck)如果想拥有这些算法族中的某个算法,都可以通过定义接口实例变量而拥有该整个算法族,在子类中再对该变量进行赋值。
像这个例子里,通过定义了FlyBehavior,这样以后任何想有飞行行为的类如飞机,都可以调用这个接口及实现它的各个飞行类。且飞行行为的变化可以通过增加新的飞行类来实现,不会对其他部分(如quack()、display() 等行为,亦或是已拥有某些特定飞行行为的对象)造成任何影响,即”算法的变化独立于使用算法的客户“。而且各个飞行行为之间也可以互相替换;即 setFlyBehavior(FlyBehavior fb)。
可见,设计模式的应用让整个项目的代码拥有了极大的灵活性,且达到了代码复用的效果。设计模式其实是一种设计上的思维方式,是前人的智慧和经验的总结,其真正的精髓不是看过就能学会的,还是需要在实际应用中不断地实践摸索,慢慢体会。
标签:影响 display 代码 巩固 image ted 更改 需要 改变
原文地址:https://www.cnblogs.com/blogtech/p/12870662.html