关于复合模式、策略模式,工厂模式、观察者模式、装饰模式的一个好例子

来源:互联网 发布:龙门镖局源码 编辑:程序博客网 时间:2024/06/03 02:13

原帖地址:http://chat.javaeye.com/blog/178414

 

我们将再次使用鸭子的例子来说明并学习,下一章我们会拜访一个真正的复合模式--MVC,没错,就是MVC.

1.首先,我们将创建一个Quackable接口

Java代码 
  1. public interface Quackable {  
  2.     //只做一件事:呱呱叫  
  3.     public void quack();  
  4. }  

 

2.现在,某些鸭子实现了Quackable接口

Java代码 
  1. public class MallardDuck implements Quackable {  
  2.     //绿头鸭子  
  3.     public void quack() {  
  4.         System.out.println("Quack");  
  5.     }  
  6. }  
  7. public class RedheadDuck implements Quackable {  
  8.     //红头鸭子  
  9.     public void quack() {  
  10.         System.out.println("Quack");  
  11.     }  
  12. }  
  13. public class DuckCall implements Quackable {  
  14.     //如果我们没有加入别的种类的鸭子,就不大好玩了  
  15.     //鸭鸣器也会呱呱叫,虽然不十分像  
  16.     public void quack() {  
  17.         System.out.println("Kwak");  
  18.     }  
  19. }  
  20. public class RubberDuck implements Quackable {  
  21.     //橡皮鸭,声音其实是吱吱叫  
  22.     public void quack() {  
  23.         System.out.println("Squeak");  
  24.     }  
  25. }  

 

3.好了,我们有了鸭子,还需要一个模拟器,制造一个会产生鸭子,还要确认鸭子呱呱叫的模拟器

Java代码 
  1. public class DuckSimulator {  
  2.     public static void main(String[] args) {  
  3.         //创建一个模拟器,调用simulate方法  
  4.         DuckSimulator simulator = new DuckSimulator();  
  5.         simulator.simulate();  
  6.     }  
  7.     void simulate() {  
  8.         //每一种会呱呱叫的鸭子都有一份实例  
  9.         Quackable mallardDuck = new MallardDuck();  
  10.         Quackable redheadDuck = new RedheadDuck();  
  11.         Quackable duckCall = new DuckCall();  
  12.         Quackable rubberDuck = new RubberDuck();  
  13.         //模拟每种鸭子  
  14.         simulate(mallardDuck);  
  15.         simulate(redheadDuck);  
  16.         simulate(duckCall);  
  17.         simulate(rubberDuck);  
  18.     }  
  19.     void simulate(Quackable duck) {  
  20.         //重载了simulate方法来模拟一种鸭子,剩下的事让多态发挥它的魔法  
  21.         duck.quack();  
  22.     }  
  23. }  

 

4.只要有水塘的地方,就大概会有鸭子和鹅,我们为这个模拟器设计一个鹅类

Java代码 
  1. public class Goose {  
  2.     //鹅的叫声是咯咯  
  3.     public void honk() {  
  4.         System.out.println("Honk");  
  5.     }  
  6. }  

 

5.我们需要鹅适配器,既然鹅不会呱呱叫,那么我们可以利用适配器将鹅适配成鸭子

Java代码 
  1. public class GooseAdapter implements Quackable {  
  2.     Goose goose;  
  3.     //传入要适配的对象  
  4.     public GooseAdapter(Goose goose) {  
  5.         this.goose = goose;  
  6.     }  
  7.     //当调用quack时,会被委托到鹅的honk方法  
  8.     public void quack() {  
  9.         goose.honk();  
  10.     }  
  11. }  

 

6.现在,模拟器中也应该可以使用鹅了,我们需要做的就是创建goose对象,将它包装进适配器

Java代码 
  1. public class DuckSimulator {  
  2.     public static void main(String[] args) {  
  3.         //创建一个模拟器,调用simulate方法  
  4.         DuckSimulator simulator = new DuckSimulator();  
  5.         simulator.simulate();  
  6.     }  
  7.     void simulate() {  
  8.         //每一种会呱呱叫的鸭子都有一份实例  
  9.         Quackable mallardDuck = new MallardDuck();  
  10.         Quackable redheadDuck = new RedheadDuck();  
  11.         Quackable duckCall = new DuckCall();  
  12.         Quackable rubberDuck = new RubberDuck();  
  13.         //通过把goose包装进gooseAdapter,我们就可以让鹅像鸭子一样  
  14.         Quackable gooseDuck = new GooseAdapter(new Goose());  
  15.         //模拟每种鸭子  
  16.         simulate(mallardDuck);  
  17.         simulate(redheadDuck);  
  18.         simulate(duckCall);  
  19.         simulate(rubberDuck);  
  20.         //一旦鹅被包装进来,我们就可以把它当做其他鸭子的Quackable对象  
  21.         simulate(gooseDuck);  
  22.   
  23.     }  
  24.     void simulate(Quackable duck) {  
  25.         //重载了simulate方法来模拟一种鸭子,剩下的事让多态发挥它的魔法  
  26.         duck.quack();  
  27.     }  
  28. }  

 

7.测试一下上面的DuckSimulator,你会发现,鹅和其他鸭子一起叫.现在我们考虑一下,我们如何在不变化鸭子类的情况下,计算呱呱叫的次数呢?有什么模式可以解决这个问题?
8.让我们创建一个装饰者,通过把鸭子包装进装饰者对象,给鸭子一些新行为(计算次数),我们不必修改鸭子的代码

 

Java代码 
  1. //QuackCounter是一个装饰者,像适配器一样实现Quackable  
  2. public class QuackCounter implements Quackable {  
  3.     //我们用一个实例变量来记录被装饰的呱呱叫对象  
  4.     Quackable duck;  
  5.     //用静态变量跟踪所有呱呱叫次数  
  6.     static int numberOfQuacks;  
  7.     public QuackCounter(Quackable duck) {  
  8.         this.duck = duck;  
  9.     }  
  10.     //当quack被调用时,我们就把调用委托给正在装饰的Quackable对象,然后把叫声的次数加一  
  11.     public void quack() {  
  12.         duck.quack();  
  13.         numberOfQuacks++;  
  14.     }  
  15.     //给装饰者加入一个静态方法,以便返回在所有Quackable中发生的叫声次数  
  16.     public static int getQuacks() {  
  17.         return numberOfQuacks;  
  18.     }  
  19. }  

 9.现在我们必须包装在QuackCounter装饰折中被实例化的每个Quackable对象,如果不这么做,鸭子会到处乱跑而使得我们无法统计叫声次数,我们需要更新模拟器,以便创建被装饰的鸭子

Java代码 
  1. public class DuckSimulator {  
  2.     public static void main(String[] args) {  
  3.         DuckSimulator simulator = new DuckSimulator();  
  4.         simulator.simulate();  
  5.     }  
  6.     void simulate() {  
  7.         //每次我们创建一个Quackable,就用一个新的装饰者包装它  
  8.         Quackable mallardDuck = new QuackCounter(new MallardDuck());  
  9.         Quackable redheadDuck = new QuackCounter(new RedheadDuck());  
  10.         Quackable duckCall = new QuackCounter(new DuckDuck());  
  11.         Quackable rubberDuck = new QuackCounter(new RubberDuck());  
  12.         //鹅不是呱呱叫,所以不用包装  
  13.         Quackable gooseDuck = new DooseAdapter(new Goose());  
  14.         //模拟每种鸭子  
  15.         simulate(mallardDuck);  
  16.         simulate(redheadDuck);  
  17.         simulate(duckCall);  
  18.         simulate(rubberDuck);  
  19.         simulate(gooseDuck);  
  20.         //就是在这里,我们收集呱呱叫行为  
  21.         System.out.println("The ducks quacked "+QuackCounter.getQuacks()+" times");  
  22.     }  
  23.     void simulate(Quackable duck) {  
  24.         duck.quack();  
  25.     }  
  26. }

我们为什么不将创建鸭子的程序集中在一个地方呢?换句话说,让我们将创建和装饰的部分包装起来,这看起来像是什么模式?
10.我们需要一些质量控制来确保鸭子一定是被包装起来的,需要建造一个工厂创建装饰过的鸭子.此工厂应该生产各种不同类型的鸭子的产品家族,使用我们使用抽象工厂模式.

Java代码 
  1. //定义一个抽象工厂,它的子类会创建不同的家族  
  2. public abstract class AbstractDuckFactory {  
  3.     //每个方法创建一种鸭子  
  4.     public abstract Quackable createMallardDuck();  
  5.     public abstract Quackable createRedheadDuck();  
  6.     public abstract Quackable createDuckCall();  
  7.     public abstract Quackable createRubberDuck();  
  8. }  
  9. //创建一个工厂,此工厂创建没有装饰者的鸭子  
  10. public class DuckFactory extends AbstractDuckFactory {  
  11.     //每个方法创建一个产品,一种特定种类的Quackable.  
  12.     //模拟器并不知道实际的产品是什么,只知道它实现了Quackable接口  
  13.     public Quackable createMallardDuck() {  
  14.         return new MallardDuck();  
  15.     }  
  16.     public Quackable createRedheadDuck() {  
  17.         return new RedheadDuck();  
  18.     }  
  19.     public Quackable createDuckCall() {  
  20.         return new DuckCall();  
  21.     }  
  22.     public Quackable createRubberDuck() {  
  23.         return new RubberDuck();  
  24.     }  
  25. }  
  26. //现在,要创建我们真正需要的工厂  
  27. public class CountingDuckFactory extends AbstractDuckFactory {  
  28.     //每个方法都会先用叫声计数装饰者将Quackable包装进来.模拟器并不知道有何不同,  
  29.     //只知道它实现了Quackable接口,所有的叫声都会被计算进去  
  30.     public Quackable createMallardDuck() {  
  31.         return new QuackCounter(new MallardDuck());  
  32.     }  
  33.     public Quackable createRedheadDuck() {  
  34.         return new QuackCounter(new RedheadDuck());  
  35.     }  
  36.     public Quackable createDuckCall() {  
  37.         return new QuackCounter(new DuckCall());  
  38.     }  
  39.     public Quackable createRubberDuck() {  
  40.         return new QuackCounter(new RubberDuck());  
  41.     }  
  42. }  

 

11.我们修改模拟器来使用这个工厂

Java代码 
  1. public class DuckSimulator {  
  2.     public static void main(String[] args) {  
  3.         DuckSimulator simulator = new DuckSimulator();  
  4.         //首先我们创建工厂,准备把它传入simulate方法  
  5.         AbstractDuckFactory duckFactory = new CountingDuckFactory();  
  6.         simulator.simulate(duckFactory);  
  7.     }  
  8.     //simulate需要一个AbstractDuckFactory参数,利用它来创建鸭子,而不是直接实例化鸭子  
  9.     void simulate(AbstractDuckFactory duckFactory) {  
  10.         Quackable redheadDuck = duckFactory.createMallardDuck();  
  11.         Quackable redheadDuck = duckFactory.createRedheadDuck();  
  12.         Quackable duckCall = duckFactory.createDuckCall();  
  13.         Quackable rubberDuck = duckFactory.createRubberDuck();  
  14.         Quackable gooseDuck = new GooseAdapter(new Goose());  
  15.         //这里不改动  
  16.         simulate(mallardDuck);  
  17.         simulate(redheadDuck);  
  18.         simulate(duckCall);  
  19.         simulate(rubberDuck);  
  20.         simulate(gooseDuck);  
  21.         System.out.println("The ducks quacked "+QuackCounter.getQuacks()+" times");  
  22.     }  
  23.     void simulate(Quackable duck) {  
  24.         duck.quack();  
  25.     }  
  26. }  

 

测试...
看到这里,你不觉得分别管理这些不同的鸭子变得有些困难吗?有什么办法帮我们作为一个整体来管理这些鸭子,甚至让我们管理几个想持续追踪的鸭子家族吗?下一次命令就能够让整个集合的鸭子听命行事,什么模式可以帮我们?
还记得吗,组合模式允许我们像对待单个对象一样对待对象集合.
12.让我们创建一群鸭子,实际上是一群Quackable

Java代码 
  1. //别忘了,组合需要和叶节点元素一样实现相同的接口,这里的叶节点就是'Quackable'  
  2. public class Flock implements Quackable {  
  3.     //在每个flock内,我们使用ArrayList记录属于这个flock的Quackable对象  
  4.     ArrayList quackers = new ArrayList();  
  5.     //用add方法新增Quackable对象到flock  
  6.     public void add(Quackable quacker) {  
  7.         quackers.add(quacker);  
  8.     }  
  9.     //注意到了吗,这里我们其实还使用了迭代器模式  
  10.     public void quack() {  
  11.         Iterator iterator = quackers.iterator();  
  12.         while (iterator.hasNext()) {  
  13.             Quackable quacker = (Quackable)iterator.next();  
  14.             //毕竟flock也是Quackable,所以也要具备quack方法,此方法会对整群产生作用  
  15.             //我们遍历ArrayList调用每个元素上的quack方法  
  16.             quacker.quack();  
  17.         }  
  18.     }  
  19. }  

 

13.我们的组合已经准备好了,我们需要一些让鸭子能进入组合的代码,再来修改模拟器

Java代码 
  1. public class DuckSimulator {  
  2.     public static void main(String[] args) {  
  3.         DuckSimulator simulator = new DuckSimulator();  
  4.         //首先我们创建工厂,准备把它传入simulate方法  
  5.         AbstractDuckFactory duckFactory = new CountingDuckFactory();  
  6.         simulator.simulate(duckFactory);  
  7.     }  
  8.     //simulate需要一个AbstractDuckFactory参数,利用它来创建鸭子,而不是直接实例化鸭子  
  9.     void simulate(AbstractDuckFactory duckFactory) {  
  10.         Quackable redheadDuck = duckFactory.createRedheadDuck();  
  11.         Quackable duckCall = duckFactory.createDuckCall();  
  12.         Quackable rubberDuck = duckFactory.createRubberDuck();  
  13.         Quackable gooseDuck = new GooseAdapter(new Goose());  
  14.           
  15.         //我们先创建一个flock,然后把许多Quackable塞给它,这个flock是主群  
  16.         Flock flockOfDucks = new Flock();  
  17.         flockOfDucks.add(redheadDuck);  
  18.         flockOfDucks.add(duckCall);  
  19.         flockOfDucks.add(rubberDuck);  
  20.         flockOfDucks.add(gooseDuck);  
  21.         //然后创建一个新的绿头鸭群  
  22.         Flock flockOfMallards = new Flock();  
  23.         //创建绿头小家族  
  24.         Quackable mallardOne = duckFactory.createMallardDuck();  
  25.         Quackable mallardTwo = duckFactory.createMallardDuck();  
  26.         Quackable mallardThree = duckFactory.createMallardDuck();  
  27.         Quackable mallardFour = duckFactory.createMallardDuck();  
  28.         //将刚创建的绿头小家族加入绿头鸭群  
  29.         flockOfMallards.add(mallardOne);  
  30.         flockOfMallards.add(mallardTwo);  
  31.         flockOfMallards.add(mallardThree);  
  32.         flockOfMallards.add(mallardFour);  
  33.         //将绿头鸭群加入主群  
  34.         flockOfDucks.add(flockOfMallards);  
  35.         //测试整群  
  36.         simulate(flockOfDucks);  
  37.         //只测试绿头鸭群  
  38.         simulate(flockOfMallards);  
  39.         //最后显示呱呱叫次数  
  40.         System.out.println("/nThe ducks quacked " +   
  41.                            QuackCounter.getQuacks() +   
  42.                            " times");  
  43.     }  
  44.     //这里不需要修改,因为flock也是Quackable  
  45.     void simulate(Quackable duck) {  
  46.         duck.quack();  
  47.     }  
  48. }  

 

14.既然我们有办法管理整群鸭子,我们也应该有办法来追踪个别的鸭子及其实时呱呱叫行为,我想到起了有个模式可以观察对象的行为:观察者模式
首先我们需要一个Observable接口,所谓Observable就是被观察的对象,Observable需要注册和通知观察者的方法.

Java代码 
  1. //如何想被观察的Quackable都必须实现QuackObservable接口  
  2. public interface QuackObservable {  
  3.     //注册观察者,任何实现了Observer接口的对象都可以监听呱呱叫  
  4.     public void registerObserver(Observer observer);  
  5.     //通知观察者  
  6.     public void notifyObservers();  
  7. }  
  8. //现在我们需要确定所有的Quackable都实现此接口,所以干脆我们让Quackable扩展此接口  
  9. public interface Quackable extends QuackObservable {  
  10.     public void quack();  
  11. }  

15.现在我们必须确定所有实现Quackable的具体类都能够扮演QuackObservable的角色.每一个类中都需要实现注册和通知,使用另外的做法:我们要在另一个Observalbe的类中封装注册和通知,然后将它和QuackObservable组合在一起,这样,我们只需要一份代码即可,QuackObservable所有的调用都委托给Observable辅助类

Java代码 
  1. //Observable必须实现QuackObservable,因为他们具有一组相同的方法,  
  2. //QuackObservable会将这些方法的调用转给Observable的方法  
  3. public class Observable implements QuackObservable {  
  4.     ArrayList observers = new ArrayList();  
  5.     QuackObservable duck;  
  6.     //在次构造器中我们传进了QuackObservable.看看notifyObservers方法,  
  7.     //你会发现当通知发生时,观察者把次对象传进去.好让观察者知道是哪个对象在呱呱叫  
  8.     public Observable(QuackObservable duck) {  
  9.         this.duck = duck;  
  10.     }  
  11.     //注册  
  12.     public void registerObserver(Observer observer) {  
  13.         observers.add(observer);  
  14.     }  
  15.     //通知  
  16.     public void notifyObservers() {  
  17.         Iterator iterator = observers.iterator();  
  18.         while (iterator.hasNext()) {  
  19.             Observer observer = (Observer)iterator.next();  
  20.             observer.update(duck);  
  21.         }  
  22.     }  
  23.     public Iterator getObservers() {  
  24.         return observers.iterator();  
  25.     }  
  26. }  

 

16.整合Observable辅助类和Quackable类

Java代码 
  1. public class MallardDuck implements Quackable {  
  2.     //每个Quackable都有一个Observable实例变量,在构造器中传入一个MallardDuck对象的引用  
  3.     Observable observable;  
  4.     public MallardDuck() {  
  5.         observable = new Observable(this);  
  6.     }  
  7.     public void quack() {  
  8.         System.out.println("Quack");  
  9.         //当呱呱叫时,需要让观察者知道  
  10.         notifyObservers();  
  11.     }  
  12.     //这是两个QuackObservable方法,注意我们只是委托给辅助类进行  
  13.     public void registerObserver(Observer observer) {  
  14.         observable.registerObserver(observer);  
  15.     }  
  16.     public void notifyObservers() {  
  17.         observable.notifyObservers();  
  18.     }  
  19. }  

 

17.我们已经实现了Observable所需要的一切,现在需要一些观察者Observer

Java代码 
  1. //Observer只有一个update方法,他需要传入一个正在呱呱叫的对象QuackObservable  
  2. public interface Observer {  
  3.     public void update(QuackObservable duck);  
  4. }  
  5. //我们需要实现Observer,否则无法以QuackObservable注册  
  6. public class Quackologist implements Observer {  
  7.     //打印出正在呱呱叫的Quackable对象  
  8.     public void update(QuackObservable duck) {  
  9.         System.out.println("Quackologist: " + duck + " just quacked.");  
  10.     }  
  11. }  

 

18.我们准备开始观察了,让我们更新模拟器,了解这一切是如何工作的

Java代码 
  1. public class DuckSimulator {  
  2.     public static void main(String[] args) {  
  3.         DuckSimulator simulator = new DuckSimulator();  
  4.         AbstractDuckFactory duckFactory = new CountingDuckFactory();  
  5.         simulator.simulate(duckFactory);  
  6.     }  
  7.     void simulate(AbstractDuckFactory duckFactory) {  
  8.         //other 创建鸭子工厂和鸭子  
  9.         //other 创建群  
  10.   
  11.         //我们在这里所需要做的就是创建一个Quackologist,把它注册成为一个群的观察者  
  12.         Quackologist quackologist = new Quackologist();  
  13.         flockOfDucks.registerObserver(quackologist);  
  14.           
  15.         simulate(flockOfDucks);  
  16.   
  17.         System.out.println("/nThe ducks quacked " +   
  18.                            QuackCounter.getQuacks() +   
  19.                            " times");  
  20.     }  
  21.     void simulate(Quackable duck) {  
  22.         duck.quack();  
  23.     }  
  24. }  

原创粉丝点击