Head First Design Mode(2)-设计模式入门

该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!

 

设计模式入门:

    欢迎来到设计模式世界;

    我们会看到设计模式的用途和优点,再看看关键的OO原则,通过实例来了解模式是如何运作的;

    以往是代码复用,现在是经验复用;

 

模拟鸭子的应用:

    Duck(鸭子)会Quark(呱呱叫)和 Swim(游泳);

    我们定义超类Duck::quark();swim();dispaly();

        外观display方法是一个抽象方法,因为每种鸭子的外观各有不同;

    ADuck,BDuck子类型的鸭子实现了display()方法;共同继承自Duck类;

 

这是一种继承的方式,在学习Java时一定学习过类似的题目;

 

现在让鸭子能飞:

    利用继承,我们在Duck类中添加fly()方法;

    但是,很多“橡皮鸭子”飞来飞去,因为父类中的方法会被所有子类继承;

    导致很多不适合’飞’行为的子类也具有了该行为;

 

当涉及“维护”时,为了“复用”(reuse)目的而使用继承,结局并不完美;

 

继承下的做法:

    在橡皮鸭类中把fly()方法覆盖掉,让它什么都不做;

    带来的问题是,其他类型的鸭子都会受到影响,你不得不为其他不会叫的鸭子类重写fly()方法;

    而当加入新鸭子时,由不得不检查这些方法;

 

我们不想代码在多个子类中重复,又很难知道鸭子的全部行为,最重要的,这样修改 会导致 牵一发而动全身,这显然不是我们想要的;

 

利用接口:

    需要一个清晰的方法,让某些鸭子类型可飞或可叫;

    我们把fly()和quark()分别声明在接口Flyable和Quarkable中,需要飞的鸭子实现相应的接口即可;

Head First Design Mode(2)-设计模式入门

 

但这个设计真的好吗,这是一个多对多的关系,如果有20种鸭子,他们的飞行行为需要调整,那简直就是灾难;

 

我们看到:

    继承并不是适当的解决方式;

    接口解决了一部分问题,但是代码却没法有效的复用;

 

采用良好的OO软件设计原则:

    软件开发的不变真理就是变化——不管当初软件设计的多好,一段时间后,总要成长与改变;

 

把问题归零:

    我们知道了使用继承并不能很好的解决问题,因为鸭子的行为在子类里不断的变化,让所有的子类都有这些行为是不恰当的;

    使用接口虽然解决了一部分问题,但是接口只有声明不具有实现的代码,所以实现接口无法达到代码复用;

 

适应此情况的设计原则:

    找到应用中变化之处,独立出来,不要和不需要变化的代码混在一起;

    变化的代码进行封装,使代码变化引起的后果变小,系统变得更有弹性;

 

该原则的另一种思考方式:

    把会变化的部分取出来并封装起来,以便以后轻易地改动或扩充此部分,而不影响不需要变化的其他部分;

 

这几乎是所有设计模式背后的精神所在“系统中的某部分改变不会影响其他部分”;

 

分开变化的 和 不变的:

    新建两组类与Duck无关的,一组是fly相关,一组是quark相关,每组类将实现各自的动作(高飞类、底飞类,咕咕叫类、嘎嘎叫类…);

    这样取出来的一组fly相关类,就可以用来表示Duck的fly行为,一个鸭子可以有多种行为;

 

设计鸭子的行为:

    一切能有弹性;比如,新建一个绿头鸭,初始的飞行行为是高飞,在运行过程中,我们可以动态的改变成底飞;

 

第二个设计原则:

    针对接口编程,而不是针对实现编程;

 

比如fly行为被放在多个分开的类中,而这些类专门提供fly行为接口的实现;

这样鸭子类就不再需要知道行为的实现细节;

 

我们用接口代表了行为:FlyBehavior、QuarkBehavior;

    由具体的行为类而不是Duck类来实现行为接口;

 

以往是行为来自超类的具体实现,或继承某个接口由子类自行实现而来;这两种做法都依赖于实现,没办法改变行为;

 

“针对接口编程”真正的意思是“针对超类型编程”:

    因为所有实现接口的类,其接口类型,都可以看作是实现类的超类(或父类);

    由于实现了相同的接口,他们就是相同的类型了,变量声明类型都是超类型;

    这利用了多态,执行时会根据实际状况执行真正的行为;

    超类型,通常是一个抽象类或一个接口;

 

如此,只要是具体实现此超类型的类所产生的对象,都可以指定给这个变量;这意味着,声明类时不用理会以后执行时的真正对象类型;

 

针对实现编程:

    Dog dog1 = new Dog();
    dog1.bark();

 

针对接口编程:

    Animal animal = new Dog();
    animal.makeSound();

 

而且,子类实例化执行的new动作不再需要在代码中硬编码,实际的子类型是什么不一定要知道,我们只关心他是否能正确的执行相应的动作;

 

实现鸭子的行为:

    FlyBehavior-interface: fly();

        FlyHigh: :fly();

        FlyLow: :fly();

    QuarkBehavior: quark();

        QuarkGugu: :quark();

        QuarkGuagua: :quark();

 

    这样的设计这些行为被其他代码复用,因为它们已经和鸭子无关了;

 

整合鸭子的行为:

    鸭子现在将飞和叫的动作“委托”给了其他对象处理,而不是用Duck类的叫和飞;

1)在Duck类中加入两个实例变量,均声明为接口类型;

2)使用performFly()和performQuark()方法,其中通过实例变量调用fly()和quark();

    鸭子对象不亲自处理叫和飞的行为,而是委托给两个实例变量对象;

3)新建鸭子DuckSub1,在初始化方法中为实例变量赋值:高飞 咕咕叫!(实例变量是从父类继承来的)

 

Head First Design Mode(2)-设计模式入门

编译运行下:

bogon:第一章 huaqiang$ javac *.java
bogon:第一章 huaqiang$ java DuckDrive
Fly high !
Quark gugu !
DuckSub1!

由于实例变量是接口类型的,我们也可以为它指定相同接口不同的实现类;

 

动态设定行为:

    加入如下两个方法;

public void setFlyBehavior(FlyBehavior fb){
    flyBehavior = fb;    
}

public void setQuarkBehavior(QuarkBehavior qb){
    quarkBehavior = qb;    
}

这样,就可以把 新的行为实现设定给指定的鸭子;

 

封装行为的大局观:

    稍稍改变描述的方式,我们把一组行为,描述为一族算法;

    那么,算法就表示鸭子能做的事,类似的场景:用一群算法计算某城市的销售税金;

 

封装飞行行为(各种飞行方式):一族算法;

……

 

我们发现同一族里的算法是可以互换的;

 

在上述示例中一定要明确几种关系:IS-A、HAS-A、IMPLEMENTS;

 

有一个的关系可能比是一个更好:

    每个鸭子有一个FlyBehavior和QuarkBehavior,好将飞和叫 委托给他们代为处理;

    这里将两个行为类 进行了 “组合”;鸭子的行为不是继承来的,而是适当行为对象组合来的;

 

第三个设计原则:

    多用组合,少用继承;

 

实用组合将算法族封装成类,在符合正确的接口标准时,建立的系统也很有弹性;

 

此间,我们用了学到的第一个设计模式:策略模式;

 

策略模式:

    定义了算法族,分别封装起来,让他们之间互相替换,此模式让算法的变化独立于使用算法的客户;

 

如何使用设计模式:

    我们开发需要使用别人设计好的库和框架;我们挑选好组件,把他们放到合适的地方;

    但是,库和框架无法帮助我们将应用组织成容易了解、容易维护、具有弹性的架构,所以需要设计模式,并在合适的场景中使用之;

    

设计模式告诉我们如何组织类和对象已解决某种问题;

 

有很多面向对象的原则,使用所有模式,当没有找到合适的设计模式时,遵循这些原则即可,下一章我们继续学习;

 

总结:

1.知道OO基础,不足以设计出良好的OO系统;

2.良好的OO设计必须具备 复用 可扩充 可维护三个特性;

3.模式可以让我们建造出具有良好OO设计质量的系统;

4.模式被认为是经历验证的OO设计经验;

5.模式不是代码,而是针对设计问题的通用解决方案;

6.大多数模式和原则,都着眼于软件变化的主题;

7.我们常把系统中会变化的部分抽出来封装;

8.模式让开发人员之间有共享的语言,能够最大化沟通的价值;

 

OO基础:

    抽象 封装  多态 继承;

OO原则:

    封装变化

    多用组合,少用继承

    针对接口编程,不针对实现编程;

OO模式:

    策略模式:定义算法族,分别封装起来,让他们之间互相替换,此模式让算法的变化独立于使用算法的客户;