「设计模式」装饰器模式
一、概述
装饰器模式(Decorator Pattern)允许向一个现有的对象扩展新的功能,同时不改变其结构。主要解决直接继承下因功能的不断横向扩展导致子类膨胀的问题,无需考虑子类的维护。
说到扩展目标对象功能,还会想到代理模式,装饰器模式和代理模式的对比:
装饰器模式的4种角色:
(1)抽象构件角色(Component):具体构件类和抽象装饰者类的共同父类。
(2)具体构件角色(ConcreteComponent):抽象构件的子类,装饰者类可以给它增加额外的职责。
(3)装饰角色(Decorator):抽象构件的子类,具体装饰类的父类,用于给具体构件增加职责,但在子类中实现。
(4)具体装饰角色(ConcreteDecorator):每一个具体装饰类都定义了一些新的行为,负责向构件添加新的职责。
二、优缺点
优点
(1)不改动原有代码,动态增加功能。
(2)对象间不会相互依赖、松耦合。
(3)符合开闭原则,扩展性好,便于维护。
缺点
(1)装饰器环节过多的话,导致装饰器类膨胀。
(2)装饰器层层嵌套比较复杂,可能导致排查问题流程繁琐。
三、实现方式
秋天到了,女朋友非要喝秋天的第一杯奶茶,到了“蜜雪冰城”奶茶店后,给女朋友点了一杯珍珠芒果奶茶,加了珍珠、芒果等配料,给自己点了一杯加冰柠檬水,加了冰块、柠檬片等配料,这时候就可以使用装饰器模式。
奶茶:抽象构件
珍珠芒果奶茶、柠檬水:具体构件
配料:装饰角色
珍珠、芒果、柠檬:具体装饰角色
下面直接上干货>>>
抽象构件(Component)角色:奶茶
public interface IMilktea {
void addDosing();
}
具体构件(ConcreteComponent)角色:
珍珠奶茶
public class PearlMilktea implements IMilktea{
@Override
public void addDosing() {
System.out.println("开始制作:珍珠奶茶");
}
}
柠檬水
public class LemonMilktea implements IMilktea{
@Override
public void addDosing() {
System.out.println("开始制作:柠檬水");
}
}
装饰(Decorator)角色:配料
public abstract class Dosing implements IMilktea{
IMilktea iMilktea;
public Dosing(IMilktea iMilktea){
this.iMilktea = iMilktea;
}
@Override
public void addDosing() {
this.iMilktea.addDosing();
}
}
具体装饰(ConcreteDecorator)角色
加珍珠
public class Pearl extends Dosing {
public Pearl(IMilktea iMilktea) {
super(iMilktea);
}
@Override
public void addDosing() {
super.addDosing();
System.out.println("制作中:加珍珠");
}
}
加芒果
public class Mango extends Dosing {
public Mango(IMilktea iMilktea) {
super(iMilktea);
}
@Override
public void addDosing() {
super.addDosing();
System.out.println("制作中:加芒果");
}
}
加柠檬
public class Lemon extends Dosing {
public Lemon(IMilktea iMilktea) {
super(iMilktea);
}
@Override
public void addDosing() {
super.addDosing();
System.out.println("制作中:加柠檬");
}
}
加冰
public class Ice extends Dosing {
public Ice(IMilktea iMilktea) {
super(iMilktea);
}
@Override
public void addDosing() {
super.addDosing();
System.out.println("制作中:加冰");
}
}
客户端
public class Client {
public static void main(String[] args) {
System.out.println("服务员:你好,需要点什么呀?");
System.out.println("我: 一杯加芒果、加珍珠的珍珠奶茶,一杯加柠檬、加冰的柠檬水");
System.out.println("服务员:好的。");
System.out.println("---------------------------");
PearlMilktea pearlMilktea = new PearlMilktea();
Pearl pearl = new Pearl(pearlMilktea);
Mango mango = new Mango(pearl);
Ice ice = new Ice(mango);
ice.addDosing();
System.out.println("-------第一杯制作完成----------");
LemonMilktea lemonMilktea = new LemonMilktea();
Lemon lemon = new Lemon(lemonMilktea);
Ice ice1 = new Ice(lemon);
ice1.addDosing();
System.out.println("-------第二杯制作完成----------");
System.out.println("我:珍珠奶茶怎么加冰了?");
System.out.println("服务员:对不起,珍珠奶茶做错了,重新给您做。");
System.out.println("---------------------------");
mango.addDosing();
System.out.println("-------不加冰的珍珠奶茶制作完成----------");
System.out.println("我:好的,谢谢!");
}
}
输出结果
服务员:你好,需要点什么呀?
我: 一杯加芒果、加珍珠的珍珠奶茶,一杯加柠檬、加冰的柠檬水
服务员:好的。
---------------------------
开始制作:珍珠奶茶
制作中:加珍珠
制作中:加芒果
制作中:加冰
-------第一杯制作完成----------
开始制作:柠檬水
制作中:加柠檬
制作中:加冰
-------第二杯制作完成----------
我:珍珠奶茶怎么加冰了?
服务员:对不起,珍珠奶茶做错了,重新给您做。
---------------------------
开始制作:珍珠奶茶
制作中:加珍珠
制作中:加芒果
-------不加冰的珍珠奶茶制作完成----------
我:好的,谢谢!
到此,女朋友喝到了“秋天的第一杯奶茶”。
四、常见应用场景
装饰器模式在Java I/O库中的应用:
Component抽象构件角色:io流中的InputStream,OutputStream,Reader,Writer。
ConcreteComponent具体构件角色:io流中的FileInputStream,FileOutputStream。
Decorate装饰角色:持有抽象构件的引用,FilterInputStream,FilterOutputStream。
ConcreteDecorate具体装饰角色:负责给构件对象添加新的责任,BufferedInputStream,BufferedOutputStream等。
转载自:https://juejin.cn/post/7145403153817534471