likes
comments
collection
share

「设计模式」装饰器模式

作者站长头像
站长
· 阅读数 20

一、概述

装饰器模式(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等。