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