【设计模式】装饰者模式

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

一、前言


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


二、装饰者模式定义


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


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


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

download.png

  说明:对类图的说明如下


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


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


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


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


三、示例说明


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


  下面是我们设计的类图。

download.png

 说明:食堂供应的主食有白米饭、面条、馒头。菜有鸡腿、鱼、白菜、土豆等等,觉得菜不够可以继续加。所有的类都是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


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

download.png

 说明:新增了一个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,谢谢各位园友的观看~ 

目录
相关文章
|
4月前
|
设计模式 Java
Java设计模式【十】:装饰者模式
Java设计模式【十】:装饰者模式
40 0
|
3月前
|
设计模式 Java API
程序技术好文:设计模式:装饰者模式
程序技术好文:设计模式:装饰者模式
19 0
|
4月前
|
设计模式 Java
【设计模式系列笔记】装饰者模式
装饰者模式是一种结构型设计模式,它允许你通过将对象放入包含行为的特殊封装类中来为原始对象添加新的行为。这种模式可以动态地将责任附加到对象上,而不影响其它对象。
59 11
|
4月前
|
设计模式 缓存 安全
设计模式-代理模式(静态代理、动态代理、cglib代理)、代理模式和装饰者模式的区别
设计模式-代理模式(静态代理、动态代理、cglib代理)、代理模式和装饰者模式的区别
|
4月前
|
设计模式
【设计模式】装饰者模式
【设计模式】装饰者模式
|
设计模式
23种设计模式_MODE08装饰者模式_手写代码实现
23种设计模式_MODE08装饰者模式_手写代码实现
|
4月前
|
设计模式 Go 开发工具
Golang设计模式——22装饰者模式
Golang设计模式——22装饰者模式
45 0
|
4月前
|
设计模式 Java
根据真实业务场景去实现一下设计模式中的装饰者模式
根据真实业务场景去实现一下设计模式中的装饰者模式
34 0
|
4月前
|
设计模式 人工智能 移动开发
认真学习设计模式之装饰者模式(Decorator Pattern)
写在前言 利用继承设计子类的行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。然而如果能够利用组合的做法扩展对象的行为,就可以在运行时动态地进行扩展。通过动态地组合对象,可以写新的代码添加新功能,而无须修改现有代码。既然没有改变现有代码,那么引进bug或产生意外副作用的机会将大幅度减少。
41 0
|
12月前
|
设计模式
设计模式~~~装饰者模式
设计模式~~~装饰者模式
29 0