Head First设计模式读书笔记三 装饰模式(个人理解:包装模式)

本文示例代码材料源自Head First设计模式
以前整理自己整理的装饰模式的链接:https://blog.csdn.net/u011109881/article/details/58719049

思想

最大特点就是不修改原有代码的前提下,可以新增功能,使用情况可以参见我之前的总结,时隔一年,发现原文(http://www.runoob.com/design-pattern/design-pattern-intro.html)在这个模式总结的还是非常好的。

在之前的总结,使用的例子是画形状和着色问题,画形状是一个基本功能,着色的功能可以通过装饰器模式给原来的画图形装饰一下,那么画图形的工具也具有了着色的功能。

这里我还是采用Head First中饮料和调料的例子来模拟装饰模式。初学阶段,多看一个例子,能够看出一些通用的规则,不是么。

上栗子:
当前有个需求如下:一家奶茶店出售各式饮料,比如饮料A,饮料B,饮料可以放入不同的配料,比如加冰,加巧克力,加摩卡等等。要如何实现呢。是否可以通过继承来实现?比如实现巧克力饮料A,摩卡饮料B,加冰饮料A。。。。。。可以想见,这样会出现类爆炸的情况,如果客人需要双倍加冰双倍摩卡饮料A怎么办?如果后期饮料或是配料增加了种类,又会增加一大波类出来。
那么,如果在饮料中添加各个配料标志位来表示是否需要添加该配料的情况怎么样?这样乍看很合适,但是当增加配料的时候,如果饮料的种类很多,那么对原来设计的修改就比较麻烦了。
就没有一种情况,可以在不修改原来的代码,并且还能在饮料和配料增加的情况还能简便的扩展的方法么?那就用到今天的主角装饰模式了。
装饰模式的核心在于装饰类和被装饰类具有共同的基类。这样,装饰者就可以在原来的基类上包装一下,看上去多了一些功能或操作。特别要注意,饮料和配料类都继承自同一个类,在创建配料对象时,需要传入饮料对象作为参数,配料在饮料对象上进行进一步封装。

示例思路(规划类图)

装饰模式基本UML图:
这里写图片描述
如果运用到饮料和配料的例子中,UML图就是这样的:
这里写图片描述
这里写图片描述
实际代码

被装饰者–饮料类

public interface IBeverage {
    public String getDescription();
    public double getCost();
}

public class BeverageA implements IBeverage{
	String description = " BeverageA ";
	double cost = 1.9;
	public String getDescription() {
		// TODO Auto-generated method stub
		return description;
	}

	public double getCost() {
		// TODO Auto-generated method stub
		return cost;
	}

}
public class BeverageB implements IBeverage{
	String description = " BeverageB ";
	double cost = 2.1;
	public String getDescription() {
		// TODO Auto-generated method stub
		return description;
	}

	public double getCost() {
		// TODO Auto-generated method stub
		return cost;
	}

}

装饰者–配料类

public interface ICondimentDecorator extends IBeverage{
}
public class Condiment1 implements ICondimentDecorator{

	String description = " Condiment1 ";
	double cost = 0.1;
	IBeverage beverage;
	
	public Condiment1(IBeverage beverage) {
		this.beverage = beverage;
	}

	public String getDescription() {
		// TODO Auto-generated method stub
		return description + beverage.getDescription();
	}

	public double getCost() {
		// TODO Auto-generated method stub
		return cost+beverage.getCost();
	}

}
public class Condiment2 implements ICondimentDecorator{

	String description = " Condiment2 ";
	double cost = 0.2;
	IBeverage beverage;
	
	public Condiment2(IBeverage beverage) {
		this.beverage = beverage;
	}
	public String getDescription() {
		// TODO Auto-generated method stub
		return description + beverage.getDescription();
	}

	public double getCost() {
		// TODO Auto-generated method stub
		return cost+beverage.getCost();
	}
	
	public void mthodOf2(){
		System.out.println("this is meyhod of 2");
	}

}

就贴2个吧,第三个和前面的很像
测试类

public class testMain {

	public static void main(String[] args) {
		IBeverage a = new BeverageA();
		System.out.println(a.getCost());
		System.out.println(a.getDescription());
		a = new Condiment1(a);
		System.out.println(a.getCost());
		System.out.println(a.getDescription());
		a = new Condiment2(a);
		System.out.println(a.getCost());
		System.out.println(a.getDescription());
		a = new Condiment3(a);
		System.out.println(a.getCost());
		System.out.println(a.getDescription());
		
		Condiment3 aaa = (Condiment3)a;
		aaa.mthodOf3();
	}

}

Condiment2和Condiment3中我另外添加了mthodOf方法,用于测试装饰模式的一些不够灵活的地方,我后面说明
测试结果:

1.9
 BeverageA 
2.0
 Condiment1  BeverageA 
2.2
 Condiment2  Condiment1  BeverageA 
2.5
 Condiment3  Condiment2  Condiment1  BeverageA 
this is meyhod of 3

总结

现在,我们回过头来看,不修改原有代码的前提下,可以新增功能么?
当然可以,如果新加了饮料,那么我们就新加一个Beverage的实现类;如果新加一个配料,那么我们就新加一个Condiment实现类;要添加各种配料,只需要拿到IBeverage的实现类实例再包装一下即可,而且最神奇的地方在于原来的结构完全没有修改。
装饰模式高度契合对修改关闭对扩展。然而装饰模式也有它的缺点。
首先 ,装饰模式对类型的控制很重要,比如在被装饰类饮料中有个方法,但是使用装饰类配料去包装之后,所得到的已经不再是饮料的引用了,它已经变成配料的引用了,此时如果调用被装饰者饮料的方法是不能调用的,强制类型转换也无法实现。因此,装饰模式最后一个包装的类型决定了它能调用什么方法。
另外,装饰器模式中装饰者和被装饰者之间的关系界限不是很明显,经过一通包装之后,我们只能看到最外面的一层对象,至于里面的核心是什么对象,一共包裹了几层包装,比较难看出来。前面我在Condiment2和Condiment3中另外添加了mthodOf方法,如果最后包装的是Condiment3,那么我只能调用到mthodOf3方法,至于里面一层的包装的方法,是无法调用到的。
最后,如果没有清晰的理解装饰者模式,这种复杂的层层包装的模式让人很费解。比如JAVA API中的IO流就是最经典的装饰模式,当时学习时也感觉云里雾里的,现在才算拨开云雾见青山呢。
这里写图片描述
是不是和饮料及配料的结构很像呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值