Decorator模式即装饰器模式,就是对类进行装饰,下面通过代码说明。

代码演示

代码展示的内容

有一个类StringDisplay:表示一句话,比如hello world。

我们通过装饰器对这句话进行装饰——加上左右边框形成|hello world|,再加上上下边框线包围这句话,形成

+-------------+
|Hello, world.|
+-------------+

UML图

每个类的解释

Display类:代表一段话的显示。使用了模版方法模式,定义了获取列数,行数等抽象方法,规定了这些方法的使用方式。

StringDisplay类:代表一句话的显示。继承并实现了Display中的所有抽象方法。

Border类:代表对一句话的装饰。继承并委托了Display类。是个抽象的装饰类。

SideBorder类:代表对某一行的左右添加装饰字符。继承Border类(意味着同时继承了Display类),实现了他们的抽象方法。

FullBorder类:代表对某一行的上下左右添加装饰字符。和SideBorder类一样,继承并实现了Border类和Display类的抽象方法。

代码

public abstract class Display {
public abstract int getColumns(); // 获取横向字符数
public abstract int getRows(); // 获取纵向行数
public abstract String getRowText(int row); // 获取第row行的字符串
public void show() { // 全部显示
for (int i = 0; i < getRows(); i++) {
System.out.println(getRowText(i));
}
}
} public class StringDisplay extends Display {
private String string; // 要显示的字符串
public StringDisplay(String string) { // 通过参数传入要显示的字符串
this.string = string;
}
public int getColumns() { // 字符数
return string.getBytes().length;
}
public int getRows() { // 行数是1
return 1;
}
public String getRowText(int row) { // 仅当row为0时返回值
if (row == 0) {
return string;
} else {
return null;
}
}
} public abstract class Border extends Display {
protected Display display; // 表示被装饰物
protected Border(Display display) { // 在生成实例时通过参数指定被装饰物
this.display = display;
}
} public class SideBorder extends Border {
private char borderChar; // 表示装饰边框的字符
public SideBorder(Display display, char ch) { // 通过构造函数指定Display和装饰边框字符
super(display);
this.borderChar = ch;
}
public int getColumns() { // 字符数为字符串字符数加上两侧边框字符数
return 1 + display.getColumns() + 1;
}
public int getRows() { // 行数即被装饰物的行数
return display.getRows();
}
public String getRowText(int row) { // 指定的那一行的字符串为被装饰物的字符串加上两侧的边框的字符
return borderChar + display.getRowText(row) + borderChar;
}
} public class FullBorder extends Border {
public FullBorder(Display display) {
super(display);
}
public int getColumns() { // 字符数为被装饰物的字符数加上两侧边框字符数
return 1 + display.getColumns() + 1;
}
public int getRows() { // 行数为被装饰物的行数加上上下边框的行数
return 1 + display.getRows() + 1;
}
public String getRowText(int row) { // 指定的那一行的字符串
if (row == 0) { // 上边框
return "+" + makeLine('-', display.getColumns()) + "+";
} else if (row == display.getRows() + 1) { // 下边框
return "+" + makeLine('-', display.getColumns()) + "+";
} else { // 其他边框
return "|" + display.getRowText(row - 1) + "|";
}
}
private String makeLine(char ch, int count) { // 生成一个重复count次字符ch的字符串
StringBuffer buf = new StringBuffer();
for (int i = 0; i < count; i++) {
buf.append(ch);
}
return buf.toString();
}
} public class Main {
public static void main(String[] args) {
Display b1 = new StringDisplay("Hello, world.");
Display b2 = new SideBorder(b1, '#');
Display b3 = new FullBorder(b2);
b1.show();
System.out.println();
b2.show();
System.out.println();
b3.show();
System.out.println();
Display b4 =
new SideBorder(
new FullBorder(
new FullBorder(
new SideBorder(
new FullBorder(
new StringDisplay("hello world!!!")
),
'*'
)
)
),
'~'
);
b4.show();
}
} /*
结果
Hello, world. #Hello, world.# +---------------+
|#Hello, world.#|
+---------------+ ~+--------------------+~
~|+------------------+|~
~||*+--------------+*||~
~||*|hello world!!!|*||~
~||*+--------------+*||~
~|+------------------+|~
~+--------------------+~
*/

模式的角色和类图

角色

  • Component:被装饰的角色,只定义了API。在本例中,由Display类扮演此角色。
  • ConcreteComponent:实现了Component的API,是具体的被装饰的角色。本例中,由StringDisplay类扮演此角色。
  • Decorator:装饰者,具有和Component相同的API,内部保留了被装饰对象Component。本例中,由Border类扮演此角色。
  • ConcreteDecorator:具体的装饰者。本例中由SideBorder和FullBorder扮演此角色。

类图

思路拓展

接口(API)的透明性

Decorator继承了Component,装饰物和被装饰物具有一致性——他们有着相同的API接口。即便API接口被装饰了一遍,也不会被隐藏起来,其他类依然可以调用被装饰后的API接口。可以用这个特性实现递归装饰。

为什么使用继承和委托

使用继承,是为了获得一致性,如上所说。

为什么使用委托呢?

如果只有继承,由于Decorator继承的是抽象的被装饰类,意味着我们要再实现一遍被装饰者的API,一旦被装饰类的API的逻辑发生改变,被装饰者也要改一次。

那如果Decorator继承的是具体的被装饰类呢?这样可以实现类似于委托那样的实现,直接调用父类的方法就可以了。

这样做的坏处至少有一个:有多少个具体的被装饰类,就要写多少遍装饰类。麻烦死了,而且重复代码一堆,绝不是什么好事情。

所以还是用委托吧。

java.io包和装饰者模式

//读取文件
Reader reader = new FileReader("xxxx.txt");
//读取时将文件放入缓冲区
Reader reader = new BufferReader(
new FileReader("xxxx.txt")
);
//还要管理行号
Reader reader = new LineNumberReader(
new BufferReader(
new FileReader("xxxx.txt")
)
);
...

缺点

增加很多功能类似的很小的修饰类

《图解设计模式》读书笔记5-2 decorator模式的更多相关文章

  1. HeadFirst设计模式读书笔记(3)-装饰者模式(Decorator Pattern)

    装饰者模式:动态地将责任附件到对象上.若要扩展功能,装饰者提东了比继承更有弹性的替代方案. 装饰者和被装饰对象有相同的超类型 你可以用一个或者多个装饰者包装一个对象. 既然装饰者和被装饰对象有相同的超 ...

  2. HeadFirst设计模式读书笔记--目录

    HeadFirst设计模式读书笔记(1)-策略模式(Strategy Pattern) HeadFirst设计模式读书笔记(2)-观察者模式(Observer Pattern) HeadFirst设计 ...

  3. Head First 设计模式读书笔记(1)-策略模式

    一.策略模式的定义 策略模式定义了算法族,分别封装起来,让它们之间可以互换替换,此模式让算法的变化独立使用算法的客户. 二.使用策略模式的一个例子 2.1引出问题 某公司做了一套模拟鸭子的游戏:该游戏 ...

  4. JavaScript设计模式:读书笔记(未完)

    该篇随我读书的进度持续更新阅读书目:<JavaScript设计模式> 2016/3/30 2016/3/31 2016/4/8 2016/3/30: 模式是一种可复用的解决方案,可用于解决 ...

  5. C#设计模式学习笔记:(10)外观模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7772184.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲结构型设计模式的第五个模式--外 ...

  6. 图解http读书笔记

    以前对HTTP协议一知半解,一直不清楚前端需要对于HTTP了解到什么程度,知道接触的东西多了,对于性能优化.服务端的配合和学习中也渐渐了解到了HTTP基础的重要性,看了一些大神对HTTP书籍的推荐,也 ...

  7. Java设计模式学习笔记(二) 简单工厂模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 正文开始... 1. 简介 简单工厂模式不属于GoF23中设计模式之一,但在软件开发中应用也较为 ...

  8. Java设计模式学习笔记(三) 工厂方法模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 简介 上一篇博客介绍了简单工厂模式,简单工厂模式存在一个很严重的问题: 就是当系统需要引入 ...

  9. Java设计模式学习笔记(四) 抽象工厂模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 抽象工厂模式概述 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问 ...

  10. 设计模式(十二)Decorator模式

    Decorator模式就是不断地为对象添加装饰的设计模式.以蛋糕为例,程序中的对象就相当于蛋糕,然后像不断地装饰蛋糕一样地不断地对其增加功能,它就变成了使用目的更加明确的对象. 首先看示例程序的类图. ...

随机推荐

  1. MySql+EF+CodeFirst

    ef+mssql详细是许多.net程序员的标配.作为一个程序员当然不能只会mssql这一个数据库,今天简单聊聊ef+mysql.推荐新人阅读. 1]首先创建一个mvc项目,如图: 创建完毕之后再nug ...

  2. 微信小程序css篇----flex模型

    一.Flex布局是什么? Flex是Flexible Box的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性. 任何一个容器都可以指定为Flex布局. .box{displ ...

  3. Prometheus快速入门

    Prometheus是一个开源的,基于metrics(度量)的一个开源监控系统,它有一个简单而强大的数据模型和查询语言,让我们分析应用程序.Prometheus诞生于2012年主要是使用go语言编写的 ...

  4. 奇葩的狐火浏览器border属性

    今天接到一个bug任务,客户反映火狐浏览器访问时某个商品楼层不显示商品.我立即打开我的火狐浏览器发现没有复现这个bug,后来经过一番折腾,才发现火狐浏览器缩放到90%时,商品楼层果然就消失了,而且每台 ...

  5. 主流Linux发行版及联系

    一.主流Linux主流发行版 RedHat:Red Hat Linux是由Red Hat公司发行的一个Linux发行包.其1.0版本于1994年11月3日发行,虽然其历史不及Slackware般悠久, ...

  6. HTML与CSS中的颜色与单位个人分享

    颜色与单位 Web安全色有216中其中色彩有210中,非色彩6中 前景色与背景色 前景色就是设置字体的颜色 背景色就是为指定元素设置背景色 - 浏览器默认背景色的颜色为透明色 颜色的命名 1.使用单词 ...

  7. Arduino-LiquidCrystal_I2C 液晶库

    I2C转接板上[PCF8574T转接板]VCC接5V,GND接GND,SCL接SCL(即A05),SDA接SDA(即A04) 常用的函数是 lcd.init(),lcd初始化 setCursor(x, ...

  8. angularjs表单注册--两次密码验证

    html <div class="container" ng-controller="RegisterCtrl"> <form name=&q ...

  9. DNS域名解析系统

    1.DNS的组成 DNS系统是为解析域名为IP地址而存在的,它是由域名空间.资源记录.名称服务器和解析器组成. 域名空间是包含一个树状结构,用于存储资源记录的空间. 资源记录是与域名相关的数据,如IP ...

  10. LTM_本地流量管理(二)

    会话保持 首先要熟悉两个概念:连接connect和会话session 连接:在四层负载均衡中,连接是最小元素. l  源端口:客户端随机产生的端口. l  源地址:发起请求的源IP地址. l  目的端 ...