概念描述
装饰器模式是一种结构型模式,它是对现有对象的一个包装,然后在包装类中加入新的功能。
场景描述
考虑这样一个场景:咖啡店中目前有咖啡,比如espresso,americano,decaf,客人根据自己的喜好,可以选择加糖,加奶或者两者都加,也可以什么都不加,当然,根据加入原料的不同价格也不相同。
设计一:
以直观的想法去设计这样一个结构,应该是
Coffee.java
public class Coffee {
private String description;
private double cost;
public Coffee(String description, double cost) {
this.description = description;
this.cost = cost;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public double getCost() {
return cost;
}
public void setCost(double cost) {
this.cost = cost;
}
}
Americano.java
public class Americano extends Coffee{
public Americano(){
super("americano",12);
}
}
SugarAmericano.java
public class SugarAmericano extends Coffee{
public SugarAmericano(){
super("sugarAmericano",15);
}
}
上面的结构一个很明显的缺陷是,咖啡和配料有很多种组合,需要将每一种组合都定义为一个类,这样的结构会变的庞大而不好维护。
设计二:
第二种改进的方式,可以将配料定义在超类中,在子类中通过条件判断语句,来组成不同的组合
Coffee.java
public class Coffee {
private String description;
private double cost;
private boolean sugar;
private boolean milk;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public double getCost() {
return cost;
}
public void setCost(double cost) {
this.cost = cost;
}
public boolean isSugar() {
return sugar;
}
public void setSugar(boolean sugar) {
this.sugar = sugar;
}
public boolean isMilk() {
return milk;
}
public void setMilk(boolean milk) {
this.milk = milk;
}
}
Americano.java
public class Americano extends Coffee {
public Americano(){
super();
}
@Override
public String getDescription() {
StringBuffer desc= new StringBuffer();
if(isSugar()){
desc.append("add sugar,");
}
if(isMilk()){
desc.append("add milk ,");
}
desc.append("americano");
return desc.toString();
}
@Override
public double getCost() {
double cost= 3.0;
if(isMilk()){
cost+=1;
}
if(isSugar()){
cost+=3;
}
return cost;
}
}
TestApp.java
public class TestApp {
public static void main(String[] args) {
Coffee coffee = new Americano();
coffee.setMilk(true);
System.out.println(coffee.getDescription());
System.out.println(coffee.getCost());
coffee.setSugar(true);
System.out.println(coffee.getDescription());
System.out.println(coffee.getCost());
}
}
设计二中的结构比第一种结构简单很多,但是如果新增一种配料,父类和子类都需要做相应的修改,这违反了开放封闭原则。
设计三:
使用装饰器设计模式后,项目结构调整为:
从图中可以看出,CoffeeDecorator是一个抽象装饰器,需要继承和引用抽象主体类。因为装饰器的本体仍然是Coffee
Coffee.java
public abstract class Coffee {
private String description;
private double cost;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public double getCost(){
return this.cost;
}
public void setCost(double cost) {
this.cost = cost;
}
}
//具体的主体Americano
public class Americano extends Coffee {
public Americano(){
super();
super.setCost(3);
super.setDescription("americano");
}
}
//具体的主体
public class Espresso extends Coffee {
public Espresso(){
super();
super.setCost(14);
super.setDescription("espresso");
}
}
//抽象装饰器
public abstract class CoffeeDecorator extends Coffee {
private Coffee coffee;
public CoffeeDecorator(Coffee coffee){
this.coffee = coffee;
}
public double getCost(){
return super.getCost()+coffee.getCost();
}
public String getDescription(){
return super.getDescription()+","+coffee.getDescription();
}
}
//具体装饰器
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee){
super(coffee);
super.setCost(5);
super.setDescription("add milk");
}
}
public class SugarDecorator extends CoffeeDecorator{
public SugarDecorator(Coffee coffee){
super(coffee);
super.setCost(2);
super.setDescription(" add sugar");
}
}
//TestApp.java
public class TestApp {
public static void main(String[] args) {
//单品
Coffee americano = new Americano();
System.out.println("americano:"+americano.getCost());
System.out.println("americano:"+americano.getDescription());
//加入糖
americano = new SugarDecorator(americano);
System.out.println("sugar americano:"+americano.getCost());
System.out.println("sugar americano:"+americano.getDescription());
//再加入牛奶
americano = new MilkDecorator(americano);
System.out.println("sugar milk americano:"+americano.getCost());
System.out.println("sugar milk americano:"+americano.getDescription());
}
}
java中的装饰器:
Java中的流是典型的装饰器模式,以输入流为例,FileInputStream是具体的主体,InputStream是抽象主体
同样的具体实体还有ByteArrayInputStream和StringBufferInputStream 。
输入流的抽象装饰器是FilterInputStream ,具体装饰器如BufferedInputStream,DataInputStream等
注意点
软件设计的原则:对修改封闭,对扩展开放
装饰器的优点:可以动态扩展一个对象的功能,比如咖啡Americano单品, 可以加糖,可以加奶,可以两者都加,这个过程,可以在运行时决定如何自由组合。
缺点:多层继承,结构稍复杂。
代码实例在https://github.com/jxl198/designPattern/tree/master/decorator