Loading

装饰器设计模式

装饰器模式

​ 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

问题引出

业务场景:有一家奶茶店卖奶茶,一开始该奶茶店只有4种奶茶,无糖的,少糖的,去冰的,加冰的,给每一种奶茶都标明一个价格。

abstract class MilkTea{
    private String description;

    public MilkTea(String description) {
        this.description = description;
    }

    public abstract double cost();

    public String getDescription() {
        return description;
    }
}

class SugarFree extends MilkTea{

    public SugarFree() {
        super("无糖奶茶");
    }

    @Override
    public double cost() {
        return 1;
    }
}

class Noice extends MilkTea{

    public Noice() {
        super("去冰奶茶");
    }

    @Override
    public double cost() {
        return 2;
    }
}

class LessSugar extends MilkTea{

    public LessSugar() {
        super("少糖奶茶");
    }

    @Override
    public double cost() {
        return 3;
    }
}

class Ice extends MilkTea{

    public Ice() {
        super("加冰的奶茶");
    }

    @Override
    public double cost() {
        return 3;
    }
}


// ===========================================

public class Problem {
    public static void main(String[] args) {
        MilkTea milkTea1=new SugarFree();
        MilkTea milkTea2=new Noice();
        MilkTea milkTea3=new LessSugar();
        MilkTea milkTea4=new Ice();

        System.out.println(milkTea1.getDescription()+":"+milkTea1.cost());
        System.out.println(milkTea2.getDescription()+":"+milkTea2.cost());
        System.out.println(milkTea3.getDescription()+":"+milkTea3.cost());
        System.out.println(milkTea4.getDescription()+":"+milkTea4.cost());

    }
}

上述代码很"很完美的完成了我们需求",当但我们的需求发生变化时,上述代码就会出现很严重的问题。比如:此茶奶茶店老板想出了一些新的业务,奶茶里面可以加调料,比如:珍珠,烧仙草,椰果,我们如果要在原来的类上进行修改,那么我们就需要再建至少4*4个类,如果后期需求一多,容易出现类爆炸,所以上述代码的实现方式是不可取的。

解决部分问题

为了避免上述的问题,程序员重新编写了代码,设置要添加的挑调料的布尔值,当需要添加的时候,将那一项设置为true即可

class MilkTea {
    private String description;
    private boolean pearl;
    private boolean hotGrassJelly;
    private boolean coconut;

    public MilkTea(String description) {
        this.description = description;
    }

    public double cost() {
        double total = 0;
        if (pearl) {
            total += 0.1;
        }
        if (hotGrassJelly) {
            total += 0.2;
        }
        if (coconut) {
            total += 0.3;
        }
        return total;
    }

    public void setPearl(boolean pearl) {
        this.pearl = pearl;
    }

    public void setHotGrassJelly(boolean hotGrassJelly) {
        this.hotGrassJelly = hotGrassJelly;
    }

    public void setCoconut(boolean coconut) {
        this.coconut = coconut;
    }

    public String getDescription() {
        if (pearl) {
            description = description + "+珍珠";
        }
        if (hotGrassJelly) {
            description = description + "+烧仙草";
        }
        if (coconut) {
            description = description + "+椰果";
        }
        return description;
    }


}

class SugarFree extends MilkTea {

    public SugarFree() {
        super("无糖奶茶");
    }

    @Override
    public double cost() {
        return 1 + super.cost();
    }
}

class Noice extends MilkTea {

    public Noice() {
        super("去冰奶茶");
    }

    @Override
    public double cost() {
        return 2 + super.cost();
    }
}

class LessSugar extends MilkTea {

    public LessSugar() {
        super("少糖奶茶");
    }

    @Override
    public double cost() {
        return 3 + super.cost();
    }
}

class Ice extends MilkTea {

    public Ice() {
        super("加冰的奶茶");
    }

    @Override
    public double cost() {
        return 3 + super.cost();
    }
}

public class EasySolution {
    public static void main(String[] args) {
        MilkTea lessSugar = new LessSugar();
        lessSugar.setCoconut(true);
        lessSugar.setHotGrassJelly(true);
        lessSugar.setPearl(true);
        System.out.println(lessSugar.getDescription() + " " + lessSugar.cost());
    }
}
少糖奶茶+珍珠+烧仙草+椰果 3.6

上述代码种,解决了类爆炸的问题,没有出现各种各样的类。奶茶店老板新加入一种的奶茶,也不会带来什么影响,符合开闭原则。

但当此时,奶茶店老板又加入了一种调料,这样就又会违反开闭原则,因为要修改父类。

装饰器模式

定义一个调料的抽象类,让不同的调料去实现。然后再将已有的奶茶传入。(套娃)

abstract class MilkTea {
    private String description;

    public MilkTea(String description) {
        this.description = description;
    }

    public abstract double cost();

    public String getDescription() {
        return description;
    }
}

class SugarFree extends MilkTea {

    public SugarFree() {
        super("无糖奶茶");
    }

    @Override
    public double cost() {
        return 1;
    }
}

class Noice extends MilkTea {

    public Noice() {
        super("去冰奶茶");
    }

    @Override
    public double cost() {
        return 2;
    }
}

class LessSugar extends MilkTea {

    public LessSugar() {
        super("少糖奶茶");
    }

    @Override
    public double cost() {
        return 3;
    }
}

class Ice extends MilkTea {

    public Ice() {
        super("加冰的奶茶");
    }

    @Override
    public double cost() {
        return 3;
    }
}

// 定义一个调料的抽象类
abstract class Condiment extends MilkTea {

    protected MilkTea milkTea;

    public Condiment(MilkTea milkTea) {
        super("调料:");
        this.milkTea = milkTea;
    }
}

// 定义具体调料
class Pearl extends Condiment {

    public Pearl(MilkTea milkTea) {
        super(milkTea);
    }

    @Override
    public double cost() {
        return milkTea.cost() + 0.1;
    }

    @Override
    public String getDescription() {
        return milkTea.getDescription() + " 珍珠";
    }
}

class HotGrassJelly extends Condiment {

    public HotGrassJelly(MilkTea milkTea) {
        super(milkTea);
    }

    @Override
    public double cost() {
        return milkTea.cost() + 0.2;
    }

    @Override
    public String getDescription() {
        return milkTea.getDescription() + " 烧仙草";
    }
}

class Coconut extends Condiment {

    public Coconut(MilkTea milkTea) {
        super(milkTea);
    }

    @Override
    public double cost() {
        return milkTea.cost() + 0.3;
    }

    @Override
    public String getDescription() {
        return milkTea.getDescription() + " 椰果";
    }
}

public class DecoratorPattern {
    public static void main(String[] args) {
        MilkTea milkTea = new SugarFree();
        MilkTea milkTea1 = new Coconut(milkTea);
        MilkTea milkTea2 = new Pearl(milkTea1);

        System.out.println(milkTea2.getDescription() + " :" + milkTea2.cost());
    }
}

装饰器模式图解

image-20210314164405986

装饰器模式UML图

image-20210314165413048

IO中的装饰器设计模式

使用了装饰器模式之后,类还是有点多,但我们已经尽力了。

jdk中的IO流用的就是装饰器模式。

如图,在IO流中,InputStream就相当于奶茶,继承自他的类,就相当与各种类型的奶茶,比如ObjectInputSteam,在众多继承了他的类中FilterInputStream,就相当于调料抽象类,继承了FilterInputStream的类,就相当于具体的调料了。

image-20210314172443672

image-20210314172452080

在调料抽象类中,一定会继承主类,而且会关联主类,且构造方法中也会将主类传入。

image-20210314172556311

Snipaste_2021-03-14_19-32-37

根据装饰器设计模式扩展Reader

// 读取文件中的字符串,并在字符串前加上行号
public class LineReader extends BufferedReader {

    protected Reader reader;

    private int lineNumber;

    public LineReader(Reader reader) {
        super(reader);
    }


    public int getLineNumber() throws IOException {
            lineNumber++;
        return lineNumber;
    }

    @Override
    // 不实现
    public int read(char[] cbuf, int off, int len) throws IOException {
        return 0;
    }

    @Override
    public void close() throws IOException {
        reader.close();
    }

    public static void main(String[] args) throws IOException {
        Reader reader = new FileReader("E:\\桌面\\a.txt");
        BufferedReader bf = new BufferedReader(reader);
        LineReader lineReader = new LineReader(bf);
        String line;
        while ((line = bf.readLine()) != null) {
            System.out.println(lineReader.getLineNumber() + ": " + line);
        }
    }
}

a.txt:

装饰器设计模式
七大设计原则

工厂模式

结果:

1: 装饰器设计模式
2: 七大设计原则
3: 
4: 工厂模式
posted @ 2021-03-14 20:14  nuoxin  阅读(64)  评论(0)    收藏  举报