【设计模式】装饰者模式

一、前言

  接着继续品读设计模式,下面介绍装饰者模式,装饰者模式在JAVA中的应用相当的广泛,如JAVA IO框架就是装饰者模式的典型应用,当时最开始使用JAVA IO时,简直是一团雾水,不明白读文件为什么需要用到这么多类,觉得很复杂,后来看了装饰者模式之后,再来看JAVA IO,则简单多了。下面,就一起来学习装饰者模式。

二、装饰者模式定义

  定义:装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

  从定义可以看出,对象的责任与对象是分离的,责任可以动态的加载到对象上,而不是将责任具体的绑定在对象上,这样,会使得对对象的扩展更加灵活和有弹性。

  装饰者模式的典型类图如下

  说明:对类图的说明如下

  Component表示组件,每个组件可以单独使用,或者而被装饰者包起来使用。

  ConcreteComponent表示具体组件,是动态加上新行为的对象,扩展自Component。

  Decorator表示装饰者的父类(父接口),所有具体的装饰者都继承(实现)自该父类(父接口),该类可以实现一系列装饰者的行为,利于代码的复用,并且有一个Component类型的实例变量。

  ConcreteDecorator表示具体的装饰者,其可以加上新的方法,新行为是通过在旧行为前面后者后面做一些计算来添加的,其继承自Decorator。

三、示例说明

  假设如下场景,当我们去食堂吃饭的时候,首先会先点一份主食,然后再点几个配菜,然后根据你点的食物,刷饭卡结账(阿姨心算的),最后美滋滋的吃饭。在这样的一种情景下,对于如何求得主食加上配菜的总价,可以采用装饰者模式来做,非常简单和高效。

  下面是我们设计的类图。

  说明:食堂供应的主食有白米饭、面条、馒头。菜有鸡腿、鱼、白菜、土豆等等,觉得菜不够可以继续加。所有的类都是Food(食物)的子类

  3.1 v1.0

  有了类图,下面开始编码。

  Food  

package com.hust.grid.leesf.decorator;

public abstract class Food {
    protected String description = "Unknown food";
    protected int number;
    
    public abstract double price();
    public String getDescription() {
        return description;
    }
}
View Code

  Rice

package com.hust.grid.leesf.decorator;

public class Rice extends Food {    
    
    public Rice(int number) {
        this.number = number;
        description = this.number + " Rice";
    }
    
    @Override
    public String getDescription() {
        return description;
    }
    
    @Override
    public double price() {
        return number * 0.2;
    }
}
View Code

  Noodle 

package com.hust.grid.leesf.decorator;

public class Noodle extends Food {    
    
    public Noodle(int number) {
        this.number = number;
        this.description = this.number + " Noodle";
    }
    
    @Override
    public String getDescription() {
        return description;
    }
    
    @Override
    public double price() {
        return number * 3.0;
    }
}
View Code

  SteamedBread

package com.hust.grid.leesf.decorator;

public class SteamedBread extends Food {
    
    public SteamedBread(int number) {
        this.number = number;
        description = this.number + " SteamedBread";
    }
    
    @Override
    public String getDescription() {
        return description;
    }
    
    @Override
    public double price() {
        return number * 0.2;
    }
}
View Code

  Course

package com.hust.grid.leesf.decorator;

public abstract class Course extends Food {
    protected Food food;
    
    @Override
    public abstract String getDescription(); 
}
View Code

  Chicken

package com.hust.grid.leesf.decorator;

public class Chicken extends Course {
    public Chicken(Food food, int number) {
        this.food = food;
        this.number = number;
    }
    
    @Override
    public String getDescription() {
        return food.getDescription() + ", " + this.number + " Chicken";
    } 
    
    @Override
    public double price() {
        return number * 5 + food.price();
    } 
}
View Code

  Fish

package com.hust.grid.leesf.decorator;

public class Fish extends Course {
    public Fish(Food food, int number) {
        this.food = food;
        this.number = number;
    }
    
    @Override
    public String getDescription() {
        return food.getDescription() + ", " + this.number + " Fish";
    } 
    
    @Override
    public double price() {
        return number * 5 + food.price();
    } 
}
View Code

  Cabbage

package com.hust.grid.leesf.decorator;

public class Cabbage extends Course {
    public Cabbage(Food food, int number) {
        this.food = food;
        this.number = number;
    }
    
    @Override
    public String getDescription() {
        return food.getDescription() + ", " + this.number + " Cabbage";
    } 
    
    @Override
    public double price() {
        return number * 5 + food.price();
    } 
}
View Code

  Potato

package com.hust.grid.leesf.decorator;

public class Potato extends Course {
    public Potato(Food food, int number) {
        this.food = food;
        this.number = number;
    }
    
    @Override
    public String getDescription() {
        return description + ", " + this.number + " Potato";
    } 
    
    @Override
    public double price() {
        return number * 5 + food.price();
    } 
}
View Code

  Main(用作测试) 

package com.hust.grid.leesf.decorator;

public class Main {
    public static void main(String[] args) {
        Food food = new Rice(3);
        food = new Chicken(food, 1);
        food = new Fish(food, 1);
        food = new Cabbage(food, 1);
        System.out.println("description is " + food.getDescription());
        System.out.println("total price is " + food.price());        
    }
}
View Code

  运行结果

description is 3 Rice, 1 Chicken, 1 Fish, 1 Cabbage
total price is 15.6

  说明:点了3两米饭,1个鸡腿,一条鱼,一份白菜,总共花费了15.6元。

  此时,点任意的菜品和主食,都可以轻易算出总共花费多少钱。

  若此时,点了饭菜之后发现食堂已经没有地方坐了,需要打包带走,也就是需要加上一个纸碗和塑料袋,那么如何对系统进行扩展呢?

  3.2 v2.0

  下面对已有的类图进行扩展如下

  说明:新增了一个Adds类,其是纸碗和塑料袋的父类,代码如下

  Adds

package com.hust.grid.leesf.decorator;

public abstract class Adds extends Food {
    protected Food food;
    
    @Override
    public abstract String getDescription(); 
}
View Code

  PaperBowl

package com.hust.grid.leesf.decorator;

public class PaperBowl extends Adds {
    public PaperBowl(Food food, int number) {
        this.food = food;
        this.number = number;
    }
    
    @Override
    public String getDescription() {
        return food.getDescription() + ", " + this.number + " PaperBowl";
    } 
    
    @Override
    public double price() {
        return number * 0.3 + food.price();
    } 
}
View Code

  PlasticBag

package com.hust.grid.leesf.decorator;

public class PlasticBag extends Adds {
    public PlasticBag(Food food, int number) {
        this.food = food;
        this.number = number;
    }
    
    @Override
    public String getDescription() {
        return food.getDescription() + ", " + this.number + " PlasticBag";
    } 
    
    @Override
    public double price() {
        return number * 0.2 + food.price();
    } 
}
View Code

  Main(用作测试)  

package com.hust.grid.leesf.decorator;

public class Main {
    public static void main(String[] args) {
        Food food = new Rice(3);
        food = new Chicken(food, 1);
        food = new Fish(food, 1);
        food = new Cabbage(food, 1);
        food = new PaperBowl(food, 1);
        food = new PlasticBag(food, 1);
        System.out.println("description is " + food.getDescription());
        System.out.println("total price is " + food.price());        
    }
}
View Code

  运行结果

description is 3 Rice, 1 Chicken, 1 Fish, 1 Cabbage, 1 PaperBowl, 1 PlasticBag
total price is 16.1

  说明:此时,还是如上的饭菜,但是需要打包带走,所以添加了一个纸碗和塑料袋,也可以轻易的算出总共花了多少钱。

四、总结

  装饰者模式在的应用很广泛,当理解了该模式之后,分析JDK IO的源码将会变得相对简单,该模式在生活中也有很多应用,多思考,多总结,所有源代码已经上传至github,欢迎fork,谢谢各位园友的观看~ 

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