装饰模式(Decorate Pattern)

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

看下下面的例子,总共有两种咖啡:Decaf、Espresso,另有两种调味品:Mocha、Whip(3种设计的主要差别在于抽象方式不同)

设计一:

装饰模式(Decorate Pattern)_第1张图片

即使添加再多的调味品,咖啡依然是咖啡,在抽象的过程中并没有考虑咖啡和调味品之间的关系

当咖啡和调味品的种类很多时,将会产生大量的类,如果一种咖啡的价格发生变动,需要找到所有相关的类逐一修改

设计二:

装饰模式(Decorate Pattern)_第2张图片

将调味品作为Coffee类的属性,比起设计一,类的数量大大减少,相应的,程序结构也更加清晰

public class Coffee {
	private boolean mocha;
	private boolean whip;
	
	public double cost(){
		double price = 0d;
		if(mocha){
			price += 0.5;
		}
		if(whip){
			price += 0.1;
		}
		return price;
	}
	
	public void addMocha(){
		this.mocha = true;
	}
	
	public void addWhip(){
		this.whip = true;
	}
}
public class Decaf extends Coffee{
	public double cost(){
		double price = super.cost();
		price += 2.0;
		return price;
	}
}
public class Espresso extends Coffee {
	public double cost(){
		double price = super.cost();
		price += 2.5;
		return price;
	}
}

测试一下:

public class Test {
	public static void main(String[] args) {
		Coffee coffee = new Decaf();
		coffee.addMocha();
		coffee.addWhip();
		//2.6
		System.out.println(coffee.cost());
	}
}

考虑到下面几个问题,设计二有明显的不足:

1,如果调味品的种类较多,Coffee类将会变得相当庞大,难以维护

2,类本身不够灵活,无法处理顾客希望添加双倍的Mocha的场景

3,添加一种新的咖啡IceCoffee,如果IceCoffee不能加Mocha,由于IceCoffee类继承自Coffee类,IceCoffee类依然从父类继承了addMocha()方法,这就需要在IceCoffee类中重写一个空的addMocha()方法

设计三:装饰模式

装饰模式分为3个部分:

1,抽象组件Component:对应Coffee类

2,具体组件ConcreteComponent:对应具体的咖啡,如:Decaf,Espresso

3,装饰者Decorator:对应调味品,如:Mocha,Whip

装饰模式有3个特点:

1,具体组件和装饰者都继承自抽象组件(Decaf、Espresson、Mocha和Whip都继承自Coffee),并且装饰者持有抽象组件的引用

2,可以使用装饰者组合具体组件创造出新的类(Mocha组合Decaf创造出MochaDecaf)

3,过程2可以重复,直到创造出需要的类

使用装饰模式,想要获得一个WhipDoubleMochaEspresso是很容易的:

装饰模式(Decorate Pattern)_第3张图片

public interface Coffee {
	public double cost();
}
public class Espresso implements Coffee {
	public double cost(){
		return 2.5;
	}
}
public class Decorator implements Coffee {
	private Coffee coffee;
	
	public Decorator(Coffee coffee){
		this.coffee = coffee;
	}
	
	public double cost(){
		return coffee.cost();
	}
}
public class Whip extends Decorator {
	public Whip(Coffee coffee){
		super(coffee);
	}
	
	public double cost(){
		return super.cost() + 0.1;
	}
}
public class Mocha extends Decorator {
	public Mocha(Coffee coffee){
		super(coffee);
	}
	
	public double cost(){
		return super.cost() + 0.5;
	}
}

测试一下:

public class Test {
	public static void main(String[] args) {
		Coffee coffee = new Mocha(new Mocha(new Whip(new Espresso)));
		//3.6(0.5 + 0.5 + 0.1 + 2.5)
		System.out.println(coffee.cost());
	}
}

看到这里是不是想到了Java中很常用的InputStream和OutputStream类?没错,它们都是使用装饰模式设计的

当然设计的时候可以灵活一些,如果只有固定一种Coffee,可以设计成下面的结构

装饰模式(Decorate Pattern)_第4张图片

如果只有一种调味品,下面的设计方式也是可以的

装饰模式(Decorate Pattern)_第5张图片


装饰模式的缺点:

1,装饰模式虽然扩展性较高,但是没有设计二简洁,类的数量略多(比设计一少很多),如何取舍可扩展性和简洁性是个问题。如果需求比较明确,并且后期发生变化的概率不大,没必要直接使用装饰模式,设计二更快速高效

2,很难搞清楚一个类究竟被装饰了多少层,可能是1层,也可能是100层

3,在某些场景下,可能需要按照一定的顺序进行装饰,稍不注意,就会产生异常

装饰模式与建造者模式之间的区别:

装饰模式的构造过程是不稳定的(是否需要装饰,装饰多少层都是可以自由调整的),建造者模式的建造过程是稳定的

你可能感兴趣的:(Design,Pattern,可扩展性与设计模式)