Head First--设计模式
看了第一章的入门内容,我觉得真的很有意思,下面我就把书上那个对Duck类的学习总结一下,我们从问题的提出开始。问题一开始是这样的:设计一个Duck类,这个类时很多种类鸭子的超类,于是,我们不加思索的设计了下面的代码:
public class Duck{ void quack();//鸭子叫声 virtual void display();//鸭子的外观显示 void swim();//鸭子都会游泳 } public class MallardDuck:Duck{ void override display() { //显示为绿鸭子 } } public class ReadheadDuck:Duck{ void override display() { //显示为红鸭子 } }
这样设计看似非常的完美,但是软件人员心中唯一的真理是“change”。过了一段时间,我们需要给鸭子天上能够飞翔的功能,看似只需要在Duck类中加入void fly()这个函数就可以了,但是,这是现实并不想我们想的那样,出现了木头鸭子也会飞翔的情况,这时们会想,我们可以把fly做成为virtual类型的,在木头鸭子中把它覆盖掉就可以了,不让他做任何的事情就可以保证木头鸭子不会飞了,看样这问题已经解决了,但是,这时出现了很多代码的重复,我们要对每一个特殊的鸭子进行编程,也就是看看是不是需要重载一些行为。我们可能需要覆盖点很多的行为,这简直就是噩梦。
这时我们想到了使用接口,把fly放到一个Flyable接口中,这样我们就可以让需要飞翔的鸭子继承这个接口,而不需要的就不用继承这个继承接口了,看似非常的完美。但是,这就造成了没法复用的代码,比如说,如果有一百个鸭子的飞翔行为是相同的,我们就要在这个一百种鸭子中写一百遍相同的代码,这又是一个噩梦的开始。
这时我们想到了OO设计原则中的一条:“找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码放到一块(OO设计原则1)”。现在我们需要分出需要变化的fly行为来,把它和鸭子这个类分离开来,这时我们会想到OO设计原则中的一条“针对接口编程,而不是针对实现编程(OO设计原则2)”。也就是说,我们可以利用FlyBehavior接口把需要变化的fly封装起来,然后针对这个接口进行编程,而不是针对每一个鸭子进行编程。所以我们完成了对fly的设计
public interface FlyBehavior{ public void fly(); } public class FlyWithWings:FlyBehavior{ public void fly(){ //会飞的鸭子 } } public class FlyNoWay:FlyBehavior { public void fly(){ //不会飞的鸭子 } } public class Duck { FlyBehavior flyBehavior; void PerformaceFly { flyBehavior.fly(); } } pulbic class HeadDuck:Duck { HeadDuck() { flyBehavior=new FlyWithWings(); } } public class WoodDuck:Duck{ WoodDuck() { flyBehavior=new FlyNoWay(); } }
这样,我们就可以比较完整的设计鸭子这个类了。
这里我们仅仅就原理进行展示,我的例子将以完整代码形式呈现。
其实上面就是策略模式的使用:定义了算法簇,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。就像上面的鸭子的行为,我们可以把它理解为是鸭子的一些算法,这样,这些行为就构成了一些算法簇,所有,也就是我们说的策略模式。
除此之外,还有一个设计原则:多用组合,少用继承。