【设计模式】结构型-②装饰器模式
【设计模式】结构型-②装饰器模式
注
学习核心
- 装饰器模式核心
- 概念:对比原有通过继承进行功能扩展的方式实现,装饰器模式可以在不修改现有代码的情况下通过添加装饰器来扩展对象的功能
- 组成:
- 简化版本:被装饰对象、装饰者(构造器中接收被装饰对象进行属性填充)
- 抽象版本:被装饰对象接口、被装饰对象实现类、装饰者抽象类(作为抽象基类,实现被装饰对象接口,重写子类方法)、装饰者具体类
- 业务场景案例
- 【JAVA中的IO流】(也可以从BIO、NIO、AIO的演进进行理解):Writer->TextWriter->BufferTextWriter
基本概念
1.初识装饰器(人吃饭)
简单案例分析:
功能扩展:人可以吃饭 =》人在吃饭的时候可以干其他事情
传统方式:通过继承并重写方法实现功能扩展
/**
* Person 类
*/
class Person {
public void eat(){
System.out.println("P 人要吃饭...");
}
}
/**
* NewPerson:
* 传统思路通过继承扩展Person的公共方法
*/
class NewPerson extends Person{
// 通过继承并重写方法实现功能扩展
@Override
public void eat() {
System.out.println("饭前动作....");
super.eat();
System.out.println("饭后动作....");
}
}
public class PersonDemo {
public static void main(String[] args) {
// 普通Person
Person person = new Person();
person.eat();
// NewPerson
NewPerson newPerson = new NewPerson();
newPerson.eat();
}
}
装饰器模式:在不改变原有代码的基础上通过引入装饰器实现功能扩展
通过定义一个装饰类,定义一个构造函数并接收被装饰对象p,可以通过在方法中调用对象p的方法,进而实现功能扩展
此处的装饰器模式涉及两个对象,一个是装饰对象WrapperPerson、一个是被装饰对象Person
class WrapperPerson {
/**
* 装饰者模式:
* 1.定义一个私有的装饰对象
* 2.提供一个带参数的构造函数初始化该对象
* 3.根据需求在原有功能的基础上扩展相应的功能
*/
private Person p;
public WrapperPerson(Person p)
{
this.p = p;
}
public void eat()
{
System.out.println("饭前动作....");
p.eat();
System.out.println("饭后动作....");
}
}
public class WrapperPersonDemo {
public static void main(String[] args) {
WrapperPerson wp = new WrapperPerson(new Person());
wp.eat();
}
}
2.装饰器模式的应用(IO流构建)
可以从BIO、NIO、AIO的演进理解,也可以从功能扩展的切入点去理解。此处的案例会比上面的更复杂一点,进一步理解装饰器模式的演进版本,它将装饰器模式的装饰对象和被装饰对象各自进一步划分为相应的接口和接口实现,简单理解就是此处涉及的对象不再局限于一个具象化的设计,而是基于"一组接口定义规范"
被装饰对象:例如一个IO对象Writer,可能会根据不同的业务场景进行划分,例如根据操作对象划分:操作文本信息TextWriter、操作媒体信息MediaWriter、操作图像信息ImageWriter,那么此处Writer就是一个接口规范,而相应的xxxWriter就是接口的具象化实现
装饰对象:类似地,回归到上述最简单的版本案例。此处定义抽象类装饰器(因为需要通过构造器接收被装饰对象,接口无法定义构造器,因此此处用抽象类)和具体类装饰器,此处WrapperWriter的定义有两点核心:
- WrapperWriter是一个抽象类(回归核心,定义构造方法接收被装饰对象)
- WrapperWriter实现Writer(即实现被装饰对象接口),重写方法继续扩展(此处是为了方法规范,尽量让调用方无感知变化)
被装饰对象:Writer(接口)、xxxWriter(接口实现)
// 接口
public interface Writer {
void write(String str);
}
// 接口实现
public class TextWriter implements Writer {
@Override
public void write(String str) {
System.out.println("text writer");
}
}
public class ImageWriter implements Writer {
@Override
public void write(String str) {
System.out.println("image writer");
}
}
public class MediaWriter implements Writer {
@Override
public void write(String str) {
System.out.println("media writer");
}
}
装饰对象:WrapperWriter(抽象类)、WrapperxxxWriter(具体装饰类)
// WrapperWriter 抽象基类
public abstract class WrapperWriter implements Writer {
private Writer writer;
public WrapperWriter(Writer writer) {
this.writer = writer;
}
@Override
public void write(String str) {
// 调用原方法
this.writer.write(str);
// 扩展方法
System.out.println("decorate write" + str);
}
}
// 子类具体扩展
public class WrapperTextWriter extends WrapperWriter {
public WrapperTextWriter(Writer writer) {
super(writer);
}
@Override
public void write(String str) {
super.write(str);
}
}
测试
public class WrapperWriterDemo {
public static void main(String[] args) {
WrapperTextWriter wtw = new WrapperTextWriter(new TextWriter());
wtw.write("Hello World");
}
}
// output
text writer
decorate write Hello World
补充理解
public class WrapperWriterDemo {
/**
* 装饰者模式是为了解决给类的功能增强而出现的
* --对Writer根据不同的业务进行划分
* Writer
* |------TextWriter
* |------MediaWriter
* |------ImageWriter
*
* ---随着业务功能的提升 这时需要对Writer再次改写 要求提升性能 --利用缓冲
* Writer
* |------TextWriter
* |----BufferedTextWriter
* |------MediaWriter
* |---BufferedMediaWriter
* |------ImageWriter
* |---BufferedImageWriter
*
* ---业务进一步提升需求 这时要求 对Writer 提高容错性和完整性 --- 利用差异传输...
* Writer
* |------TextWriter
* |----BufferedTextWriter
* |----InfoExceptionBufferedTextWrite
* |------MediaWriter
* |---BufferedMediaWriter
* |---InfoExceptionBufferedMediaWriter
* |------ImageWriter
* |---BufferedImageWriter
* |----InfoExceptionBufferedImageWriter
*
* 可以利用装饰者模式 解决继承来实现功能增强的这种行为
* Writer
* |---TextWriter
* |---MediaWriter
* |---ImageWriter
* |---BufferedWriter
* |--InfoExceptionWriter
*
* class BufferedWriter extends Writer{
* private TextWriter textWriter;
* public BufferedWriter( TextWriter textWriter){
* this.textWriter=textWriter;
* }
* }
*
* 使用装饰者模式的 特点:
* 1.装饰者中的构造函数 必须接受被装饰的类
* 2.装饰的类和被装饰的类应该属于同一个体系
*/
}
案例分析
案例2
⼀般在业务开发的初期,往往内部的ERP使⽤只需要判断账户验证即可,验证通过后即可访问ERP的所有资源。但随着业务的不断发展,团队⾥开始出现专⻔的运营⼈员、营销⼈员、数据⼈员,每个⼈员对于ERP的使⽤需求不同,有些需要创建活动,有些只是查看数据。同时为了保证数据的安全性,不会让每个⽤户都有最⾼的权限。那么以往使⽤的 SSO 是⼀个组件化通⽤的服务,不能在⾥⾯添加需要的⽤户访问验证功能。这个时候我们就可以使⽤装饰器模式,扩充原有的单点登录服务。但同时也保证原有功能不受破坏,可以继续使用