设计模式--结构模式--装饰模式
一、基本概念
有一次面试时,面试官问了一个问题,怎么做类增强,当时心里想,这是什么鬼问题,通过实现接口,继承类不就可以了吗,当时不知道面试官的想法或者要解决什么问题。后续带着这个问题做项目,碰到了装饰模式,才明白,面试官可能是让我理解一下设计模式的应用。
Java中三个基本特性:继承、接口、反射是实现扩展功能基础手段。其他模式的本质也无非是这些。
装饰模式(Decorator Pattern) ,也称为包装模式(wapper Pattern):动态地给一个对象增加一些额外的职责。它最明显的区别就是,不是继承一个类,而是通过组合的方式,增强了功能。
普通共识:装饰模式比生成子类实现更为灵活,装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,可以在不需要创建更多子类的情况下,让对象的功能得以扩展。是一种用于替代继承的技术,它通过一种无须定义子类的方式给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系。
如果从方便认知上可以这样理解:继承,认为是爷爷->父亲->儿子,辈分不一样;而装饰模式呢,可以认为是辈分是一致,只做增强和特性化需求。
二、简单理解
这个小段,用一个小例子说明一下装饰模式(Decorator Pattern) /包装模式(wapper Pattern)。
1、定义枪的接口,枪可以装刺刀和射击
package comm.pattern.struct.decorator.wapper;
import java.security.PublicKey;
/**
*
* @Title: IGun.java
* @Package: comm.pattern.struct.decorator.wapper
* @Description: 定义枪的类型
* @author yangzhancheng
* @2022年5月25日:下午11:02:52
*
*/
public interface IGun {
/**
* 装刺刀,尽量不这么定义,一个类定义一个方法
*/
public void bayonet();
/**
* 射击
*/
public void shut();
}
2、定义步枪(非全自动步枪,只能一发一射)
package comm.pattern.struct.decorator.wapper;
/**
*
* @Title: Rifle.java
* @Package: comm.pattern.struct.decorator.wapper
* @Description: 步枪
* @author yangzhancheng
* @2022年5月25日:下午11:04:58
*
*/
public class Rifle implements IGun {
/**
* 装刺刀
*/
public void bayonet() {
System.out.println("步枪装刺刀....");
}
/**
* 射击
*/
@Override
public void shut() {
System.out.println("步枪射击,点射");
}
}
3、定义自动步枪(在步枪的基础上增强,或者说是包装一下)
package comm.pattern.struct.decorator.wapper;
/**
*
* @Title: AutoRifleMapper.java
* @Package: comm.pattern.struct.decorator.wapper
* @Description: 自动步枪
* @author yangzhancheng
* @2022年5月25日:下午11:08:14
*
*/
public class AutoRifleMapper implements IGun{
private IGun iGun;
public AutoRifleMapper(IGun iGun){
this.iGun = iGun;
}
/**
* 装刺刀
*/
@Override
public void bayonet(){
iGun.bayonet();
}
/**
* 射击
*/
@Override
public void shut(){
iGun.shut();
System.out.println("自动步枪射击,连续射击");
}
}
4、测试类
package comm.pattern.struct.decorator.wapper;
/**
*
* @Title: Client.java
* @Package: comm.pattern.struct.decorator.wapper
* @Description: 客户端演示
* @author yangzhancheng
* @2022年5月25日:下午11:18:50
*
*/
public class Client {
public static void main(String[] args) {
IGun rifle = new Rifle();
rifle.bayonet();
rifle.shut();
System.out.println("\n---------------------------------");
AutoRifleMapper autoRifleMapper = new AutoRifleMapper(rifle);
autoRifleMapper.bayonet();
autoRifleMapper.shut();
}
}
测试结果
步枪装刺刀.... 步枪射击,点射 --------------------------------- 步枪装刺刀.... 步枪射击,点射 自动步枪射击,连续射击
三、按设计模式思路理解
(一)基本概念
1、装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。
2、装饰对象包含一个真实对象的引用,非常关键。
3、角色:
A:抽象构件角色(Component):定义一个对象接口或抽象类,可以给这些对象动态地添加职责。
B:具体构件角色(ConcreteComponent):实际被动态地添加职责的对象。
C:抽象装饰者角色(Decorator):实现了Component接口,用来扩展Component类的功能,但对于Component来说,是无需知道Decorator的存在的。
D:具体装饰者角色(ConcreteDecorator):动态地添加职责的对象。
E:客户端(Client)
(二)例子(为简单实现,把C省略了)
1:抽象构件角色(Component):
package comm.pattern.struct.decorator;
/**
*
* @Title: Component.java
* @Package: comm.pattern.struct.decorator
* @Description: 抽象构建,模拟杨过
* @author yangzhancheng
* @2020年3月16日:上午12:00:38
*
*/
public interface Component {
public void operation();
}
2:具体构件角色(ConcreteComponent):
package comm.pattern.struct.decorator;
public class ConcreteComponent implements Component {
public void operation() {
System.out.println("小张说:'杨过,名过,字改之,是金庸武侠小说《神雕侠侣》的主人公';");
}
}
3:抽象装饰者角色(Decorator):
省略。
4:具体装饰者角色(ConcreteDecorator):
4-1:
package comm.pattern.struct.decorator;
/**
*
* @Title: Decorator.java
* @Package: comm.pattern.struct.decorator
* @Description: 装饰类
* @author yangzhancheng
* @2020年3月16日:上午12:05:54
*
*/
class DecoratorA implements Component
{
private Component component;
public DecoratorA(Component component)
{
this.component=component;
}
public void operation()
{
component.operation();
System.out.println("小李补充:‘他在活死人墓待过,练过玉女心经’;");
}
}
4-2:
package comm.pattern.struct.decorator;
/**
*
* @Title: DecoratorB.java
* @Package: comm.pattern.struct.decorator
* @Description: 装饰类
* @author yangzhancheng
* @2020年3月16日:上午12:10:12
*
*/
class DecoratorB implements Component {
private Component component;
public DecoratorB(Component component) {
this.component = component;
}
public void operation() {
component.operation();
System.out.println("小黄补充:‘他有一只雕兄’。");
}
}
5:客户端(Client)\测试类
package comm.pattern.struct.decorator;
/**
*
* @Title: Client.java
* @Package: comm.pattern.action.templateMethod
* @Description: 描述该文件做什么
* @author yangzhancheng
* @2020年3月1日:下午4:47:17
*
*/
public class Client {
public static void main(String[] args) {
/**
* 定义基础信息,他是杨过
*/
Component component = new ConcreteComponent();
component.operation();
/**
* 增强一次,或者说是包装/装饰一下,使得人物信息更完美
*/
System.out.println("---------------------------------");
Component partyComponentDec = new DecoratorA(component);
partyComponentDec.operation();
/**
* 再次增强一次,或者说是包装/装饰一下,使得人物信息更完美,还生动
*/
System.out.println("---------------------------------");
Component allComponent = new DecoratorB(new DecoratorA(new ConcreteComponent()));
allComponent.operation();
}
}
运行输出
小张说:'杨过,名过,字改之,是金庸武侠小说《神雕侠侣》的主人公'; --------------------------------- 小张说:'杨过,名过,字改之,是金庸武侠小说《神雕侠侣》的主人公'; 小李补充:'他在活死人墓待过,练过玉女心经'; --------------------------------- 小张说:'杨过,名过,字改之,是金庸武侠小说《神雕侠侣》的主人公'; 小李补充:'他在活死人墓待过,练过玉女心经'; 小黄补充:'他有一只雕兄'。
四、优缺点
装饰与外观模式的比较:装饰模式是对同一类接口进行功能增强;外观模式,是把子类集中到一起,让另外一个类统一展示。
优点:
1、装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象扩展功能,即插即用。
2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。
3、装饰者完全遵守开闭原则。
缺点:
1、会出现更多的代码,更多的类,增加程序复杂性。
2、动态装饰时,多层装饰时会更复杂
五、源代码分析
(一)springBoot 中 wapper
(二)IO 工作流
1、用Reader\BufferedReader\InputStreamReader\FileReader 说明,其中InputStreamReader\FileReader 看成是一个,因为他们是通过继承实现的。
A、BufferedReader 与 Reader关系,定义了read()方法为输入。

B、InputStreamReader\FileReader 和reader关系


2、用角色模型分析设计模式
A:抽象构件角色(Component):
public abstract class Reader implements Readable, Closeable {
//读出流处理
public int read() throws IOException {
char cb[] = new char[1];
if (read(cb, 0, 1) == -1)
return -1;
else
return cb[0];
}
//读出流处理
public int read(char cbuf[]) throws IOException {
return read(cbuf, 0, cbuf.length);
}
abstract public int read(char cbuf[], int off, int len) throws IOException;
}
B:具体构件角色(ConcreteComponent):
package java.io;
public class FileReader extends InputStreamReader {
public FileReader(String fileName) throws FileNotFoundException {
super(new FileInputStream(fileName));
}
public FileReader(File file) throws FileNotFoundException {
super(new FileInputStream(file));
}
public FileReader(FileDescriptor fd) {
super(new FileInputStream(fd));
}
}
package java.io;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import sun.nio.cs.StreamDecoder;
public class InputStreamReader extends Reader {
private final StreamDecoder sd;
public int read() throws IOException {
return sd.read();
}
public int read(char cbuf[], int offset, int length) throws IOException {
return sd.read(cbuf, offset, length);
}
}
C:具体装饰者角色(ConcreteDecorator):
package java.io;
public class BufferedReader extends Reader {
//构造 注意Reader类型就可以
public BufferedReader(Reader in) {
this(in, defaultCharBufferSize);
}
//重新增强了read方法
public int read() throws IOException {
synchronized (lock) {
ensureOpen();
for (;;) {
if (nextChar >= nChars) {
fill();
if (nextChar >= nChars)
return -1;
}
if (skipLF) {
skipLF = false;
if (cb[nextChar] == '\n') {
nextChar++;
continue;
}
}
return cb[nextChar++];
}
}
}
//重新增强了read方法
public int read(char cbuf[], int off, int len) throws IOException {
synchronized (lock) {
ensureOpen();
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int n = read1(cbuf, off, len);
if (n <= 0) return n;
while ((n < len) && in.ready()) {
int n1 = read1(cbuf, off + n, len - n);
if (n1 <= 0) break;
n += n1;
}
return n;
}
}
//read方法不是特别好用,增强了readLine方法,这个就是比较巧妙的用法
public String readLine() throws IOException {
return readLine(false);
}
//readLine的具体实现方法
String readLine(boolean ignoreLF) throws IOException {
StringBuffer s = null;
int startChar;
synchronized (lock) {
ensureOpen();
boolean omitLF = ignoreLF || skipLF;
bufferLoop:
for (;;) {
if (nextChar >= nChars)
fill();
if (nextChar >= nChars) { /* EOF */
if (s != null && s.length() > 0)
return s.toString();
else
return null;
}
boolean eol = false;
char c = 0;
int i;
/* Skip a leftover '\n', if necessary */
if (omitLF && (cb[nextChar] == '\n'))
nextChar++;
skipLF = false;
omitLF = false;
charLoop:
for (i = nextChar; i < nChars; i++) {
c = cb[i];
if ((c == '\n') || (c == '\r')) {
eol = true;
break charLoop;
}
}
startChar = nextChar;
nextChar = i;
if (eol) {
String str;
if (s == null) {
str = new String(cb, startChar, i - startChar);
} else {
s.append(cb, startChar, i - startChar);
str = s.toString();
}
nextChar++;
if (c == '\r') {
skipLF = true;
}
return str;
}
if (s == null)
s = new StringBuffer(defaultExpectedLineLength);
s.append(cb, startChar, i - startChar);
}
}
}
}
D:客户端/测试端(Client)
package com.yang.springBoot010;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
public class IOtest {
public static void main(String[] args) throws Exception {
BufferedReader bufferedReader = null;
FileReader fReader = null;
try {
fReader = new FileReader(new File("/工作日志.txt"));
bufferedReader = new BufferedReader(fReader);
String strline = null;
while (null != (strline = bufferedReader.readLine())) {
System.out.println(strline);
}
} catch (Exception e) {
System.out.println(e);
} finally {
if (bufferedReader != null) {
bufferedReader.close();
}
if (fReader != null) {
fReader.close();
}
}
}
}
浙公网安备 33010602011771号