设计模式之——装饰者模式

一、认识装饰者模式

定义:

装饰模式指的是在不必改变原类文件的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。提供了比继承更有弹性的替代方案。

作用:

在不用修改任何底层底层代码的情况下,给对象赋予新的功能。

本章可以称为“给爱用继承的人一个全新的设计眼界”

接下来,我们来看看下面这个例子!

二、咖啡店实例

以下是一家咖啡店最初的类设计。
四种具体的咖啡种类继承抽象类Beverage,并各自实现了其抽象方法cost()。


图片.png

由于咖啡可以加入各种配料,
例如:蒸奶(Steamed Milk)、豆浆(Soy)、 摩卡(Mocha,也就是巧克力风味)或覆盖奶泡。

1)第一次糟糕的尝试如下...

图片.png

他们将每种咖啡与各种配料的各种搭配都设计成了类!?
图片.png

这样给维护带来了大麻烦!
如果某种饮料和配料价格变化,如果添加某种饮料或者配料,都需要更改许多个类的内容!
而且违反了多用组合,少用继承针对接口编程,而不是针对实现编程这两个设计原则!

2)然后,有人提出用实例变量和继承追踪这些调理

图片.png

代码如下

class Beverage{
    String description;
    public static float milkCost = .2f;
    public static float soyCost = .1f;
    public static float mochaCost = .3f;
    public static float whipCost = .4f;
    //各种调料的份数
    int milk,soy,mocha,whip;
    /*为调料设计set和get方法
    ....*/
    boolean hasMilk(){return milk == 0?false:true;}
    boolean hasSoy(){return milk == 0?false:true;}
    boolean hasMocha(){return milk == 0?false:true;}
    boolean hasWhip(){return milk == 0?false:true;}

    public double cost() {
        float condimentCost = 0.0f;
        if (hasMilk()) { condimentCost += milkCost; }
        if (hasSoy()) { condimentCost += soyCost; }
        if (hasMocha()) { condimentCost += mochaCost; }
        if (hasWhip()) { condimentCost += whipCost; }
        return condimentCost;
    }

如果想添加多份配料,也可以调整超类里的cost()方法。

public double cost(){
       return milk*milkCost+soy*soyCost+mocha*mochaCost+whip*whipCost;
   }

还是会存在下列问题
1.当调料价钱改变,我们要改变超类中各配料价格常量的值
2.当添加新调料,我们需要在超类中添加新的变量,需要改变超类中的cost()方法
3.而且,每一种饮料都会拥有每种调料的的实例变量,就会出现第一章中的问题。如果茶饮料里面加上摩卡就会很奇怪,尽管可以设置hasmocha() == false,但是从意义上讲还是很奇怪。

这里咱们要提出又一种设计原则
开放—关闭原则:类应该对扩展开放,对修改关闭。
这样的原则既有弹性可以应对变化,又保证了正确的代码的安全性。

我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配 新的行为。如能实现这样的目标,有什么好处呢?这样的设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。

3)用装饰者模式解决问题

通过上面两个例子,我们认识到一味继承不能解决所有问题。

这里要采用不一样的做法:我们要以饮料为主体,然后在运行时以调料来“装饰”(decorate)饮料。比方说,如果顾客想要摩卡 和奶泡深焙咖啡,那么,要做的是:
1拿一个深焙咖啡(DarkRoast)对象
2以摩卡(Mocha)对象装饰它
3 以奶泡(Whip)对象装饰它
4 调用cost()方法,将调料的价钱加上去。


图片.png

通过上面的方法,我们了解到装饰者模式的一些特点
1.装饰者和被装饰对象有相同的超类型。
2. 你可以用一个或多个装饰者包装一个对象。
3.既然装饰者和被装饰对象有相同的超类型,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象代替它。
4. 装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的。
5.对象可以在任何时候被装饰,所以可以在运行时动态地,不限量地用你喜欢的装饰者来装饰对象。
下图为装饰者模式的模型图

图片.png

运用到这家咖啡店就是下图
图片.png

代码实现

public abstract class Beverage {
    String description = "Unknown yet";
    public String getDescription(){return description;}
    public abstract double cost();
}

class DarkRoast extends Beverage {
    public DarkRoast(){description = "DarkRoast";}
    @Override
    public double cost() {
        return 1.2;
    }
}

class HouseBlend extends Beverage{
    public HouseBlend(){description = "House Blend Coffee";}
    @Override
    public double cost() {
        return 1.4;
    }
}

abstract class CondimentDecorator extends Beverage {
    public abstract String getDescription();
}

class Soy extends CondimentDecorator{
    Beverage beverage;
    public Mocha(Beverage beverage){this.beverage = beverage;}
    public String getDescription(){return beverage.getDescription()+",Soy";}
    public double cost(){return 0.1+beverage.cost();}
}

class Mocha extends CondimentDecorator{
    Beverage beverage;
    public Mocha(Beverage beverage){this.beverage = beverage;}
    public String getDescription(){return beverage.getDescription()+",mocha";}
    public double cost(){return 0.3+beverage.cost();}
}

class Whip extends CondimentDecorator{
        Beverage beverage;
    public Whip(Beverage beverage){this.beverage = beverage;}
    public String getDescription(){return beverage.getDescription()+",whip";}
    public double cost(){return 0.4+beverage.cost();}
}

class Milk extends CondimentDecorator{
    Beverage beverage;
    public Milk(Beverage beverage){this.beverage = beverage;}
    public String getDescription(){return beverage.getDescription()+",milk";}
    public double cost(){return 0.2+beverage.cost();}
}
 public class Main {
    public static void main(String[] args) {
        Beverage a = new DarkRoast();
        a = new Milk(a);
        a = new Milk(a);
        a = new Mocha(a);

        Beverage b = new HouseBlend();
        b = new Milk(b);
        b = new Mocha(b);
        b = new Whip(b);

        System.out.println(a.getDescription()+"  "+a.cost()+"$");
        System.out.println(b.getDescription()+"  "+b.cost()+"$");
    }
}

运行结果


图片.png

java.io类

图片.png

同理,输出流的设计也是雷同的。
学习装饰者模式可以帮我们更好地理解流相关的类。

你可能感兴趣的:(设计模式之——装饰者模式)