一、前言
接着继续品读设计模式,下面介绍装饰者模式,装饰者模式在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; } }
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; } }
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; } }
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; } }
Course
package com.hust.grid.leesf.decorator; public abstract class Course extends Food { protected Food food; @Override public abstract String getDescription(); }
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(); } }
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(); } }
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(); } }
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(); } }
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()); } }
运行结果
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(); }
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(); } }
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(); } }
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()); } }
运行结果
description is 3 Rice, 1 Chicken, 1 Fish, 1 Cabbage, 1 PaperBowl, 1 PlasticBag
total price is 16.1
说明:此时,还是如上的饭菜,但是需要打包带走,所以添加了一个纸碗和塑料袋,也可以轻易的算出总共花了多少钱。
四、总结
装饰者模式在的应用很广泛,当理解了该模式之后,分析JDK IO的源码将会变得相对简单,该模式在生活中也有很多应用,多思考,多总结,所有源代码已经上传至github,欢迎fork,谢谢各位园友的观看~