码迷,mamicode.com
首页 > 其他好文 > 详细

headFirst学习笔记之一:设计模式入门(4.28)

时间:2015-04-28 22:33:26      阅读:151      评论:0      收藏:0      [点我收藏+]

标签:

1.简单的模拟鸭子应用:

超类Duck:呱呱叫(quack)和游泳两个方法由超类实现,因为所有的鸭子都有这个功能,display由子类自己实现,表示的是外观。

1 class Duck{
2    quack(){}
3    swim(){}
4    abstract display();
5 }

子类例子:

 1 class MallardDuck{
 2    @override
 3    display(){
 4    //外观是绿头
 5    }
 6 }
 7 
 8 class RedHeadDuck{
 9    @override
10    display(){
11    //外观是红头
12    }
13 }

 

2.需要增加会飞的鸭子:

joe的最想当然的做法:Duck类中添加fly()方法。

1 class Duck(){
2    quack(){}
3    swim(){}
4    abstract display();
5    fly(){}
6 }

问题出现了:并非所有的子类都会飞,比如子类是橡皮鸭子。所以在超类中添加某些新行为,会使得不具有该行为的子类也具有这些行为了。

joe然后进行弥补:将橡皮鸭中的fly()重写,但是什么都不做。

 1 class RubberDuck{
 2     quack(){
 3     //覆盖成吱吱叫
 4     }    
 5     display(){
 6     //外观是橡皮鸭
 7     }
 8     fly(){
 9    //什么都不做
10    }
11 }

问题出现了:如果以后又增加一个木头鸭呢,既不会飞也不会叫,难道也要一个一个的修改,覆盖为什么都不做吗?所以这种方法,会使得每次添加一个新类的时候,joe都要检查是否需要覆盖fly和quark方法。

于是joe觉得继承不是答案,决定改为用接口实现。建立flyable和quackable接口,需要实现这些功能的子类自己去实现接口就可以了。

 1 interface Flyable{
 2    fly();
 3 }
 4 
 5 interface Quackable{
 6    quack();
 7 }
 8 
 9 class MallardDuck extends Duck implements Flyable, Quackable{
10    display(){}
11    fly(){}
12    quack(){}
13 }
14 
15 class RubberDuck extends Duck implements Quackable{
16    display(){//橡皮鸭}
17    quack(){//吱吱叫}
18 }
19 
20 class RubberDuck extends Duck{
21    display(){//木头鸭}
22 }

问题出现了:因为接口不具有实现代码,所以实现接口会使得代码重复率太高。

3.合适的解决办法:

(1)为什么继承不是合适的办法:因为新增加的行为maybe不是所有子类都具有,在新增加子类时,我们还得检查,这个子类拥有父类所有的行为是否恰当,否则就要另外处理。比如木头鸭也是鸭子,但是他不叫不飞。所以不能把叫和飞放到duck类中。

(2)为什么接口不是合适的办法:因为接口不具有实现代码,所以实现接口会使得代码重复率太高。每一个子类中都要写一遍fly的实现,会疯的。

(3)解决办法的原则:找出变化之处,把它独立出来;针对接口编程,而非针对实现编程(这句话还不太懂)。

变化的部分是fly和quack,所以新建两组类(与Duck无关)FlyBehavior和QuackBehavior,再增加一个类Quiet。Duck仍然是superclass,但是fly和quack已经不在Duck里面了。

但是根据第二点原则,我们将类FlyBehavior和QuackBehavior改为接口。并且写对应的实现类。

 1 interface FlyBehavior{
 2    fly();
 3 }
 4 
 5 class FlyWithWings implements FlyBehavior{
 6    fly(){//实现鸭子飞行}
 7 }
 8 
 9 class FlyNoWay implements FlyBehavior{
10    fly(){//什么都不做,不飞}
11 }
 1 interface QuackBehavior{
 2   quack();
 3 }
 4 
 5 class Quack() implements QuackBehavior{
 6   quack(){//实现呱呱叫}
 7 }
 8 
 9 class Squeak() implements QuackBehavior{
10   quack(){//实现吱吱叫}
11 }
12 
13 class MuteQuack() implements QuackBehavior{
14   quack(){//不叫}
15 }

这样的话,可以让飞行和叫的行为被复用,如果要添加新行为,也不会影响现有的行为类和使用行为的鸭子类。

4.插播刚刚关于“针对接口编程,不要针对实现编程”的理解。

针对实现编程:就是前面两种方法,将行为的实现写到超类里,或者写到接口然后子类继承接口再自己写实现。这样的不好前面已经讲过了。

针对接口编程:就是第三种方法,定义行为接口,然后写行为类。将其与“使用这些行为”的duck类独立开来。

为什么要这么做:针对接口编程真正的意思是针对超类型编程,超类型可以是class也可以是interface(那为什么上面要定义为interface?),程序可以针对超类型编程,但是实现时会根据实际情况执行到真正的行为(比较拗口和难懂,看例子最生动)。

 1 class Animal{
 2   abstract makeSound();
 3 }
 4 
 5 class Dog() extends Animal{
 6   makeSound(){
 7     bark(); 
 8   }
 9   bark(){//汪汪叫}
10 }
11 
12 class Cat() extends Animal{
13   makeSound(){
14    meow();
15   }
16   meow(){//喵喵叫}
17 }

针对实现编程:

1 Dog d = new Dog();
2 d.bark;

针对接口编程:

1 Animal animal = new Dog();
2 animal.makeSound();
3 
4 a = getAnimal();
5 a.makeSound();

(其实还是有点懵,先这样吧)

5.整合鸭子的行为:(1)Duck将fly和quack行为委托delegate到别人那里处理,而不是用自己(Duck或者其子类)内部定义的fly和quack的具体实现。(2)在子类中设定fly和quack行为的实例变量。

 1 class Duck{
 2   QuackBehavior quackBehavior;
 3   FlyBehavior flyBehavior;
 4   
 5   performQuack(){
 6     quackBehavior.quack();
 7   }
 8   performFly(){
 9     flyBehavior.fly();
10   }
11   swim(){}
12   display(){}
13 }
1 class MallardDuck extends Duck{
2    MallardDuck(){
3      quackBehavior = new Quack();
4      flyBehavior = new FlyWithWings(); 
5    }
6 }

然后MallardDuck是真的能够实现呱呱叫和飞行了。

但是有点小问题:我们的原则是不针对具体实现编程,但是我们现在正在结构体里制造一个具体的实现类Quack和FlyWithWings!

这会在后面改正,现在只是初始化实例变量有点不够弹性,但是整体好多了啦~(这么自问自答的精分书也是够了。)

6.实际代码:

 1 public interface FlyBehavior {
 2    public void fly();
 3 }
 4 
 5 public class FlyNoWay implements FlyBehavior {
 6 
 7     public void fly() {
 8         // TODO Auto-generated method stub
 9       System.out.println("i can‘t fly!");
10     }
11 }
12 
13 public class FlyWithWings implements FlyBehavior {
14 
15     public void fly() {
16         // TODO Auto-generated method stub
17         System.out.println("i am flying!");
18     }
19 }
 1 public interface QuackBehavior {
 2    public void quack();
 3 }
 4 
 5 public class Quack implements QuackBehavior {
 6 
 7     public void quack() {
 8         // TODO Auto-generated method stub
 9         System.out.println("Quack!");
10     }
11 }
12 
13 public class MuteQuack implements QuackBehavior {
14 
15     public void quack() {
16         // TODO Auto-generated method stub
17         System.out.println("silence");
18     }
19 
20 }
 1 public abstract class Duck {
 2    FlyBehavior flyBehavior;
 3    QuackBehavior quackBehavior;
 4    
 5    public Duck(){}
 6    public abstract void display();
 7    public void performFly(){
 8        flyBehavior.fly();
 9    }
10    public void performQuack(){
11        quackBehavior.quack();
12    }
13    public void swim(){
14        System.out.println("i can swim!");
15    }
16 }
17 
18 public class MallardDuck extends Duck {
19     public MallardDuck(){
20         quackBehavior = new Quack();
21         flyBehavior = new FlyWithWings();
22     }
23     public void display() {
24         // TODO Auto-generated method stub
25         System.out.println("i am a Mallard Duck!");
26     }
27 
28 }
1 public class MiniDuckSimulator {
2    public static void main(String[] args){
3        Duck mallard = new MallardDuck();
4        mallard.performFly();
5        mallard.performQuack();
6    }
7 }

输出结果是:

1 i am flying!
2 Quack!

我将Duck mallard = new MallardDuck();改为MallardDuck mallard = new MallardDuck();输出结果是一样的,那么写作duck的原因一定和之前说的“针对接口编程有关”,但是这里我还没看出来用处(应该是我笨,这本精分的书告诉我多和自己对话噗)。

7.动态设定行为:如果不想在鸭子的结构体里实例化,而是通过setter来动态设定。

 1 class Duck{   
 2    .... 
 3    public void setFlyBehavior(FlyBehavior fb){
 4        flyBehavior = fb;
 5    }
 6    
 7    public void setQuackBehavior(QuackBehavior qb){
 8        quackBehavior = qb;
 9    }
10 }

8.那么如果现在要添加一个模型鸭,一套流程走下来该怎么弄呢?

(1)新建鸭子类型:ModelDuck,不会飞但是会叫的模型鸭哦~但是要给它提供一个乘坐火箭飞的技能!即使我不会飞,但是我也是有梦想的!(model duck告诉自己)

 1 public class ModelDuck extends Duck {
 2 
 3     public ModelDuck(){
 4         quackBehavior = new Quack();
 5         flyBehavior = new FlyNoWay();
 6     }
 7     public void display() {
 8         // TODO Auto-generated method stub
 9        System.out.println("i am a Model Duck!");
10     }
11 
12 }

(2)新建新的飞行为类:FlyRocketPowered

1 public class FlyRocketPowered implements FlyBehavior {
2 
3     public void fly() {
4         // TODO Auto-generated method stub
5        System.out.println("I am flying with a rocket!");
6     }
7 
8 }

(3)改变测试类,为model duck实现自己的梦想!

1 public class MiniDuckSimulator {
2    public static void main(String[] args){
3        //用的是duck
4        Duck model = new ModelDuck();
5        model.performFly();
6        model.setFlyBehavior(new FlyRocketPowered());
7        model.performFly(); 
8    }
9 }

输出结果:

1 i can‘t fly!
2 I am flying with a rocket!

可以发现,对原程序需要做的改动几乎为0,而且感觉起来就很动态啊~

9.跳出duck,看看整体的格局。

我们把行为想成是“一族算法”,算法代表鸭子能做的事情(不同的叫法和飞行法),客户就可以使用封装好的飞行和呱呱叫算法族;当你将两个类结合起来用,这就是组合。设计原则:少用继承,所用组合。

这一章所学习的就是策略模式(strategy pattern):策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

headFirst学习笔记之一:设计模式入门(4.28)

标签:

原文地址:http://www.cnblogs.com/liyuhui21310122/p/4464086.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!