一 场景
在开发中,有时候的需求会要求我们在某个对象原有的基础上添加一些属性或者行为,这时候我们可以考虑直接在类里面添加(违背类开-闭原则),或者通过继成添加(如果多次就需要添加多个类,维护起来很困难),这时候就可以考虑使用装饰者模式。
二 定义
装饰者模式概念:
动态的给对象添加一些额外的属性或行为。相比于使用继承,装饰者模式更加灵活。
装饰者模式核心模块:
- Component(抽象构件)
它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。 - ConcreteComponent(具体构件)
它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。 - Decorator(抽象装饰类)
它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。 - ConcreteDecorator(具体装饰类)
它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。
三 实例
我们给一个甜品店做一个订单系统
一共两种商品:蛋糕 ,面包
可在这四种蛋糕的基础上添加各种水果:香蕉,草莓
添加其它装饰:奶油
uml类图:
代码 :
GoodsComponent 这是一个抽象构件,他定义了具体的构件对象
package headFirst.decoratorPattern;
/**
* @author zhaokai008@ke.com
* @date 2019-07-03 23:57
*/
public interface GoodsComponent {
double cost();
String getDescription();
}
CakeConcreteComponent 这是一个cake的定义具体的构件对象
package headFirst.decoratorPattern;
/**
* @author zhaokai008@ke.com
* @date 2019-07-04 00:00
*/
public class CakeConcreteComponent implements GoodsComponent {
public double cost() {
return 10;
}
public String getDescription() {
return "cake";
}
}
BreadConcreteComponent:这是一个Bread的定义具体的构件对象
package headFirst.decoratorPattern;
/**
* @author zhaokai008@ke.com
* @date 2019-07-04 00:04
*/
public class BreadConcreteComponent implements GoodsComponent {
public double cost() {
return 5;
}
public String getDescription() {
return "bread";
}
}
Decorator:抽象装饰类,新加了个unitPrice 单价的装饰方法
package headFirst.decoratorPattern;
/**
* @author zhaokai008@ke.com
* @date 2019-07-04 00:08
*/
public abstract class Decorator implements GoodsComponent {
public abstract double cost() ;
public abstract String getDescription() ;
public abstract double unitPrice();
}
MilkDecorator:具体装饰类
package headFirst.decoratorPattern;
/**
* @author zhaokai008@ke.com
* @date 2019-07-04 00:12
*/
public class MilkDecorator extends Decorator {
public MilkDecorator(GoodsComponent goodsComponent) {
this.goodsComponent = goodsComponent;
}
private GoodsComponent goodsComponent;
public double cost() {
return goodsComponent.cost() + 10;
}
public String getDescription() {
return goodsComponent.getDescription() + " add milk";
}
public double unitPrice() {
return 10;
}
}
StrawberryDecorator:具体装饰类
package headFirst.decoratorPattern;
/**
* @author zhaokai008@ke.com
* @date 2019-07-04 00:11
*/
public class StrawberryDecorator extends Decorator {
private GoodsComponent goodsComponent;
public StrawberryDecorator(GoodsComponent goodsComponent) {
this.goodsComponent = goodsComponent;
}
public double cost() {
return goodsComponent.cost() + 15;
}
public String getDescription() {
return getDescription() + " add Strawberry";
}
public double unitPrice() {
return 15;
}
}
BananaDecorator:具体装饰类
package headFirst.decoratorPattern;
/**
* @author zhaokai008@ke.com
* @date 2019-07-04 00:06
*/
public class BananaDecorator extends Decorator {
GoodsComponent goodsComponent;
public BananaDecorator(GoodsComponent goodsComponent) {
this.goodsComponent = goodsComponent;
}
public double cost() {
return goodsComponent.cost() + 12;
}
public String getDescription() {
return goodsComponent.getDescription() + " add Banana";
}
public double unitPrice() {
return 12;
}
}
test:测试类,加两根香蕉和奶油的蛋糕多少钱
package headFirst.decoratorPattern;
/**
* @author zhaokai008@ke.com
* @date 2019-07-04 00:24
*/
public class Test {
public static void main(String [] args){
GoodsComponent goodsComponent = new CakeConcreteComponent();
goodsComponent = new BananaDecorator(goodsComponent);
goodsComponent = new BananaDecorator(goodsComponent);
goodsComponent = new MilkDecorator(goodsComponent);
System.out.println(goodsComponent.getDescription() + "cost: " +goodsComponent.cost());
}
}
//--------------input
cake add Banana add Banana add milkcost: 44.0
四 总结
相信从上面大家也能看出装饰者模式的一些优缺点:
优点
- 比继成更加灵活
- 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,得到功能更为强大的对象。
- 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”。
缺点 - 实例化组件时,增加了代码的复杂度,而且不易了解。(这个问题工厂模式和生成器模式会有解决办法,一般装饰者模式都不是单独使用的)
tips
java原生的io库用到了装饰者模式,感兴趣的可以去学习一下。
网友评论