装饰者模式,从吃黄焖鸡开始提及

黄焖鸡米饭最热卖的外卖之一,国人都喜欢吃,吃过黄焖鸡米饭的应该都知道,除了黄焖鸡米饭主体外,还能够添加各类配菜,如土豆、香菇、鹌鹑蛋、青菜等。若是须要你来设计一套黄焖鸡米饭结帐系统,你该如何设计呢?java

前置条件:主体:黄焖鸡米饭 价格:16,配菜:土豆 价格:二、香菇 价格:二、鹌鹑蛋 价格:二、青菜 价格:1.5git

这还不简单?看个人,你随手就来了下面这段代码。github

public class HuangMenJiMiFan {
    // 黄焖鸡价格
    private double huangMenJiPrice = 16D;
    // 土豆价格
    private double potatoPrice = 2D;
    // 鹌鹑蛋价格
    private double eggPrice = 2D;
    // 香菇价格
    private double mushroomPrice = 2D;
    // 青菜价格
    private double vegPrice = 1.5D;
    // 总价格
    private double totalPrice = 0D;
    // 订单描述
    private StringBuilder desc = new StringBuilder("黄焖鸡米饭 ");

    // 是否加土豆
    private boolean hasPotato = false;
    // 是否加鹌鹑蛋
    private boolean hasEgg = false;
    // 是否加香菇
    private boolean hasMushroom = false;
    // 是否加蔬菜
    private boolean hasVeg = false;

    public HuangMenJiMiFan(){
        this.totalPrice = this.huangMenJiPrice;
    }

    public void setHasPotato(boolean hasPotato) {
        this.hasPotato = hasPotato;
    }

    public void setHasEgg(boolean hasEgg) {
        this.hasEgg = hasEgg;
    }

    public void setHasMushroom(boolean hasMushroom) {
        this.hasMushroom = hasMushroom;
    }

    public void setHasVeg(boolean hasVeg) {
        this.hasVeg = hasVeg;
    }

    public String getDesc(){
        if (hasEgg){
            this.desc.append("+ 一份鹌鹑蛋 ");
        }
        if (hasMushroom){
            this.desc.append("+ 一份香菇 ");
        }
        if (hasPotato){
            this.desc.append("+ 一份土豆 ");
        }
        if (hasVeg){
            this.desc.append("+ 一份蔬菜 ");
        }
        return desc.toString();
    }

    public double cost(){
        if (hasEgg){
            this.totalPrice +=this.eggPrice;
        }
        if (hasMushroom){
            this.totalPrice +=this.mushroomPrice;
        }
        if (hasPotato){
            this.totalPrice +=this.potatoPrice;
        }
        if (hasVeg){
            this.totalPrice +=this.vegPrice;
        }
        return totalPrice;
    }
}
复制代码

只要在点黄焖鸡米饭的时候,把添加的配菜设置成true就好,这段代码确实解决了黄焖鸡米饭结算问题。可是我须要加两份土豆呢?我须要添加一种新配菜呢?或者我新增一个黄焖排骨呢?这时候实现起来就须要去改动原来的代码,这违背了设计模式的开放-关闭原则设计模式

开放-关闭原则:类应该对扩展开放,对修改关闭微信

上面的设计违背了开放-关闭原则,为了不这个问题,采用装饰者模式彷佛是一种可行的解决办法。app

装饰者模式:动态的给一个对象添加一些额外的职责,就增长功能来讲,装饰模式比生成子类更为灵活。ide

装饰者模式的通用类图以下: 学习

装饰者模式的通用类图
从类图中,咱们能够看出装饰者模式有四种角色:

  • Component:核心抽象类,装饰者和被装饰者都须要继承这个抽象类
  • ConcreteComponent:对装饰的对象,该类必须继承Component
  • Decorator:装饰者抽象类,抽象出具体装饰者须要装饰的接口
  • ConcreteDecorator:具体的装饰者,该类必须继承Decorator类,而且里面有一个变量指向Component抽象类

装饰者模式的核心概念咱们都知道了,那就来实现一把,用装饰者模式来设计黄焖鸡米饭的结帐系统。测试

Component类的设计,仔细想一想,无论黄焖鸡米饭仍是配菜都会涉及到金额计算。因此咱们把该方法抽象到Component类。来设计咱们黄焖鸡米饭结帐系统的Component类,咱们取名叫作Food,Food类的具体设计以下:ui

/** * 核心抽象类 */
public abstract class Food {

    String desc = "食物描述";

    public String getDesc() {
        return this.desc;
    }
    // 价格计算
    public abstract double cost();
}
复制代码

ConcreteComponent类是咱们具体的被装饰对象,咱们这里的装饰对象是黄焖鸡米饭,咱们来设计咱们黄焖鸡米饭的被装饰对象Rice类,Rice类的具体实现以下:

/** * 被装饰者-黄焖鸡米饭 */
public class Rice extends Food{
    public Rice(){
        this.desc ="黄焖鸡米饭";
    }
    @Override
    public double cost() {
        // 黄焖鸡米饭的价格
        return 16D;
    }
}
复制代码

Decorator类是装饰者的抽象类,咱们须要定义一个getDesc()的抽象接口,由于在Food类中,getDesc()不是抽象的,在后面的具体装饰者中,须要重写getDesc()类,因此咱们须要将抽象在装饰者这一层。咱们来设计黄焖鸡米饭结帐系统的装饰者抽象类FoodDecoratorFoodDecorator类的具体设计以下:

public abstract class FoodDecorator extends Food {
    // 获取描述
    public abstract String getDesc();
}
复制代码

ConcreteDecorator类是具体的装饰者,咱们有四个具体的装饰者,分别是土豆、香菇、鹌鹑蛋、青菜,具体的装饰者须要作的事情是计算出被装饰者装饰完装饰品后的总价格和更新商品的描述。四个具体装饰者的设计以下:

public class Egg extends FoodDecorator {
    String desc = "鸡蛋";
    // 存放Component对象,该对象多是被装饰后的
    Food food;

    public Egg(Food food){
        this.food = food;
    }

    // 计算总价 当前Component对象的价格加上当前装饰者的价格
    @Override
    public double cost() {
        return food.cost() + 2D;
    }
    @Override
    public String getDesc() {
        return food.getDesc()+" + "+this.desc;
    }
}

复制代码
public class Mushroom extends FoodDecorator {
    String desc = "香菇";
    Food food;

    public Mushroom(Food food){
        this.food = food;
    }
    // 计算总价
    @Override
    public double cost() {
        return food.cost() + 2D;
    }
    @Override
    public String getDesc() {
        return food.getDesc()+" + "+this.desc;
    }
}
复制代码
public class Potato extends FoodDecorator {
    String desc = "土豆";
    Food food;

    public Potato(Food food){
        this.food = food;
    }
    // 计算总价
    @Override
    public double cost() {
        return food.cost() + 2D;
    }
    @Override
    public String getDesc() {
        return food.getDesc()+" + "+this.desc;
    }
}
复制代码
public class Veg extends FoodDecorator {
    String desc = "蔬菜";
    Food food;
    public Veg(Food food){
        this.food = food;
    }
    // 计算总价
    @Override
    public double cost() {
        return food.cost() + 1.5D;
    }
    @Override
    public String getDesc() {
        return food.getDesc()+" + "+this.desc;
    }
}
复制代码

装饰者的全部角色都实现完了,咱们来测试一下使用装饰者模式以后的黄焖鸡结帐系统,编写一个App测试类。

public class App {
    public static void main(String[] args) {
        // 点一份米饭
        Rice rice = new Rice();
        // 加个鸡蛋
        Egg egg = new Egg(rice);
        // 在加土豆
        Potato potato = new Potato(egg);
        // 再加一份白菜
        Veg veg = new Veg(potato);
        System.out.println(veg.getDesc());
        System.out.println(veg.cost());
    }
}
复制代码

测试结果

咱们的描述和金额都是正确的,可能你仍是没怎么明白装饰者模式,一块儿来看看咱们的黄焖鸡米饭被装饰后的示意图:

咱们的黄焖鸡米饭共有三层装饰,第一层是鸡蛋,第二层是土豆,第三层是蔬菜。咱们在最后调用价格计算和商品描述都是调用了最外层的装饰者的方法,有点像递归同样,每一层的装饰者都有被前一个装饰者装饰后的黄焖鸡米饭对象。里面会产生想递归同样的调用。但愿看完这张图以后,对你理解装饰者模式有帮助。

使用装饰者模式以后的黄焖鸡米饭结帐系统,在新增配菜或者产品时,咱们不须要修改原先的功能,只须要对类进行扩展就行了,这彻底遵循了开放-关闭原则

装饰者模式的优势

  • 装饰类和被装饰类能够独立发展,而不会互相耦合,换句话说,就是Component类无须知道Decorator类,Decorator类也不用知道具体的被装饰者。
  • 装饰者模式是继承关系的一个替代方案,从上面的黄焖鸡米饭的案例中,咱们能够看出,无论装饰多少层,返回的对象仍是Component
  • 装饰者模式能够动态的扩展一个实现类的功能

装饰者模式的优势

  • 多层装饰模式比较复杂,你能够想象一下剥洋葱,若是最里面的装饰出了问题,你的工做量会有多大?

最后多说一句,JDK 中的 java.io 就是用装饰者模式实现的,有兴趣的能够去深刻了解一下。

源代码

文章不足之处,望你们多多指点,共同窗习,共同进步

最后

打个小广告,欢迎扫码关注微信公众号:「平头哥的技术博文」,一块儿进步吧。

平头哥的技术博文
相关文章
相关标签/搜索