设计模式笔记-装饰器模式

让人想犯罪 __ 提交于 2020-03-08 21:12:14

设计模式

装饰器模式


一个比继承更具有可扩展性的设计模式。这种设计模式带来的直接好处是对扩展开发,对修改关闭,这是一个重要的设计原则。当然,它也带来了一些问题,比如:类数量较多,客户端程序员难以理解。但是,只要理解了这种设计模式的结构就可以轻松地使用这种设计模式相关的api,比如java io相关的api。本文会在末尾基于装饰器模式对java io做一个总结。

定义

动态地赋予对象新的责任。

所谓动态的意思是在运行时而非编译时。而继承是在编译时就决定了的,所以继承必须在编译时完成对类的扩展。继承导致的问题就是死板,即代码灵活性较低,因为我们总要先在某个子类中定义好需要扩展的行为,再去另外一个子类中定义需要扩展的行为…直到所有子类。当子类个数较多时就会非常复杂。

所谓责任是指行为/功能。特别需要说明的是,我们绝对不改变 现有类 的行为,我们只改变 当前类 该行为的结果,这两个类具有相同的接口(注意,所谓接口是指可以接收相同的消息,所以我使用具有而非实现二字)。

上述解释通过阅读下述假定场景可知。

假定场景

请先看下面的饮料相关代码

假定要卖咖啡,所以要计算所有种类咖啡的价格。现在有摩卡咖啡类,它有一个cost()用来计价。还有一些配料,比如牛奶,泡芙等,它们都有一个cost()来计价。现有类是摩卡咖啡,当前类是牛奶。我们让牛奶的构造器参数列表要求一个咖啡对象,这样把摩卡咖啡对象(moka)就可以传递给牛奶对象。注意:现在需要定义牛奶对象的cost()方法,该cost()的逻辑是0.75+moka.cost()。这里牛奶对摩卡咖啡进行了扩展,因为我们修改了cost()的逻辑。说到这里,你应该想到了二者应该具有一致的接口,且该接口包含cost()方法,没错。但是,我们并没有修改摩卡咖啡的cost()方法一丝一毫,这里你可以说这不就是委托/代理嘛,是的。

我想应该会有人有一个强烈的疑问:我又没有在摩卡咖啡类中 扩展 摩卡咖啡/咖啡基类,这算什么扩展。从继承的角度,扩展要么就是在子类中添加一些新的方法/字段,要么直接在基类添加。其实,扩展就是让一个对象具有新的行为。这个,可以是添加单独的方法/字段,也可以是让同一个方法产生不同的行为。这里一定要记住,我们绝不修改已有的代码。

运行时修改对象的行为

通常,装饰器设计模式中会有一个抽象基类,在此处假设就是饮料类。饮料类包含一个抽象方法,就是cost()。摩卡咖啡具有cost(),配料牛奶也具有cost()。再说一遍,我们有一个摩卡咖啡对象(一个饮料对象),用该对象创建一个牛奶对象(一个饮料对象)。这样,就可以理解了饮料对象的行为在运行时被我们改变了

饮料相关代码

public abstract class Bevarage {
    abstract double cost();
}
//被装饰者
public class MokaCoffee extends Bevarage {
    @Override
    double cost() {
        return 1.00;
    }
}

//装饰器基类
public abstract class Decorator extends Bevarage {
}

//装饰器
public class Milk extends Decorator {
    private Bevarage bevarage;
    @Override
    double cost() {
        return bevarage.cost() + 0.2;
    }

    public Milk(Bevarage bevarage) {
        this.bevarage = bevarage;
    }
}
public class Test {
    public static void main(String[] args) {
        //摩卡咖啡就是饮料
        Bevarage moka = new MokaCoffee();
        //赋予新的责任
        moka = new Milk(moka);
        double price = moka.cost();
        System.out.println(price);
    }
}
1.2

Process finished with exit code 0

其实,从代码上看是非常易懂的。

行话

MokaCoffee可以称为组件,Milk可以称为装饰器

注意

不要总是想着在代码中使用装饰器设计模式,这会使得代码更加难以理解!且类的个数变多!
只需要在总是可能发生变化的地方考虑使用即可。

java io的装饰器模式

以字节输入流为例InputStream就是组件的抽象基类,FilterInputStream就是装饰器基类, ByteInputSteam和FileInputStream就是具体的组件类,而BufferedInputStream就是具体的装饰器类。

自定义一个装饰器类

public class ToUpperCase extends FilterInputStream {
    @Override
    public int read() throws IOException {
        int temp = super.read();
        if (temp >= 'a' && temp <= 'z') {
            temp = temp - ('a'-'A');
        }
        return temp;
    }

    public ToUpperCase(InputStream in) {
        super(in);
    }
}
ToUpperCase in = new ToUpperCase(new StringBufferInputStream("abCD"));
StringBuilder stringBuilder = new StringBuilder();
int temp;
while ((temp = in.read()) != -1) {
    stringBuilder.append((char) temp);
}
System.out.println(stringBuilder);
ABCD
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!