共计 6453 个字符,预计需要花费 17 分钟才能阅读完成。
作者 | 北极星小组
想要写好代码,设计模式(Design Pattern)是必不可少的基本功,设计模式是对面向对象设计(Object Oriented Design)中重复呈现的一类问题的一种解决方案,本篇介绍装璜器模式(Decorator Pattern)。
在咱们日常的开发过程中,一个最常见的场景就是在已有的根底上新增性能,惯例的做法有以下几种:
- 批改已有的类:违反开闭准则。
- 减少新的子类:每次都得新增大量对应的类,随着性能的减少,子类越来越收缩。
在此场景下,装璜器模式就能够体现出它的劣势了,它容许在不批改原有对象的前提下,灵便的扩大已有类的性能。上面是装璜器模式的一个通用的类图:
△UML
其中的各个类的作用如下:
- 形象组件 (Component): 能够是接口或者抽象类,它定义了具体类以及装璜器所领有的办法。
- 具体组件 (ComponentA, ComponentB): 具体的组件,实现或者继承自形象组件。能够了解成上述场景中已存在的类。
- 形象装璜器 (Decorator): 通常为抽象类,持有一个被装璜的对象,定义了具体装璜器的办法。此类非必须也能够没有,具体装璜器也可间接继承或者实现形象组件。
- 具体装璜器 (DecoratorX, DecoratorY): 具体的装璜器,继承自形象装璜器 (也可间接继承自形象组件),扩大了形象组件的某些性能。
上面,将通过 3 个具体的案例的解说装璜器的应用形式,不便大家进一步的了解。
一、装璜器在工作解决场景的利用
在理论的开发中,咱们常常须要定义不同的类来解决各种不同的工作。假如一个这样的场景,咱们的零碎有多个具体的类,用来解决不同类型的工作。当初须要增加一个性能,就是在解决完工作后收回一条音讯。针对这个场景,应用装璜器模式的实现思路如下:
- 形象组件 (TaskProcessor):解决工作的抽象类 (亦可通过接口实现),定义一个通用的工作解决办法 process()。
- 具体组件 (TaskProcessorA, TaskProcessorB): 负责实现具体的工作解决逻辑
- 形象装璜器 (TaskProcessDecorator): 持有一个工作解决对象实例
- 具体装璜器 (AfterTaskProcessDecorator): 实现具体的工作解决实现后的音讯告诉扩大能力
具体的代码如下:
package com.baidu.demo;
public class Decorator {
// 形象组件
static abstract class TaskProcessor {abstract void process();
}
// 具体组件
static class TaskProcessorA extends TaskProcessor {
@Override
void process() {System.out.println("TaskProcessorA 解决实现");
}
}
// 具体组件
static class TaskProcessorB extends TaskProcessor {
@Override
void process() {System.out.println("TaskProcessorB 解决实现");
}
}
// 形象装璜器
static abstract class TaskProcessDecorator extends TaskProcessor {
protected TaskProcessor processor;
public TaskProcessDecorator(TaskProcessor processor) {this.processor = processor;}
abstract void process();}
// 具体装璜器
static class AfterTaskProcessDecorator extends TaskProcessDecorator {public AfterTaskProcessDecorator(TaskProcessor processor) {super(processor);
}
@Override
void process() {processor.process();
afterProcess();}
void afterProcess() {System.out.println("工作处理完毕,发送音讯...");
}
}
public static void main(String[] args) {
// 扩大之前
System.out.println("==========before==========");
TaskProcessor processorA = new TaskProcessorA();
processorA.process();
TaskProcessor processorB = new TaskProcessorB();
processorB.process();
// 装璜器扩大之后:TaskProcessorA TaskProcessorB 并未做任何批改,即可实现性能的扩大
System.out.println("==========after==========");
TaskProcessor decoratorA = new AfterTaskProcessDecorator(processorA);
decoratorA.process();
TaskProcessor decoratorB = new AfterTaskProcessDecorator(processorB);
decoratorB.process();}
}
// 输入后果如下
==========before==========
TaskProcessorA 解决实现
TaskProcessorB 解决实现
==========after==========
TaskProcessorA 解决实现
工作处理完毕,发送音讯...
TaskProcessorB 解决实现
工作处理完毕,发送音讯...
二、装璜器在文件 IO 场景的利用
装璜器模式,一个典型的利用就是文件 IO 操作,最根底的类实现字节流读取类,应用装璜器模式能够封装文件字节流读取类,而后能够持续封装可缓存的文件字节流读取类,在我的项目中按需应用。具体实现如下:
- InputStream:具体组件,实现读取字节流。
- FileInputStream:具体装璜器,作为 InputStream 的子类,扩大文件操作。
- BufferedInputStream:具体装璜器,作为 FileInputStream 的子类,扩大缓存操作。
具体代码如下:
// 具体组件,实现读取字节流
public abstract class InputStream {public int read(byte b[], int off, int len) {}}
// 具体装璜器,作为 InputStream 的子类,扩大文件操作
public class FileInputStream extends InputStream {
protected InputStream in;
public FileInputStream(String name) {
InputStream in = ... // 此处省略,通过文件名创建对象
this.in = in;
}
public int read(byte b[], int off, int len) {return this.in.read(b, off, len);
}
}
// 具体装璜器,作为 FileInputStream 的子类,扩大缓存操作
public class BufferedInputStream extends FileInputStream {
protected FileInputStream in;
protected byte[] buffer;
public BufferedInputStream(FileInputStream in) {this.in = in;}
public int read(byte b[], int off, int len) {if (this.buffer == null || this.buffer.length == 0) {this.in.read(this.buffer, 0, in.lenght());
}
System.arraycopy(this.buffer, off, b, 0, len);
...
}
}
public static void main(String[] args) {FileInputStream fs = new FileInputStream('./test.log');
BufferedInputStream bs = new BufferedInputStream(fs);
byte[] b;
bs.read(b, 0, 1);
}
三、装璜器在日志零碎场景的利用
在日志零碎中,个别罕用日志的级别别离为 DEBUG(调试)、INFO(运行信息)、WARN(正告)、ERROR(谬误),一旦产生谬误级别的日志后,则须要触发报警告诉相干人员及时进行跟进,报警形式个别有:邮件、短信、如流等,通常咱们会依据业务场景以组合的形式进行报警告诉,应用装璜器模式则能很好实现组合报警这一性能。
- 形象组件:Log 接口形象
- 具体组件:Slf4j 具体日志类的实现
- 形象装璜器:LogDecorator 日志装璜器的基类
- 具体装璜器:MailLogDecorator、SMSLogDecorator、InfoFlowLogDecorator 具体装璜类
/**
* 日志接口
*/
public interface Log {void debug(String message);
void info(String message);
void warn(String message);
void error(String message);
}
/**
* Slf4j 日志
*/
public class Slf4jLog implements Log {
// 日志记录对象
private final Logger log = LoggerFactory.getLogger("system_log");
@Override
public void debug(String message) {if (log.isDebugEnabled()) {log.debug(message);
}
}
@Override
public void info(String message) {if (log.isInfoEnabled()) {log.info(message);
}
}
@Override
public void warn(String message) {if (log.isWarnEnabled()) {log.warn(message);
}
}
@Override
public void error(String message) {if (log.isErrorEnabled()) {log.error(message);
}
}
}
/**
* 日志装璜器
*/
public class LogDecorator implements Log {
protected Log log;
public LogDecorator(Log log) {this.log = log;}
@Override
public void debug(String message) {log.debug(message);
}
@Override
public void info(String message) {log.info(message);
}
@Override
public void warn(String message) {log.warn(message);
}
@Override
public void error(String message) {log.error(message);
}
}
/**
* 邮件日志装璜器
*/
public class MailLogDecorator extends LogDecorator {public MailLogDecorator(Log log) {super(log);
}
@Override
public void warn(String message) {log.warn(message);
mail(message);
}
@Override
public void error(String message) {log.error(message);
mail(message);
}
public void mail(String message) {
// 模仿邮件发送
log.info("邮件已发送,信息:" + message);
}
}
/**
* 短信日志装璜器
*/
public class SMSLogDecorator extends LogDecorator {public SMSLogDecorator(Log log) {super(log);
}
@Override
public void error(String message) {log.error(message);
send(message);
}
public void send(String message) {
// 模仿短信发送
log.info("短信已发送,信息:" + message);
}
}
/**
* 如流日志装璜器
*/
public class InfoflowLogDecorator extends LogDecorator {public InfoflowLogDecorator(Log log) {super(log);
}
@Override
public void warn(String message) {log.warn(message);
send(message);
}
@Override
public void error(String message) {log.error(message);
send(message);
}
public void send(String message) {
// 模仿如流发送
log.info("如流音讯已发送,信息:" + message);
}
}
/**
* 日志测试类
*/
public class LogTest {
/**
* 测试日志装璜器
*/
@Test
public void testLogDecorator() {Log log = new SMSLogDecorator(new InfoFlowLogDecorator(new MailLogDecorator(new Slf4jLog())));
log.debug("零碎调试开启");
log.info("零碎失常运行");
log.warn("数据为空正告");
log.error("db 连贯谬误");
}
}
===========output=========
15:16:56.564 [main] DEBUG system_log - 零碎调试开启
15:16:56.566 [main] INFO system_log - 零碎失常运行
15:16:56.566 [main] WARN system_log - 数据为空正告
15:16:56.566 [main] INFO system_log - 邮件已发送,信息:数据为空正告
15:16:56.566 [main] INFO system_log - 如流音讯已发送,信息:数据为空正告
15:16:56.566 [main] ERROR system_log - db 连贯谬误
15:16:56.566 [main] INFO system_log - 邮件已发送,信息:db 连贯谬误
15:16:56.566 [main] INFO system_log - 如流音讯已发送,信息:db 连贯谬误
15:16:56.566 [main] INFO system_log - 短信已发送,信息:db 连贯谬误
Process finished with exit code 0
四、总结
如上几个案例,装璜器的最大作用就是在不批改原有类的根底上扩大已有的性能,它合乎开闭准则,而且实现也比拟灵便。
———- END ———-
举荐浏览【技术加油站】系列:
百度工程师教你玩转设计模式(工厂模式)
百度工程师教你玩转设计模式(适配器模式)
百度工程师教你玩转设计模式(单例模式)