装饰者模式能够动态地将责任附加到对象上, 在扩展对象功能方面比继承更加灵活, 具体来说, 装饰者模式将行为委托给相应的包装对象, 并添加上自己的对应逻辑来实现特定的功能. 装饰者模式的 UML 图如下:
首先需要有被装饰的组件接口和具体组件, 然后有装饰者对象, 由于装饰者对象需要能够代替组件, 所以要继承组件接口, 并组合组件对象来完成委托任务.
下面以一个简单的快餐店为例子来介绍装饰者模式的用法. 快餐店会有炒面, 炒饭这些快餐, 可以额外附加鸡蛋, 火腿, 培根这些配菜, 当然加配菜需要额外加钱, 每个配菜的价钱通常不太一样. 按照上面的 UML 类图, 先定义组件接口 (快餐) 和具体组件(炒饭, 炒面).
- // 快餐接口
- public interface FastFood {
- float getCost(); // 获取价格
- String getDescription(); // 获取描述
- }
- // 炒饭
- public class FriedRice implements FastFood{
- private float price = 5;
- String description = "炒饭";
- @Override
- public float getCost() {
- return this.price;
- }
- @Override
- public String getDescription() {
- return this.description;
- }
- }
- // 炒面
- public class FriedNoodles implements FastFood{
- private float price = 5;
- String description = "炒面";
- @Override
- public float getCost() {
- return this.price;
- }
- @Override
- public String getDescription() {
- return this.description;
- }
- }
接下来是配菜相关接口和类:
- // 配菜
- public interface Garnish extends FastFood{
- String getDescription();
- }
- // 鸡蛋
- public class Egg implements Garnish{
- float price = 1.5f;
- String description = "鸡蛋";
- private FastFood fastFood;
- public Egg(FastFood fastFood){
- this.fastFood = fastFood;
- }
- @Override
- public float getCost() {
- return this.price + fastFood.getCost();
- }
- @Override
- public String getDescription() {
- return this.description + fastFood.getDescription();
- }
- }
- // 培根
- public class Bacon implements Garnish{
- private float price = 2f;
- private String description = "培根";
- private FastFood fastFood;
- public Bacon(FastFood fastFood){
- this.fastFood = fastFood;
- }
- @Override
- public float getCost() {
- return this.price + fastFood.getCost();
- }
- @Override
- public String getDescription() {
- return this.description + fastFood.getDescription();
- }
- }
- // 火腿
- public class Ham implements Garnish{
- float price = 1f;
- String description = "火腿";
- private FastFood fastFood;
- public Ham(FastFood fastFood){
- this.fastFood = fastFood;
- }
- @Override
- public float getCost() {
- return this.price + fastFood.getCost();
- }
- @Override
- public String getDescription() {
- return this.description + fastFood.getDescription();
- }
- }
接下来就可以点几份快餐测试一下代码了:
- public class DecoratorPatternTest {
- public static void main(String[] args){
- FastFood friedRice = new FriedRice();
- System.out.println(friedRice.getDescription() + "" + friedRice.getCost() +" 元 ");
- FastFood friedNoodles = new FriedNoodles();
- friedNoodles = new Egg(friedNoodles);
- friedNoodles = new Ham(friedNoodles);
- System.out.println(friedNoodles.getDescription() + "" + friedNoodles.getCost() +" 元 ");
- friedRice = new Bacon(friedRice);
- System.out.println(friedRice.getDescription() + "" + friedRice.getCost() +" 元 ");
- }
- }
输出如下:
炒饭 5.0 元
火腿鸡蛋炒面 7.5 元
培根炒饭 7.0 元
现在来回顾一下上面的类结构, 我们在点培根炒饭时, 先创建一个炒饭对象, 然后用培根对象把炒饭包装了一下, 当计算价格以及输出描述时, 我们的做法是获取装饰者 (培根) 对象的价格和描述, 同时需要获取组件 (炒饭) 的价格我描述, 我们将这项任务委托给组件来完成, 火腿鸡蛋炒面也是同样的道理. 装饰者继承组件, 使得装饰者可以包装任意的具体组件, 同样也可以包装装饰者; 同时, 装饰者也可以加入自己的逻辑, 给组件增添不一样的行为, 例如这里在技术价格以及获取描述时, 除了返回装饰者自己的属性, 还增加了返回组件属性的逻辑.
其实装饰者模式在 Java API 中使用的很多, 举个很简单的例子, 当我们需要读取文件时, 很可能会写出下面的代码:
1 BufferedReader bf = new BufferedReader(new FileReader(new File("./test.txt")));
其中 BufferedReader 和 FileReader 就充当了装饰者的作用, 而 File 则是被装饰的组件, 还有一些其他的 API 也用到了装饰者模式, 需要大家自己去摸索了.
参考文献:
<<Head First 设计模式>>
装饰者模式
来源: http://www.bubuko.com/infodetail-3217640.html