大话设计模式之装饰者模式

大话设计模式之装饰者模式

一.概述

通过阅读本篇文章,可以给喜欢使用继承的开发人员提供一种新的思路。我们将会了解滥用继承带来的不良后果,同时也会介绍比继承更合理的实现方式。

利用继承设计子类的行为,是在编译时期静态决定的,而且所有的子类都会继承到相同的行为。然而,如果能够利用组合的做法扩展对象的行为,就可以在运行时动态地进行扩展。

二.走进装饰者模式

           假设现在有一家知名的咖啡店,因为扩展速度太快,希望你能帮他们重新设计一套饮料销售系统,满足当下的需求。

           原先的类设计结构图如2-1所示

           大话设计模式之装饰者模式_第1张图片

(2-1)

           顾客在购买饮料时,通常会加入各种各样的调料,比如牛奶、Mocha,豆浆(Soy)。所以饮料系统需要考虑调料的问题。我们第一次尝试,如果调料不同就新定义一个新的饮料对象。设计结构图如图2-2所示

大话设计模式之装饰者模式_第2张图片    

(2-2)

           从图中我们可以看下,这种设计方式虽然可以实现功能,但是对象太多,如果新加了一种调料,有需要创建很多的饮料对象,而且原有的代码逻辑也需要更改。

           能不能解决饮料对象太多的问题呢?利用继承和实例变量,把所有的调料都定义在超类中,并给这些调料设置属性。如果子类需要调料,那就给调料设置相应的属性。对应的结构图如图2-3所示

大话设计模式之装饰者模式_第3张图片

(2-3)

           利用这种方法,虽然可以减少类的数量,但是所有的子类,都会继承基类中的调料,假设有些饮料不需要牛奶作为调料,但是饮料对象中仍然会有牛奶调料属性,这显然有点不太合理。

           通过上文,我们已经认识到利用继承无法完全解决饮料系统对应的问题,比如:类数量太多,设计死板,基类的属性和方法并不适用于所有的子类。

           我们以饮料为主体,然后在运行时以调料来”装饰“饮料。比方说如果顾客想要摩卡和奶泡深焙咖啡,那么要做的是:

           1、那一个深焙咖啡(DarkRoast)对象

           2、以摩卡(Mocha)对象装饰它;

           3、以奶泡(Whip)对象装饰它;

           4、调用cost()方法,并依赖委托(delegate)将调料的价钱加上去。

           如何通过代码实现这种美妙的思想,我们首先来了解装饰者的定义和类图。

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

类结构图如2-4所示

大话设计模式之装饰者模式_第4张图片

(2-4)

           我们利用装饰者模式,重新改造饮料系统的结构。如图2-5所示

大话设计模式之装饰者模式_第5张图片

(2-5)

           代码实现

          

package cn.lzz.hf.third;

public abstract class Beverage {
	
	
	protected double amount;
	
	protected double price;
	
	protected String description;
	
	/**
	 * 描述
	 * @return
	 */
	public abstract String getDescriptions();
	
	/**
	 * 计算价格
	 * @return
	 */
	public abstract Double cost();

	protected double getAmount() {
		return amount;
	}

	protected void setAmount(double amount) {
		this.amount = amount;
	}

	protected double getPrice() {
		return price;
	}

	protected void setPrice(double price) {
		this.price = price;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}
	
}
package cn.lzz.hf.third;

public class DarkRoast extends Beverage {
	
	public DarkRoast() {
		super();
		// TODO Auto-generated constructor stub
	}
	
	public DarkRoast(double amount,double price,String description){
		this.amount=amount;
		this.price=price;
		this.description=description;
	}
	
	@Override
	public String getDescriptions() {
		// TODO Auto-generated method stub
		return this.description;
	}

	@Override
	public Double cost() {
		// TODO Auto-generated method stub
		return this.amount*this.price;
	}

}
package cn.lzz.hf.third;

/**
 * 调味品装饰超类
 * @author Administrator
 *
 */
public abstract class CondimentDecorator extends Beverage{
	protected Beverage beverage;
	
	public CondimentDecorator(){
		
	}
	public CondimentDecorator(Beverage beverage){
		this.beverage=beverage;
	}
	@Override
	public String getDescriptions() {
		// TODO Auto-generated method stub
		StringBuilder decriiption=new StringBuilder();
		decriiption=decriiption.append(this.description).append(",");
		if(null!=this.beverage){
			decriiption=decriiption.append(this.beverage.getDescriptions());
		}
		return decriiption.toString();
	}

	@Override
	public Double cost() {
		// TODO Auto-generated method stub
		return this.price*this.amount+(this.beverage==null?0.0:this.beverage.cost());
	}
	protected Beverage getBeverage() {
		return beverage;
	}

	protected void setBeverage(Beverage beverage) {
		this.beverage = beverage;
	}
	
}

package cn.lzz.hf.third;

public class Milk extends CondimentDecorator {
	
	public Milk(double amount,double price,String description){
		this.amount=amount;
		this.price=price;
		this.description=description;
	}
	public Milk(double amount,double price,String description,Beverage beverage){
		this.amount=amount;
		this.price=price;
		this.description=description;
		this.beverage=beverage;
	}

}

package cn.lzz.hf.third;

public class Mocha extends CondimentDecorator {

	public Mocha(double amount,double price,String description){
		this.amount=amount;
		this.price=price;
		this.description=description;
	}
	public Mocha(double amount,double price,String description,Beverage beverage){
		this.amount=amount;
		this.price=price;
		this.description=description;
		this.beverage=beverage;
	}
}

package cn.lzz.hf.third;

public class Soy extends CondimentDecorator {

	public Soy(double amount,double price,String description){
		this.amount=amount;
		this.price=price;
		this.description=description;
	}
	public Soy(double amount,double price,String description,Beverage beverage){
		this.amount=amount;
		this.price=price;
		this.description=description;
		this.beverage=beverage;
	}

}

package cn.lzz.hf.third;

public class Whip extends CondimentDecorator {

	public Whip(double amount,double price,String description){
		this.amount=amount;
		this.price=price;
		this.description=description;
	}
	public Whip(double amount,double price,String description,Beverage beverage){
		this.amount=amount;
		this.price=price;
		this.description=description;
		this.beverage=beverage;
	}

}

package cn.lzz.hf.third;

public class Test {
	public static void main(String[] args) {
		Beverage darkRoast=new DarkRoast(1,1.1,"焦炒咖啡");
		darkRoast=new Milk(1,2,"牛奶", darkRoast);
		darkRoast=new Mocha(2,3,"摩卡", darkRoast);
		darkRoast=new Soy(1,4,"大豆", darkRoast);
		darkRoast=new Whip(1,4,"泡沫", darkRoast);
		System.out.println("description:"+darkRoast.getDescriptions());
		System.out.println("totalPrice:"+darkRoast.cost());
	}
}

运行结果

description:泡沫,大豆,摩卡,牛奶,焦炒咖啡
totalPrice:17.1


三.总结

           装饰者模式意味着一群装饰者类,这些类用来包装具体组件。装饰者可以在被装饰者的行为前后加上自定义的行为,甚至可以将被装饰者的行为整个取代掉,而达到特定的目的。当然,装饰者模式会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。

           详细内容可以参考《Head First 设计模式》。

你可能感兴趣的:(设计模式-java,java,设计模式)