装饰者模式
Decorator
装饰者模式(装饰模式)是一种结构型模式
在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能
它是通过创建一个包装对象,也就是装饰来包裹真实的对象
引入装饰者模式
项目需求: 有一家汉堡店,汉堡店里有卖汉堡和酱料,顾客可以只点汉堡也可以点汉堡+酱料,该如何设计结构
方案一
面向接口编程的思想 , 接口抽象套餐价格方法和套餐内容方法,让套餐实现类去实现方法
当汉堡或酱料很多时,不同的搭配有很多种 缺点: 会产生大量的类
方案二
将酱料充当抽象类的字段,抽象类中有设置某酱料(番茄酱,辣椒酱等…)数量的方法,汉堡则作为实现类去实现抽象类
缺点: 当要增加新酱料时,要改动抽象类,违反开闭原则
方案三
使用装饰者模式
装饰者模式角色
大话设计模式结构图
装饰者模式可以看成将一个主体一层一层包裹
比如客户要求: 鸡肉汉堡+番茄酱+辣椒酱
那么主体就是鸡肉汉堡,对酱料(包装)进行一层一层的包裹
主体被装饰者: Component, ConcreteComponent
装饰者(酱料): Decorator , ConcreteDecorator
把酱料一层一层裹在汉堡上 就是 装饰者模式
汉堡店例子使用装饰者模式
当ConcreteComponent很多都有共同点时,可以抽成一个缓冲层 (如图:很多汉堡抽成一个缓冲层,汉堡实现类去实现Hamburg抽象类)
装饰者模式代码
汉堡例子的装饰者模式代码
整体结构
Component
public abstract class Component {
//成本
private double money;
//套餐内容描述
private String description;
protected String getDescription() {
return description;
}
protected void setDescription(String description) {
this.description = description;
}
protected double getMoney() {
return money;
}
protected void setMoney(double money) {
this.money = money;
}
/**
* 价格
*/
public abstract double price();
/**
* 套餐内容描述
*/
public abstract String description();
}
ConcreteComponent
public class Hamburg extends Component {
@Override
public double cost() {
return getMoney();
}
@Override
public String description() {
return getDescription();
}
}
public class ChickenHamburg extends Hamburg{
public ChickenHamburg() {
//设置价格为5
setMoney(5);
//设置套餐内容为鸡肉汉堡
setDescription("鸡肉汉堡(5.0)");
}
}
public class BeefHamburg extends Hamburg{
public BeefHamburg() {
//设置价格为16.5
setMoney(16.5);
//设置套餐内容为牛肉汉堡
setDescription("牛肉汉堡(16.5)");
}
}
Decorator
public class Decorator extends Component{
protected Component component;
//可以通过构造器设置Component
public Decorator(Component component) {
this.component = component;
}
@Override
public double price() {
//酱料的钱 + Component的钱 (一层一层包裹的Component)
return getMoney() + component.getMoney();
}
@Override
public String description() {
//酱料套餐内容 + Component套餐内容 (一层一层包裹的Component)
return getDescription() + " + " +component.description();
}
}
ConcreteDecotator
public class ChiliSauce extends Decorator{
public ChiliSauce(Component component) {
super(component);
setMoney(2.5);
setDescription("辣椒酱");
}
}
public class TomatoSauce extends Decorator {
public TomatoSauce(Component component) {
super(component);
setMoney(1.5);
setDescription("番茄酱(1.5)");
}
}
汉堡店
public class HamburgStore {
public static void main(String[] args) {
//目标: 鸡肉汉堡加番茄酱加辣椒酱
//1.主体:鸡肉汉堡(被装饰者)
Component hamburg = new ChickenHamburg();
System.out.println("描述: " + hamburg.description());
System.out.println("成本: " + hamburg.price() + "元");
System.out.println("==================================");
//2.番茄酱 --装饰--> (鸡肉汉堡) ==形成的Component==> (鸡肉汉堡+番茄酱)
hamburg = new TomatoSauce(hamburg);
System.out.println("描述: " + hamburg.description());
System.out.println("成本: " + hamburg.price() + "元");
System.out.println("==================================");
//3.辣椒酱 --装饰--> (鸡肉汉堡+番茄酱) ==形成的Component==> (鸡肉汉堡+番茄酱+辣椒酱)
hamburg = new ChiliSauce(hamburg);
System.out.println("描述: " + hamburg.description());
System.out.println("成本: " + hamburg.price() + "元");
System.out.println("==================================");
}
}
/*
描述: 鸡肉汉堡(5.0)
成本: 5.0元
==================================
描述: 番茄酱(1.5) + 鸡肉汉堡(5.0)
成本: 6.5元
==================================
描述: 辣椒酱 + 番茄酱(1.5) + 鸡肉汉堡(5.0)
成本: 4.0元
==================================
*/
核心: Decorator继承Component且聚合Component(字段有Component),这样使得每次装饰完都是Component整体,方便继续装饰
如果要增加新汉堡只需要继承Hamburg抽象层
如果要增加新酱料只需要继承Decorator
如果要增加新食品(鸡块,鸡腿等)只需要继承Component
不会违反开闭原则
JDK中的装饰模式
IO中的装饰模式
//类比 Component
public abstract class InputStream
//FileInputStream继承自InputStream 类比ConcreteComponent(被装饰者 汉堡)
public class FileInputStream extends InputStream
//FilterInputStream继承自InputStream 并且 字段有InputStream(包含被装饰者) 类比Decorator
public class FilterInputStream extends InputStream {
protected volatile InputStream in;
//...
}
//BufferedInputStream继承自FilterInputStream 类比ConcreteDecorator(装饰者 酱料)
public class BufferedInputStream extends FilterInputStream {
//BufferInputStream 对 FileInputStream 进行装饰 (装饰包裹这个对象 类似管道包裹在另一根管道上)
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\asd.text"));
总结
与一开始的继承相比,它们都是为了扩展对象功能,但是装饰模式更加灵活且复杂
装饰模式: 动态的将新功能增加到对象上(装饰包裹这个对象)
特点
-
装饰者 和 被装饰者 拥有共同的父类(或接口) Component
-
装饰者 聚合(组合)了 被装饰者
装饰者包含被装饰者的引用
装饰者某个字段是被装饰者
-
通过不同的装饰类与被装饰类排列组合,可以有很多不同行为功能的组合
使用场景
- 需要动态的给对象增加功能还可以动态撤销功能
- 取代 例子中使用继承的基本功能的排列组合