设计模式学习--装饰者模式(Decorator Pattern)

设计模式学习--装饰者模式(Decorator Pattern)

2013年5月19日 天气:阴天下雨
不停脚步继续学习设计模式
小巫在呐喊:啊、啊、啊、啊、啊

什么是装饰者模式?

其实我们用Java I/O的时候已经用到过了,只是可能不知道这就是设计模式,在后面有一个设计自己的装饰者模式,再介绍吧。

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

新的设计原则

对扩展开发,对修改关闭

写一写要点

1. 继承属于扩展形式之一,但不见得达到弹性设计的最佳方式。
2. 在我们的设计中,应该允许行为可以被扩展,而无须修改现有的代码。
3. 组合和委托可用于在运行时动态加上新的行为。
4. 除了继承,装饰者模式也可以让我们扩展行为。
5. 装饰者模式意味着一群装饰者类,这些类用来包装具体组件。
6. 装饰者类反映出被装饰的组件类型(事实上,他们具有相同的类型,都经过接口或继承实现)
7. 装饰者可以在被装饰者的行为与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。
8. 你可以用无数个装饰者包装一个组件。
9. 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。
10. 装饰者会导致设计中出现许多的小对象,如果过度使用,会让程序变得很复杂。




装饰者模式例子:星巴兹咖啡

怎么开始?
写下星巴兹的代码:
package decoratorPattern;
/**
 * 2013/5/19
 * @author wwj
 * 饮料抽象类
 */
public abstract class Beverage {
	String description = "Unknown Beverage"; //饮料描述
	
	public String getDescription() {	//得到饮料描述
		return description;
	}
	
	public abstract double cost();	//抽象方法,用于计算饮料价格
}


package decoratorPattern;

/**
 * 2013/5/19
 * @author wwj
 * 调料抽象类:代表装饰者
 */
public abstract class CondimentDecorator extends Beverage {

	@Override
	public double cost() {
		return 0;
	}
	
	/**
	 * 所有的调料装饰者都必须重新实现getDescription()方法。
	 */
	public abstract String getDescription();

}


写饮料的代码:
这里有四种:Epresso、HouseBlend、DarkRoast、Decat

package decoratorPattern;

/**
 * 2013/5/19
 * @author wwj
 * 浓缩咖啡,一种饮料
 */
public class Espresso extends Beverage {
	public Espresso() {
		description = "Espresso";
	}
	
	@Override
	public double cost() {
		return 1.99;
	}

}

package decoratorPattern;

/**
 * 2013/5/19
 * @author wwj
 * 综合饮料
 */
public class HouseBlend extends Beverage {
	public HouseBlend(){
		description = "House Blend Coffee";
	}
	
	@Override
	public double cost() {
		return .89;
	}

}

package decoratorPattern;

/**
 * 2013/5/19
 * @author wwj
 * 深培咖啡
 */
public class DarkRoast extends Beverage {
	public DarkRoast(){
		description = "DarkRoast";
	}
	@Override
	public double cost() {
		return .99;
	}

}

package decoratorPattern;

/**
 * 2013/5/19
 * @author wwj
 * 低咖啡因
 */
public class Decat extends Beverage {
	public Decat(){
		description = "Decat";
	}
	@Override
	public double cost() {
		return 1.05;
	}

}


写调料的代码
这里有三种调料:Mocha、Soy、Whip
package decoratorPattern;

/**
 * 2013/5/19
 * @author wwj
 * 具体装饰者:摩卡
 */
public class Mocha extends CondimentDecorator {
	//定义一个实例变量记录饮料,也就是被装饰者
	Beverage beverage;
	
	/**
	 * 用来把被装饰者记录到实例变量中去
	 * @param beverage
	 */
	public Mocha(Beverage beverage) {
		this.beverage = beverage;
	}

	
	/**
	 * 这里利用委托的做法,得到一个叙述,然后在其后加上附加的叙述
	 */
	@Override
	public String getDescription() {
		return beverage.getDescription() + ", Mocha";
	}
	
	/**
	 * 要计算Mocha饮料的价钱。首先把调用委托给被装饰对象,以计算价钱,然后再加上Mocha的价钱,得到最后结果
	 */
	public double cost() {
		return .20 + beverage.cost();
	}
}

package decoratorPattern;

/**
 * 2013/5/19
 * @author wwj
 * 豆浆,一种配料
 */
public class Soy extends CondimentDecorator {
	Beverage beverage;
	
	
	public Soy(Beverage beverage) {
		this.beverage = beverage;
	}


	@Override
	public String getDescription() {
		return beverage.getDescription() + ",Soy";
	}
	
	public double cost() {
		return .15 + beverage.cost();
	}

}

package decoratorPattern;

/**
 * 2013/5/19
 * @author wwj
 * 奶泡,一种配料
 */
public class Whip extends CondimentDecorator {
	Beverage beverage;
	
	
	public Whip(Beverage beverage) {
		this.beverage = beverage;
	}


	@Override
	public String getDescription() {
		return beverage.getDescription() + ",Whip";
	}
	
	public double cost() {
		return .10 + beverage.cost();
	}

}

现在可以供应咖啡了
package decoratorPattern;

/**
 * 测试类,供应咖啡
 * @author wwj
 *
 */
public class StarbuzzCoffee {

	public static void main(String[] args) {
		//订一杯Espresso,不需要调料,打印出它的描述与价钱
		Beverage beverage = new Espresso();
		System.out.println(beverage.getDescription() + " $" + beverage.cost());
		
		Beverage beverage2 = new DarkRoast();
		beverage2 = new Mocha(beverage2);	//用Mocha装饰它
		beverage2 = new Mocha(beverage2);	//用第二个Mocha装饰它
		beverage2 = new Whip(beverage2);	//用Whip装饰它
		System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
		
		
		Beverage beverage3 = new HouseBlend();
		beverage3 = new Soy(beverage3);
		beverage3 = new Mocha(beverage3);
		beverage3 = new Whip(beverage3);
		System.out.println(beverage3.getDescription() + " $" + beverage3.cost());
	}
}
实验结果如下:

Espresso $1.99
DarkRoast, Mocha, Mocha,Whip $1.49
House Blend Coffee,Soy, Mocha,Whip $1.34

如果觉得看得不够爽,买办HeadFirst设计模式吧,就算小巫是新手也还是觉得这本书值得一卖,图文并茂,看起来不会累。


刚开始的时候已经说过Java中使用装饰者模式的I/O
下面就扩展一下它的行为:将大写字符转换为小写字符
package javaIo;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

/*
 * 编写自己的Java I/O装饰者
 */
public class LowerCaseInputStream extends FilterInputStream {

	protected LowerCaseInputStream(InputStream in) {
		super(in);
	}
	/**
	 * 针对字节
	 */
	public int read() throws IOException {
		int c = super.read();
		return (c == -1 ? c : Character.toLowerCase((char) c));
	}
	
	/**
	 * 针对字节数组
	 */
	public int read(byte[] b, int offset, int len) throws IOException {
		int result = super.read(b, offset, len);
		for(int i = offset; i < offset + result; i++) {
			b[i] = (byte) Character.toLowerCase((char)b[i]);
		}
		return result;
	}

}


package javaIo;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class InputTest {
	public static void main(String[] args) throws IOException{
		int c;
		try {
			//设置FileInputStream,先用BufferedInputStream装饰它,再用我们崭新的
			//LowerCaseInputStream过滤器装饰它
			InputStream in =
				new LowerCaseInputStream(
						new BufferedInputStream(new FileInputStream("D:/test.txt")));
			while((c = in.read()) >= 0) {
				System.out.print((char)c);
			}
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

测试结果:
i know the decorator pattern

ok,装饰者模式就是这样子啦,搞清楚装饰者和被装饰者之间的关系,也没啥不好理解的。下一个设计模式:工厂模式,比较常用的一个模式。今天是星期日,宝贵的周末时间就要没了,可能就不会想这两天那么频繁地发表博客了,不过有空一定会继续学习的。









评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小巫技术博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值