美文网首页
设计模式之装饰对象模式

设计模式之装饰对象模式

作者: 零星瓢虫 | 来源:发表于2020-07-07 23:32 被阅读0次

简介

装饰者模式,简而言之,可以针对已经存在的行为基础进行装饰。在进行装饰后达到自己想要的实现的功能。

同样,我们引入现实例子对装饰者模式进行层层分析。

现在,假如我们开了一家咖啡点,想要做一套点咖啡的系统,系统主要是根据选择的各种原料进行计算咖啡的价格;

直接进入设计代码环节吧。

public abstract  class Coffee {
    public String descriprion;
    
    public String getDescription(){
        return descriprion;
    };
    
    public abstract int  cost();
}

public class KaBuQiNuo extends Coffee{
    
    @Override
    public String getDescription() {
        // TODO Auto-generated method stub
        descriprion = "kabuqinuo";
        return super.getDescription();
    }

    @Override
    public int cost() {
        // TODO Auto-generated method stub
        return 1000;
    }

}
public class JiaoTang extends Coffee{
    
    @Override
    public String getDescription() {
        // TODO Auto-generated method stub
        descriprion = "JiaoTang ";
        return super.getDescription();
    }

    @Override
    public int cost() {
        // TODO Auto-generated method stub
        return 2000;
    }

}

上面直接定义了 Coffee 基类,同时对应不同的类似于 KaBuQiNuo(卡布奇诺)、JiaoTang 咖啡种类直接继承父类并实现相应 Description 和 cost 方法即可,当我们需要计算的时候进行 Cost 叠加。

上述写法看上去没有什么问题,但是当要对咖啡进行拓展的时候,我们需要加 YeGuo(椰果)、YanMai(燕麦) 之类的配料之后就要重新新建子类(YeGuoCoffee)并且进行实现cost方法,甚至如果我们要区分亚洲和美洲口味的 Coffee。最后组合起来子类就会越来越多,简直就是类的大集合大爆炸,这样肯定是不行的 。

本着希望不需要太多类的原则,对基类 Coffee 进行优化,抽取基本原材料:

public abstract  class Coffee {
    public String descriprion;
    
    public Milk milk;
    
    public YeGuo yeGuo;
    
    public YanMai yanmai;
    
    public String getDescription(){
        return this.descriprion;
    }
    

    public void setMiik(Milk milk){
        this.milk = milk;
    }
    
    public boolean hasMilk(){
        if(milk != null){
            return true;
        }
        return false;
    };
    
    public void setYeGuo(YeGuo yeGuo){
        this.yeGuo = yeGuo;
    }
    public boolean hasYeGuo(){
        if(yeGuo != null){
            return true;
        }
        return false;
        
    };
    

    public void setYanMai(YanMai yanmai){
        this.yanmai = yanmai;
    }
    
    public boolean hasYanMai(){
        if(yanmai != null){
            return true;
        }
        return false;
        
    };
    
    public int cost (){
        int cost = 0;
        if(hasMilk()){
            cost += 1000;
        }
        if(hasYeGuo()){
            cost += 2000;
        }
        if(hasYanMai()){
            cost += 3000;
        }
    }
    
}

对基类 Coffee 进行改造,将 Coffee 相关基本原材料放在基类中,并对应判断是否有相关原料,在子类中仅仅需要去实现 cost 在基类基础上加上新增加原材料的价格即可。


public class ChinaCoffee extends Coffee{
    
    @Override
    public String getDescription() {
        // TODO Auto-generated method stub
        descriprion = "kabuqinuo";
        return super.getDescription();
    }

    @Override
    public int cost() {
        // TODO Auto-generated method stub
            return super.cost() + 50;
    }

}

Coffee 的基本材料固定在基类中进行相关处理,而 Coffee 的种类根据各种搭配则可能进行变化。针对变化的情况。子类只需要实现相应的 cost 方法即可。

到这里,看上去这个点咖啡的系统已经不错了,如果我们新增了美式咖啡,只需要在子类对应增加相应价格。而不要对应再去关心是否有 YanMai 或者 YeGuo 等。

但是再仔细想想,如果提出下面几个问题,这么系统是否还很方便呢?
1 调整现有的 Coffee 的基本原料的价格(需要更改基类现有代码)
2 增加 Coffee 的基本原料 (需要更改基类代码)
3 假如需要推出南美口味 Coffee 新品,可能不再需要 Milk 原料了。子类却依然会继承到 hasMilk 方法。

这样看来,系统仍然不够强壮。每次修改或者增加依然会修改到现有的代码。需要继续优化,使得这个系统尽量不去修改原有代码,而对外进行扩展。

这里就涉及到设计模式的一个重要原则:

类应该对扩展开发过,对修改关闭

接下里正式进入到装饰者模式。

把装饰者模式先按照案例进行拆分:

1 先拿到一个 Coffee 的对象
2 Milk 进行装饰
3 YeGuo 进行装饰
3 依次调用 cost 方法,依赖 委托方式 将调料的价格加上去

类关系图.png

类似于上述图中,进行一层层地把 Coffee 基类进行包装,并层层调用 cost 方法,最后进行累加。

1 首先调用最外层 YeGuo 的 cost 方法。
2 YeGuo 调用 Milk 的 cost 方法。
3 Milk 调用 Coffee 的 cost 方法。
4 Coffee 返回价格。
5 Milk 在结果上加上自己的价格。
6 YeGuo 在结果上加上自己的价格,返回总价格。

分析完成步骤,接下来具体看看实现方式。

先从 Coffee 类来写:

public abstract  class Coffee {
    public String descriprion = "UnKnow Coffee";
    
    public String getDescription(){
        return descriprion;
    }
    
    public abstract double cost();
}

接下来实现一个装饰者的基类:

public abstract class SimpleDecorator extends Coffee {
    public abstract String getDescription();
}

有了基类,可以实现一些地方的 Coffee 。

public class ChinaCoffee extends Coffee {
    
    public ChinaCoffee() {
        // TODO Auto-generated constructor stub
        descriprion = "ChinaCoffee";
    }

    @Override
    public double cost() {
        // TODO Auto-generated method stub
        return 2.99;
    }

}

接下来,我们就用 Milk 装饰 Coffee。

public class Milk extends SimpleDecorator{
    Coffee coffee;
    
    public Milk(Coffee coffee) {
        this.coffee = coffee;
    }

    @Override
    public String getDescription() {
        return coffee.getDescription() + ",Milk";
    }

    @Override
    public double cost() {
        return 3.99+ coffee.cost();
    }

}

再加一个 YeGuo 类进行装饰:

public class YeGuo extends SimpleDecorator{
    Coffee coffee;
    
    public YeGuo (Coffee coffee) {
        this.coffee = coffee;
    }

    @Override
    public String getDescription() {
        return coffee.getDescription() + ",YeGuo ";
    }

    @Override
    public double cost() {
        return 4.99+ coffee.cost();
    }

}

装饰对象 Milk 实现了简单的装饰基类,同时在内部维护了 Coffee 的引用类型。这里维护的 Coffee 引用类型保证了我们的装饰效果层层进行调用。

最后我们来看看装饰者模式的实现情况。

public class Main {
    
    public static void main(String args []){
        Coffee coffee = new ChinaCoffee();
        System.out.println(coffee.getDescription()+"RMB" + coffee.cost());
        
        coffee = new Milk(coffee);
        System.out.println(coffee.getDescription()+"RMB" + coffee.cost());
        
        coffee = new YeGuo(coffee);
        System.out.println(coffee.getDescription()+"RMB" + coffee.cost());
    }

}

可以看到上述代码,先定了一杯 ChinaCoffee,然后直接用 Milk 和 YeGuo 进行装饰。 运行上述代码:

ChinaCoffee RMB2.99
ChinaCoffee,Milk RMB6.98
ChinaCoffee,Milk,YeGuo  RMB11.97

得到运行结果。

结合 UML 图再看下装饰者模式:

UML 图.png

就这样通过装饰者模式,完成了一套稳定的 Coffee 点单系统。
再回到上面提到的三个问题。
1 修改价格,直接在 ChinaCoffee 或者对应装饰原料 Milk 中修改,不必修改基类;
2 如果要新增原料,继承 SimpleDecorator 实现对应方法。
3 不必在继承不必要的方法。

同时看到,对内 cost 的修改进行了封闭,同时可以进行拓展新的品种 JapanCoffee 或者 新的原料 YanMai 等。

Java 中其实也有装饰者模式的类,例如 InputStream 和它的相关子类。具体可自行在 Java 源码中进行查看。

相关文章

  • Golang 设计模式之-装饰模式

    Golang 设计模式之-装饰模式 最近在温习设计模式,虽然面向对象不是go的特长,但用go实现的设计模式,比ja...

  • 修饰模式

    维基百科-修饰模式 php 设计模式 之 装饰模式 hero博客——装饰模式 动态的给一个对象添加一些额外的职责,...

  • 设计模式之装饰对象模式

    简介 装饰者模式,简而言之,可以针对已经存在的行为基础进行装饰。在进行装饰后达到自己想要的实现的功能。 同样,我们...

  • 设计模式之装饰器模式

    也称装饰者模式、装饰器模式、Wrapper、Decorator。 装饰模式是一种结构型设计模式,允许你通过将对象放...

  • 装饰模式

    Android进阶之设计模式 装饰模式 定义: 动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成...

  • JavaScript 设计模式核⼼原理与应⽤实践 之 结构型设计

    JavaScript 设计模式核⼼原理与应⽤实践 之 结构型设计模式 装饰器模式,又名装饰者模式。它的定义是“在不...

  • 装饰对象:装饰者模式

    装饰对象:装饰者模式   这是《Head First设计模式(中文版)》第三章的读书笔记。   装饰者模式,可以称...

  • 装饰设计模式

    装饰设计模式 装饰模式:动态的给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式好比生成子类实现更为灵活。...

  • 装饰设计模式 2016.10.9

    package 装饰设计模式; /*装饰设计模式 * 当想要对已有的对象进行功能增强时。 * 可以定义类,将已有对...

  • 装饰模式

    装饰模式属于单一职责性设计模式。在GOF的《设计模式:可复用面向对象软件的基础》一书中对装饰模式是这样说的:动态地...

网友评论

      本文标题:设计模式之装饰对象模式

      本文链接:https://www.haomeiwen.com/subject/sxrnqktx.html