携手合作
模式通常被一起使用,并被组合在同一个设计解决方案中
复合模式在一个解决方案中结合两个或多个模式,以解决一般或重复发生的问题
案例分析
策略模式中提到了鸭子模型,而这次鸭子将在同一个解决方案中展示模式是如何共存甚至携手合作的。
适配器模式应用
重建鸭子模拟器,通过使用一堆模式来赋予它一些有趣的能力。
1. 创建一个Quackable接口
public interface Quackable{
//Quackable只需做好一件事:quack(呱呱叫)
public void quack();
}
2. 某些鸭子实现了Quackable接口
//绿头鸭
public class MallardDuck implements Quackable{
public void quack(){
System.out.println("Quack");
}
}
//红头鸭
public class RedheadDuck implements Quackable{
public void quack(){
System.out.println("Quack");
}
}
//鸭鸣启会呱呱叫,但听起来并不十分像真的鸭叫声
public class DuckCall implements Quackable{
public void quack(){
System.out.println("Kwak");
}
}
//当橡皮鸭呱呱叫时,其实声音时吱吱叫
public class RubberDuck implements Quackable{
public void quack(){
System.out.println("Kwak");
}
}
3. 我们有了鸭子,还需要一个模拟器
让我们来制造一个会产生一些鸭子,还要确认鸭子会呱呱叫的模拟器
public class DuckSimulator{
public static void main(String[] args){
DuckSimulator duckSimulator = new DuckSimulator();
duckSimulator.simulate();
}
void simulate(){
//产生鸭子实例
Quackable mallardDuck = new MallardDuck();
Quackable redheadDuck = new RedheadDuck();
Quackable duckCall = new DuckCall();
Quackable rubberDuck = new RubberDuck();
System.out.println("Duck Simulator");
//模拟鸭子
simulate(mallardDuck);
simulate(redheadDuck);
simulate(duckCall);
simulate(rubberDuck);
}
//传入呱呱叫对象,多态调用
void simulate(Quackable duck){
duck.quack();
}
}
4. 当鸭子出现在这里时,鹅也应该在附近
只要有水塘的地方,就大概会有鸭子和鹅。我们为这个模拟器设计了一个Goose(鹅)类
public class Goose{
//鹅不是呱呱叫,是咯咯叫
public void honk(){
System.out.println("Honk");
}
}
思考:假设我们想要在所有使用鸭子的地方使用鹅,毕竟鹅会叫、会飞、会游,和鸭子差不多。为什么我们不能在模拟器中使用鹅呢?什么模式可以让我们轻易的在模拟器中使用鹅呢?
5. 我们需要适配器
//适配器会实现目标接口,也就是Quackable
public class GooseAdapter implements Quackable{
Goose goose;
//构造器需要传入要适配的鹅对象
public GooseAdapter(Goose goose){
this.goose = goose;
}
public void quack(){
//当调用quack()时,会被委托到鹅的honk()方法
goose.honk();
}
}
6. 现在模拟器中也可以使用鹅了
我们需要做的就是创建Goose对象,将它包装进适配器,以便实现Quackable。
public class DuckSimulator{
public static void main(String[] args){
DuckSimulator duckSimulator = new DuckSimulator();
duckSimulator.simulate();
}
void simulate(){
//产生鸭子实例
Quackable mallardDuck = new MallardDuck();
Quackable redheadDuck = new RedheadDuck();
Quackable duckCall = new DuckCall();
Quackable rubberDuck = new RubberDuck();
//通过把Goose包装进GooseAdapter,我们就可以让鹅像鸭子一样
Quackable gooseDuck = new GooseAdapter(new Goose());
System.out.println("Duck Simulator");
//模拟鸭子
simulate(mallardDuck);
simulate(redheadDuck);
simulate(duckCall);
simulate(rubberDuck);
//把它当做其他鸭子的Quackable对象
simulate(gooseDuck);
}
//传入呱呱叫对象,多态调用
void simulate(Quackable duck){
duck.quack();
}
}
适配器模式类图
装饰者模式应用
思考:在一群鸭子中,会有多少呱呱叫声?我们要如何在不变化鸭子类的情况下,计算呱呱叫的次数呢?有没有什么模式可以帮上忙?
7. 使用装饰者模式
怎样才能办到呢?我们创建一个装饰者,通过把鸭子包装进装饰者对象,给鸭子一些新行为(计算次数的行为)。我们不必修改鸭子的代码。
//QuackCounter是一个装饰者
//像适配器一样,我们需要实现目标接口
public class QuackCounter implements Quackable{
//我们用一个实例变量来记录被装饰的呱呱叫对象
Quackable duck;
//我们用静态变量跟踪所有呱呱叫次数
static int numberOfQuacks;
public QuackCounter(Quackable duck){
this.duck = duck;
}
public void quack(){
//当quack()被调用时,我们就把调用委托给正在装饰的Quackable对象
duck.quack();
//然后把叫声的次数+1
numberOfQuacks++;
}
//给装饰者加入一个静态方法,以便返回在所有Quackable中发生的叫声次数
public static int getQuacks(){
return numberOfQuacks;
}
}
8. 更新模拟器,创建被装饰的鸭子
现在,我们必须包装在QuackCounter装饰者中被实例化的每个Quackable对象,如果不这么做,鸭子就会到处乱跑而使得我们无法统计其叫声次数
public class DuckSimulator{
public static void main(String[] args){
DuckSimulator duckSimulator = new DuckSimulator();
duckSimulator.simulate();
}
void simulate(){
//每次我们创建一个Quackable,就用一个新的装饰者包装它
Quackable mallardDuck = new QuackCounter(new MallardDuck());
Quackable redheadDuck = new QuackCounter(new RedheadDuck());
Quackable duckCall = new QuackCounter(new DuckCall());
Quackable rubberDuck = new QuackCounter(new RubberDuck());
//通过把Goose包装进GooseAdapter,我们就可以让鹅像鸭子一样
Quackable gooseDuck = new GooseAdapter(new Goose());
System.out.println("Duck Simulator");
//模拟鸭子
simulate(mallardDuck);
simulate(redheadDuck);
simulate(duckCall);
simulate(rubberDuck);
//把它当做其他鸭子的Quackable对象
simulate(gooseDuck);
System.out.println("The ducks quacked "+ QuackCounter.getQuacks() + " times");
}
//没有任何的变动,被装饰的对象还是Quackable
void simulate(Quackable duck){
duck.quack();
}
}
装饰者模式类图
工厂模式
思考:使用装饰对象才能获得被装饰过的行为,有包装才有效果,没有包装就没有效果。为什么我们不将创建鸭子的程序集中在一个地方呢?换句话说,让我们将创建和装饰的部分包装起来吧。
9. 使用工厂产生鸭子
我们需要一些质量控制来确保鸭子一定是被包装起来的。我们要建造一个工厂,创建装饰过的鸭子。此工厂应该生产各种不同类型的鸭子的产品家族,所以我们要用抽象工厂模式。
//我们定义一个抽象工厂,它的子类们会创建不同的家族
public abstract class AbstractDuckFactory{
//每个方法创建一种鸭子
public abstract Quackable createMallardDuck();
public abstract Quackable createRedheadDuck();
public abstract Quackable createDuckCall();
public abstract Quackable createRubberDuck();
}
让我们创建一个工厂,此工厂创建没有装饰者的鸭子:
//DuckFactory扩展抽象工厂
public class DuckFactory extends AbstractDuckFactory(){
public Quackable createMallardDuck(){
return new MallardDuck();
}
public Quackable createRedheadDuck(){
return new RedheadDuck();
}
public Quackable createDuckCall(){
return new QuackCounter(new DuckCall());
}
public Quackable createRubberDuck(){
return new RubberDuck();
}
}
创建我们真正需要的工厂CountingDuckFactory
//CountingDuckFactory也扩展自抽象工厂
public class CountingDuckFactory extends AbstractDuckFactory(){
//每个方法都会先用叫声计数装饰者将Quackable包装起来
//模拟器并不知道有何不同,但是所有的叫声都会被计算进去
public Quackable createMallardDuck(){
return new QuackCounter(new MallardDuck());
}
public Quackable createRedheadDuck(){
return new QuackCounter(new RedheadDuck());
}
public Quackable createDuckCall(){
return new DuckCall();
}
public Quackable createRubberDuck(){
return new QuackCounter(new RubberDuck());
}
}
10. 设置模拟器来使用这个工厂
public class DuckSimulator{
public static void main(String[] args){
DuckSimulator duckSimulator = new DuckSimulator();
AbstractDuckFactory duckFactory = new CountingDuckFactory();
duckSimulator.simulate(duckFactory);
}
void simulate(AbstractDuckFactory duckFactory){
//利用工厂实例化鸭子,而不是直接实例化鸭子
Quackable mallardDuck = duckFactory.createMallardDuck();
Quackable redheadDuck = duckFactory.createRedheadDuck();
Quackable duckCall = duckFactory.createDuckCall();
Quackable rubberDuck = duckFactory.createRubberDuck();
Quackable gooseDuck = new GooseAdapter(new Goose());
System.out.println("Duck Simulator");
simulate(mallardDuck);
simulate(redheadDuck);
simulate(duckCall);
simulate(rubberDuck);
simulate(gooseDuck);
System.out.println("The ducks quacked "+ QuackCounter.getQuacks() + " times");
}
//其余代码完全没有变动
void simulate(Quackable duck){
duck.quack();
}
}
工厂模式类图
迭代器模式+组合模式
思考:为什么需要管理个别的鸭子?我们需要将鸭子视为一个集合,甚至是子集合(subcollection),为了满足巡逻员想管理鸭子家族的要求。如果我们下一次命令,就能让整个集合的鸭子听命行事,那就太好了,什么模式可以帮助我们?
11. 让我们创建一群鸭子(组合模式+迭代器模式)
组合模式允许我们像对待单个对象一样对待对象集合。还有什么能比组合模式创建一群Quackable更好呢?
//组合需要和叶节点元素一样实现相同的接口。这里的“叶节点”就是Quackable
public class Flock implements Quackable{
ArrayList quackers = new ArrayList();
public void add(Quackable quacker){
quackers.add(quacker);
}
public void quack(){
Iterator iterator = quackers.iterator();
while(iterator.hasNext()){
Quackable quacker = (Quackable)iterator.next();
quacker.quack();
}
}
}
12. 修改对应的模拟器
public class DuckSimulator{
public static void main(String[] args){
DuckSimulator duckSimulator = new DuckSimulator();
AbstractDuckFactory duckFactory = new CountingDuckFactory();
duckSimulator.simulate(duckFactory);
}
void simulate(AbstractDuckFactory duckFactory){
Quackable redheadDuck = duckFactory.createRedheadDuck();
Quackable duckCall = duckFactory.createDuckCall();
Quackable rubberDuck = duckFactory.createRubberDuck();
Quackable gooseDuck = new GooseAdapter(new Goose());
Flock flockOfDucks = new Flock();
flockOfDucks.add(redheadDuck);
flockOfDucks.add(duckCall);
flockOfDucks.add(rubberDuck);
flockOfDucks.add(gooseDuck);
//绿头鸭小家族
Quackable mallardDuckOne = duckFactory.createMallardDuck();
Quackable mallardDuckTwo = duckFactory.createMallardDuck();
Quackable mallardDuckThree = duckFactory.createMallardDuck();
Quackable mallardDuckFour = duckFactory.createMallardDuck();
Flock flockOfMallards = new Flock();
flockOfMallards.add(mallardDuckOne);
flockOfMallards.add(mallardDuckTwo);
flockOfMallards.add(mallardDuckThree);
flockOfMallards.add(mallardDuckFour);
//绿头鸭家族群加入主群
flockOfDucks.add(flockOfMallards);
//测试一整群
simulate(flockOfDucks);
//只测试绿头鸭家族
simulate(flockOfMallards);
}
void simulate(Quackable duck){
duck.quack();
}
}
迭代器模式+组合模式类图
观察者模式
思考:我们需要持续追踪个别鸭子的实时呱呱叫,使用哪个设计模式?
有一个模式可以观察对象的行为:观察者模式。
13. 设计一个Observable接口
所谓的Observable就是被观察的对象。Observable需要注册和通知观察者的方法。为了实现简单,暂时忽略删除观察者的方法:
//任何被观察的对象都实现该接口
public interface QuackObservable{
//注册观察者,任何实现了Observer接口的对象都可以监听呱呱叫
public void registerObserver(Observer observer);
//通知观察者
public void notifyObservers();
}
现在我们需要确定所有的Quackable都实现此接口
//Quackable接口来扩展QuackObservable
public interface Quackable extends QuackObservable{
public void quack();
}
我们需要确定所有实现Quackable的具体类都能够扮演QuackObservable的角色。
实现:我们在另一个被称为Observable的类中封装注册和通知的代码,然后将它和QuackObservable组合在一起,这样,我们只需要一份代码即可,QuackObservable所有的调用都委托给Observable辅助类。
public class Observable implements QuackObservable{
ArrayList observers = new ArrayList();
QuackObservable duck;
public Observable(QuackObservable duck){
this.duck = duck;
}
public void registerObserver(Observer observer){
observers.add(observer);
}
public void notifyObservers(){
Iterator iterator = observers.iterator();
while(iterator.hasNext()){
Observer observer = (Observer)iterator.next();
observer.update(duck);
}
}
}
14. 整合Observable辅助类和Quackable类
确定Quackable类和Observable组合在一起,并且他们知道怎样来委托工作。然后,他们就准备好成为Observable了。下面是MallardDuck的实现,其他鸭子的实现也类似。
public class MallardDuck implements Quackable{
//每个Quackable都有一个Observable实例
Observable observable;
public MallardDuck(){
observable = new Observable(this);
}
public void quack(){
System.out.println("Quack");
//当我们呱呱叫时,需要让观察者知道
notifyObservers();
}
public void registerObserver(Observer observer){
//委托给辅助类
observable.registerObserver(observer);
}
public void notifyObservers(){
//委托给辅助类
observable.notifyObservers();
}
}
15. 完成Observer端
我们已经实现了Observable所需要的一切,现在我们需要一些观察者(Observer):
public interface Observer{
public void update(QuackObservable duck);
}
public class Quackologist implements Observer{
public void update(QuackObservable duck){
//打印出正在呱呱叫的Quackable对象
System.out.println("Quackologist:"+duck+" just quacked.");
}
}
16. 更新模拟器
public class DuckSimulator{
public static void main(String[] args){
DuckSimulator duckSimulator = new DuckSimulator();
AbstractDuckFactory duckFactory = new CountingDuckFactory();
duckSimulator.simulate(duckFactory);
}
void simulate(AbstractDuckFactory duckFactory){
//创建鸭子工厂和鸭子
//创建群
Quackologist quackologist = new Quackologist();
//quackologist注册为一个群的观察者
flockOfDucks.registerObsever(quackologist);
simulate(flockOfDucks);
}
void simulate(Quackable duck){
duck.quack();
}
}