概述
说到装饰者设计模式,大家并不陌生。在jdk源码中的 IO框架大量使用了装饰者设计模式,所以学习好装饰者设计模式对我们理解java 的 io框架有很大的帮助。
1、定义
装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
1.2、装饰者模式UML图
1.3、 装饰者模式具备的特点
- 装饰者和被装饰对象有相同的超类型。
- 你可以用一个或多个装饰者包装一个对象。
- 既然装饰者和被装饰对象有相同的超类型,所以在任何需要原始对象(被包装的)的场合,
可以用装饰过的对象代替它。 - 装饰者可以在所委托被装饰者的行为之前与 / 或之后,加上自己的行为,以达到特定的目的。
- 对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰
对象。
1.4、 大白话解释装饰者模式
- 在不改变接口的前提下,动态扩展对象的访问。
- 动态继承,让类具有在运行期改变行为的能力。
- 装饰模式,突出的是运行期增加行为,这和继承是不同的,继承是在编译期增加行为。因为具体的执行扩展能力我们动态添加的,而不是像继承那样,在编译的时候就已经完成。这样如果我们需要修改或者增加功能,必然需要修改继承代码。违反了设计模式的基本设计原则。装饰者模式动态的将责任附加到对象上,若要扩展功能,装饰者提供了比继承更具有弹性的替代方案
- 装饰者模式很好的满足了设计模式中的开闭原则。
- 我们从类结构图中可以看出,装饰者实例Decorator 中会有Component的一个引用。这里主要是满足了设计模式中所提倡的多用组合,少用继承的理念。
2、 入门案例
入门案例我们就采用《Head First 设计模式》中的星巴兹咖啡馆。比如咖啡店有HouseBlend(混合咖啡)和darkRoast(深焙咖啡)等原始咖啡。客户可以根据自己的喜好,在这些原始口味的上面加上自己喜欢的调料。这里比如有牛奶,摩卡(mocha),Milk(牛奶)等…。
在此基础上,我们抽象说所有咖啡的接口:Beverage,调味品装饰类:CondimentDecorator,以及他们的实现类等,整个类的基础图如下:
2.1、 代码实现
- 抽象构件类(Component):Beverage 接口
public abstract class Beverage {
// 描述咖啡详情
String description = "咖啡的描述";
public String getDescription(){
return description;
}
// 获取当前咖啡的价格
public abstract double cost();
}
- 被修饰者:DarkRoast (深焙咖啡)
public class DarkRoast extends Beverage{
public DarkRoast(){
description = "深焙咖啡";
}
@Override
public double cost() {
return 1.0;
}
}
- 被修饰者:HouseBlend(混合咖啡)
public class HouseBlend extends Beverage{
public HouseBlend() {
description = "混合咖啡";
}
@Override
public double cost() {
return .89;
}
}
- 装饰者类(Descorator):ConimentDecorator
public abstract class CondimentDecorator extends Beverage{
public abstract String getDescription();
}
- 具体装饰者类(ConcreteDescorator):Mocha(摩卡)
public class Mocha extends CondimentDecorator{
Beverage beverage;
public Mocha(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ",摩卡";
}
@Override
public double cost() {
return beverage.cost() + .20;
}
}
- 具体装饰者类(ConcreteDescorator):Milk(牛奶)
public class Milk extends CondimentDecorator{
Beverage beverage;
public Milk(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + " ,牛奶";
}
@Override
public double cost() {
return beverage.cost() + .10;
}
}
7.客户端(Client)
public class StarbuzzCoffee {
public static void main(String args[]) {
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Milk(beverage2);
System.out.println(beverage2.getDescription()
+ " $" + beverage2.cost());
Beverage beverage3 = new HouseBlend();
beverage3 = new Milk(beverage3);
beverage3 = new Milk(beverage3);
beverage3 = new Mocha(beverage3);
System.out.println(beverage3.getDescription()
+ " $" + beverage3.cost());
}
}
3、装饰者模式在Java IO 流框架中的实现
3.1 、 IO流中InputStream的UML继承结构图
从继承结构图中,我们可以发现ObjectInputStream,ByteArrayInputStream,FileInputStream是被修饰者。FilterInputStream是装饰者类。DataInputStream,BufferInputStream是装饰者类的具体实例。具体的IO源码分析我们会在Netty源码分析的章节中进行详细分析部分案例类。
4、面试提问:装饰者和代理模式的区别
其实装饰者模式和代理模式在类的继承结构图上几乎是一样的。但是两个的侧重点是不同的。装饰者模式侧重点是动态的增加类行为。而代理模式更多的是控制具体对象实例的访问。