乐趣区

关于设计模式:Head-First-设计模式-14-复合-Compound-模式

复合模式

在一个解决方案中联合两个或多个模式,以解决个别或反复产生的问题。P500

思考题

public interface Quackable {public void quack();
}

public class MallardDuck implements Quackable {public void quack() {System.out.println("Quack");
    }
}

public class Goose {public void honk() {System.out.println("Honk");
    }
}

假如咱们想要在所有应用鸭子的中央应用鹅,毕竟鹅会叫、会飞、会游,和鸭子差不多。什么模式能够让咱们轻易地将鸭子和鹅掺杂在一起呢?P503

  • 适配器模式。题目须要轻易地将一种行为转换为另一种行为,且不要扭转原有的类,所以须要应用适配器转换。

思考题

咱们要如何在不变动鸭子类的状况下,计算所有鸭子呱呱叫的总次数呢?有没有什么模式能够帮上忙?P505

  • 装璜器模式。题目要求减少新的行为,且不扭转原有类,所以能够应用装璜器。
  • 代理模式。代理模式会管制拜访,而鹅经适配器后转换的行为不应该被统计,所以能够通过代理模式进行管制。

思考题

你可能为鹅写一个形象工厂吗?创立”内鹅外鸭“的对象时,你怎么解决?P511

  • 新建一个工厂,专门创立被适配器转换成鸭子的鹅

    public abstract class AbstractGooseDuckFactory {public abstract Quackable createGooseDuck();
    }
    
    public class GooseDuckFactory extends AbstractGooseDuckFactory {public Quackable createGooseDuck() {return new GooseAdapter(new Goose());
        }
    }

思考题

咱们须要将鸭子视为一个汇合,甚至是子集合(subcollection),如果咱们下一次命令,就能让整个汇合的鸭子听命行事,那就太好了。什么模式能够帮咱们?P512

  • 迭代器模式。因为咱们须要将鸭子视为一个汇合,能够遍历执行同一操作,所以能够应用迭代器模式不便遍历。
  • 组合模式。因为鸭子汇合可能会含有子集合和鸭子,并也须要反对上述行为,所以能够应用组合模式将鸭子和鸭子汇合对立起来。

思考题

你可能有方法继续追踪个别鸭子的实时呱呱叫吗?P516

  • 观察者模式。题目意思就是鸭子在呱呱叫时告诉察看人员,所以鸭子是可被察看的,应该继承 Observable 类,而察看人员应该实现 Observer 接口,察看人员在个别鸭子上注册以便实时接管鸭子的呱呱叫行为。

    • 依照以上设计会批改所有的鸭子类,所以就想到能够再加一个装璜器继承 Observable 类,并实现 Quackable 接口,这样改变量最小,不会扭转原有鸭子类,也能够将鸭子和可被察看解耦。但设想很美妙,一去实现就会遇到很多问题:用户代码必须与该装璜器耦合,须要特判该装璜器以执行注册观察者和告诉观察者的办法;该装璜器只能最初包装,如果被其余装璜器包装就无奈再调用相应办法;不便于将相应的办法扩大到组合模式中的汇合上。所以还是须要接口上的批改,扭转所有鸭子的行为。
    • 书上设计是让 Quackable 接口继承 QuackObservable 接口以便所有能叫的鸭子都能被察看;批改所有鸭子类,并将 Observable 类组合进鸭子类中,将注册观察者和告诉观察者的办法外部委托到 Observable 相应的办法中;同时也要批改相应的装璜器。

思考题

咱们还没有扭转一个 Quackable 的实现,即 QuackCounter 装璜器。它也必须成为 Observable。你何不试着写出它的代码呢?P518

public class QuackCounter implements Quackable {
    Quackable duck;
    static int numberOfQuacks;
    
    public QuackCounter(Quackable duck) {this.duck = duck;}
    
    public void quack() {duck.quack();
        ++numberOfQuacks;
    }
    
    public static int getQuacks() {return numberOfQuacks;}
    
    public void registerObserver(Observer observer) {duck.registerObserver(observer);
    }
    
    public void notifyObservers() {duck.notifyObservers();
    }
}

思考题

万一呱呱叫学家想察看整个群,又该怎么办呢?当察看某个组合时,就等于察看组合内的每个货色。P520

public class Flock implements Quackable {ArrayList ducks = new ArrayList();
    
    public void add(Quackable duck) {ducks.add(duck);
    }
    
    public void quack() {Iterator iterator = ducks.iterator();
        while(iterator.hasNext()) {Quackable duck = (Quackable) iterator.next();
            duck.quack();}
    }
    
    public void registerObserver(Observer observer) {Iterator iterator = ducks.iterator();
        while(iterator.hasNext()) {Quackable duck = (Quackable) iterator.next();
            duck.registerObserver(observer);
        }
    }
    
    public void notifyObservers() {// 鸭群注册观察者都委托到孩子上了,所以告诉观察者的事件并不需要鸭群做任何事}
}

所思所想

  • 能够通过让原有接口继承新接口的形式,再减少接口办法和相应的性能的同时,缩小用户批改代码。例如:JDK7 中就让原有的 Closable 接口继承 AutoClosable 接口,使得原有的用户代码都不用批改就能在 JDK7 中应用带资源的 try 语句能主动敞开资源的新个性。(第一次看见 AutoClosable 接口时,间接从语义上就认为 AutoClosable 继承了 Closable,没想到正相反)

本文首发于公众号:满赋诸机(点击查看原文)开源在 GitHub:reading-notes/head-first-design-patterns

退出移动版