复合模式

在一个解决方案中结合两个或多个模式,以解决一般或重复发生的问题。 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

Head First 设计模式 —— 14. 复合 (Compound) 模式的更多相关文章

  1. java设计模式-----14、桥接模式

    Bridge 模式又叫做桥接模式,是构造型的设计模式之一.Bridge模式基于类的最小设计原则,通过使用封装,聚合以及继承等行为来让不同的类承担不同的责任.它的主要特点是把抽象(abstraction ...

  2. Java设计模式14:建造者模式

    什么是建造者模式 发现很多框架的源码使用了建造者模式,看了一下觉得挺实用的,就写篇文章学习一下,顺便分享给大家. 建造者模式是什么呢?用一句话概括就是建造者模式的目的是为了分离对象的属性与创建过程,是 ...

  3. 设计模式(14)--Command(命令模式)--行为型

    作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.模式定义:   命令模式属于对象的行为模式.命令模式又称为行动(Action)模式或交易(Transactio ...

  4. Java描述设计模式(14):解释器模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.解释器模式 1.基础概念 解释器模式是对象的行为模式.给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器.客户端 ...

  5. 设计模式学习--复合模式(Compound Pattern)

    设计模式学习--复合模式(Compound Pattern) 概述 ——————————————————————————————————————————————————— 2013年8月4日<H ...

  6. 设计模式之第14章-命令模式(Java实现)

    设计模式之第14章-命令模式(Java实现) “小明,滚出去.”“小明,这个问题怎么做?”(可怜的小明无奈躺枪.小明:老师,我和你有什么仇什么怨,我和你有什么仇什么怨啊到底...老师:小明,滚出去.习 ...

  7. 设计模式-14 MVC模式

    一 MVC设计模式 MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式,它是一个存在于服务器 表达层的模型,它将应用分开,改变应用之间的高度耦合 MVC设计模式将 ...

  8. C#设计模式(14)——模板方法模式(Template Method)

    一.引言 提到模板,大家肯定不免想到生活中的“简历模板”.“论文模板”.“Word中模版文件”等,在现实生活中,模板的概念就是——有一个规定的格式,然后每个人都可以根据自己的需求或情况去更新它,例如简 ...

  9. 第14章 命令模式(Command Pattern)

    原文 第14章 命令模式(Command Pattern) 命令模式(Command Pattern) 概述   在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”.但在某些场合,比如 ...

随机推荐

  1. 微信小程序图片上传和裁剪

    本篇博客用于解决微信小程序图片裁剪问题 图片裁剪常用于头像选择和图片合成等. 图片裁剪解决方案: 目前网络上知名的微信小程序图片裁剪插件是we-cropper(文末有链接) 操作步骤:下载好we-cr ...

  2. STL——容器(Map & multimap)的查找

    map.find(key);   //查找键key是否存在,若存在,返回该键的元素的迭代器:若不存在,返回map.end(); map.count(key);   //返回容器中键值为key的对组个数 ...

  3. Oracle 要慌了!华为终于开源了自家的 Huawei JDK——毕昇 JDK!

    没错,自阿里.腾讯之后,华为也终于开源了自家的 JDK--毕昇 JDK! 免费!免费!免费!!! Oracle 要慌了? 毕昇 JDK 毕昇 JDK 是华为内部 OpenJDK 定制版 Huawei ...

  4. Linux下设置定时任务删除归档日志

    1.编辑删除归档日志的脚本----/home/oracle/clearlog.sh #! /bin/bash source ~/profile_orcl#记录归档删除的日志exec >> ...

  5. SpringBoot魔法堂:应用热部署实践与原理浅析

    前言 后端开发的同学想必每天都在重复经历着修改代码.执行代码编译,等待--重启Tomcat服务,等待--最后测试发现还是有bug,然后上述流程再来一遍(我听不见)

  6. Eureka系列(六) TimedSupervisorTask类解析

      为什么要单独讲解TimedSupervisorTask这个类呢?因为这个类在我们DiscoveryClient类的initScheduledTasks方法进行定时任务初始化时被使用得比较多,所以我 ...

  7. 配置redis服务器允许远程连接

    说明 默认情况下,redis只允许本机访问.如果需要外部访问,需要修改下配置文件. 配置修改 redis.windows.conf 将bind 127.0.0.1 注释 将protected-mode ...

  8. 《单元测试之道》Java版学习日志

    在软件工程这门课程中,首先谈单元测试的概念,单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的.很明确的功能是否正确.通常而言,一个单元测试是用于判断某个特定条件或某个特定函数的行为.我们 ...

  9. Guns自动化生成代码使用

    一.Guns简介 Guns基于Spring Boot2,致力于做更简洁的后台管理系统.包含系统管理,代码生成,多数据库适配,SSO单点登录,工作流,短信,邮件发送,OAuth2登录,任务调度,持续集成 ...

  10. java.util.Collections

    p.p1 { margin: 0; font: 11px Monaco } span.s1 { text-decoration: underline } span.s2 { color: rgba(1 ...