声明:
本文只为方便我个人查阅和理解,详细的分析以及源代码请移步 原作者的博客http://chjavach.iteye.com/
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* 看到书上说是用装饰模式来计算奖金,第一个问题就是:这跟策略模式有什么区别?
* 个人理解:
* 策略模式一个时刻只能使用一个策略,各种策略之间是平等的
* 装饰模式之间的“装饰”(策略)是相互合作的(例如一种“装饰”基于另一种“装饰”)
* 可以使用多个“装饰”,最终各个“装饰”组成一个整体
*
* 对于装饰模式的理解,我认为其实是“组合优于继承”的理念的实现:“装饰”的目的就是为了扩展(或利用)一个类的功能
* 这可通过继承,可使用组合。通常是“组合优于继承”
*
* 为了使得“装饰器”之间可嵌套、组合,这些装饰器之间应该有相同的装饰方法:1.继承同一个父类 或者2.实现相同的接口
*/
/*
* 以下代码的业务逻辑:奖金=基本奖金+当月奖金+团队奖金
* 书上是把Component写在抽象类的,我这里写成接口
* bylijinnan
*/
interface IComponent {
double calculateBonus(String user);
}
//基本奖金。默认为0.这个类是被装饰的类,“装饰”从这个类开始
class BaseBonusComponent implements IComponent {
public double calculateBonus(String user) {
return 0;
}
}
abstract class Decorator implements IComponent{
protected IComponent component;
public Decorator(IComponent component) {
this.component = component;
}
public double calculateBonus(String user) {
//you can do something more here...
return component.calculateBonus(user);
}
}
//当月奖金
class MonthBonusDecorator extends Decorator {
public MonthBonusDecorator(IComponent component) {
super(component);
}
//加上当月奖金1000元
public double calculateBonus(String user) {
return component.calculateBonus(user) + 1000;
}
}
//团队奖金
class TeamBonusDecorator extends Decorator {
public TeamBonusDecorator(IComponent component) {
super(component);
}
//加上团队奖金2000元
public double calculateBonus(String user) {
return component.calculateBonus(user) + 2000;
}
}
/*
* 扩展:书上提到Java I/O使用了装饰模式
* 业务逻辑:简单地把英文字母向后移动两个位置,例如a->c,c->e...z->b
*/
class EncryptOutputStream extends OutputStream {
//持有“被装饰”的对象
private OutputStream outputStream;
public EncryptOutputStream(OutputStream outputStream) {
this.outputStream = outputStream;
}
@Override
public void write(int arg) throws IOException {
//做自己的一些处理,相当于“装饰”
arg += 2;
if (arg > 'z') {
arg -= 26;
}
//调用“被装饰”的对象的方法
this.outputStream.write(arg);
}
/*//下面这样写的话,可实现“装饰器”互换
public void close() throws IOException {
outputStream.flush();
super.close();
}
*/
}
class EncryptOutputStream2 extends FilterOutputStream {
//private OutputStream outputStream; //这里为什么不再需要持有“被装饰”的对象呢?查看FilterOutputStream源码会发现,FilterOutputStream已经持有了
public EncryptOutputStream2(OutputStream outputStream) {
super(outputStream);
}
@Override
public void write(int arg) throws IOException {
//做自己的一些处理,相当于“装饰”
arg += 2;
if (arg > 'z') {
arg -= 26;
}
//调用“被装饰”的对象的方法
super.write(arg);
}
}
//这个类是用来测试的
public class DecoratorPattern {
public static void main(String[] args) throws Exception {
//测试奖金的计算
BaseBonusComponent baseBonus = new BaseBonusComponent();
Decorator monthBonus = new MonthBonusDecorator(baseBonus);
Decorator teamBonus = new TeamBonusDecorator(monthBonus);
/*//装饰的顺序可以改变,写成下面这样也可以
Decorator teamBonus = new TeamBonusDecorator(baseBonus);
Decorator monthBonus = new MonthBonusDecorator(teamBonus);
*/
System.out.println(teamBonus.calculateBonus("Tom")); //在这个例子不传递user也可以,但实际应用中每个人的奖金都应该不同
//测试“装饰”OutputStream
/*
A.当EncryptOutputStream extends OutputStream时,代码中1和2的顺序不可互换
因为out.close时,调用顺序是-->DataOutputStream.close()
-->BufferedOutputStream.close()
-->EncryptOutputStream.close()
-->FileOutputStream.close(),
当执行到BufferedOutputStream.close()时,查看源码有BufferedOutputStream extends FilterOutputStream,
所以BufferedOutputStream继承了FilterOutputStream的close方法,而FileOutputStream.close()会调用flush这个方法,
强制输出缓存的数据(BufferedOutputStream要在8192字节满了才输出)
但当1和2调换过来时,没有内容输出到文件中。
因为,调用顺序是 -->DataOutputStream.close()
-->EncryptOutputStream.close();
EncryptOutputStream.close()会直接调用父类OutputStream.close()(这个方法是个空方法什么也不做)
如果想要1和2可互换,可重写EncryptOutputStream的close方法,调用flush()方法,具体代码见前面(我注释掉了)
B.当EncryptOutputStream2 extends FilterOutputStream时,代码中1和2的顺序可互换
查看FilterOutputStream的close()方法是调用了flush()方法的
*/
DataOutputStream out = new DataOutputStream(
new BufferedOutputStream( //1.
new EncryptOutputStream( //2.
new FileOutputStream("d:/tmp/test.txt"))));
out.write("abcdxyz".getBytes());
out.close();
DataOutputStream out2 = new DataOutputStream(
new EncryptOutputStream2(
new BufferedOutputStream(
new FileOutputStream("d:/tmp/test2.txt"))));
out2.write("az".getBytes());
out2.close();
}
}