首页 > 其他分享 >百度工程师教你玩转设计模式(装饰器模式)

百度工程师教你玩转设计模式(装饰器模式)

时间:2022-12-23 11:31:39浏览次数:61  
标签:log void 装饰 玩转 message 设计模式 public 百度 String

百度工程师教你玩转设计模式(装饰器模式)_设计模式

作者 | 北极星小组

想要写好代码,设计模式(Design Pattern)是必不可少的基本功,设计模式是对面向对象设计(Object Oriented Design)中反复出现的一类问题的一种解决方案,本篇介绍装饰器模式(Decorator Pattern)。

在我们日常的开发过程中,一个最常见的场景就是在已有的基础上新增功能,常规的做法有以下几种:

  • 修改已有的类:违背开闭原则。
  • 增加新的子类:每次都得新增大量对应的类,随着功能的增加,子类越来越膨胀。

在此场景下,装饰器模式就可以体现出它的优势了,它允许在不修改原有对象的前提下,灵活的扩展已有类的功能。下面是装饰器模式的一个通用的类图:

百度工程师教你玩转设计模式(装饰器模式)_装饰器模式_02


△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 ----------

推荐阅读【技术加油站】系列:

​百度工程师教你玩转设计模式(工厂模式)​

​百度工程师教你玩转设计模式(适配器模式)​

​百度工程师教你玩转设计模式(单例模式)​

百度工程师教你玩转设计模式(装饰器模式)_java_03

标签:log,void,装饰,玩转,message,设计模式,public,百度,String
From: https://blog.51cto.com/u_15082365/5965294

相关文章

  • 设计模式之美--建造者模式
    建造者模式的作用:创建对象;为什么要使用创建者模式来创建对象,普通的new对象不可以吗?如果使用构造器来创建对象,当参数值的数量过多时,代码中的构造器数量和形式较混乱,并且......
  • 设计模式-----结构型模式
    设计模式结构型包含:适配器模式、桥接模式、组合模式、装饰模式、享元模式、外观模式、代理模式。关系分类适配器:将一个类的接口转换成客户希望的另一个接口。适配器模式使得......
  • 设计模式-----创建型模式
    创建型模式隐藏了这些类的实例是如何被创建和放在一起,整个系统关于这些的对象所知道的是由抽象接口所定义的接口。这样创建型模式在创建什么、谁创建它、它是怎么被创建的,以......
  • Centos 下载百度网盘文件
    安装准备pipinstallscreen#保持下载不中断screen-Sbypy#创建一个专门针对bypy的使用界面pipinstallbypy#百度网盘python版登陆认证百度网盘bypyinfoPlea......
  • PPT 玩转形状
    形状https://www.cnblogs.com/vipsoft/p/16943810.html形状也可以非常复杂形状的神奇功能--合并形状编辑顶点https://www.cnblogs.com/vipsoft/p/1694463......
  • 一行CSS样式去除百度地图版权,去除百度地图右上角平移缩放控件的市县区文字
    /*去除百度地图版权,去除百度地图右上角平移缩放控件的市县区文字*/.anchorBL,.BMap_zlHolder{display:none;visibility:hidden;}......
  • python之调用高德、百度api解析经纬度地址
    调用高德#高德地图根据经纬度反查地址,每天只能调用5000次defgaode_excute_single_query(coordStrings,currentkey='你自己的api-key'):#1-将coordList中的经纬......
  • 23种设计模式,你学废了嘛?
      23种经典设计模式共分为3种类型,分别是创建型、结构型和行为型。今天,我们把这3种类型分成3个对应的小模块,逐一带你回顾一下每一种设计模式的原理、实现、设计意图和......
  • 设计模式——命令模式
    今天我们来学习命令模式,这个设计模式使用频率较低,也不不太好理解,并且只应用在特定的环境下。所以,暂时不准备深入的学习,了解即可。概述命令模式:(CommandPattern)将一个请求封......
  • 百度PaddlePaddle抠图
     环境准备python版本需要是3.6至3.10。pip版本需要是20.2.2或更高。本地安装的是python3.6,pip版本升级到了21.3.1。python--version>>Python3.6.4......