设计模式系列之四装饰者模式

装饰者模式的定义:

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

Java实现

  借用《Head First设计模式》 中的咖啡店卖咖啡的样例。便于说明,这里简洁一下。假如我要写一个咖啡点单计价的软件。业务逻辑是这样的,一杯咖啡10元,如果加糖另外加1元,如果加奶另外加两元。实际来讲,采用硬编码的方式我可以写三个类来实现该需求:1.咖啡单买;2.咖啡+糖;3.咖啡+奶。然后就完事儿,任务完成。但是如果哪一天,老板打算卖即加糖又加奶的咖啡的话,就得再增加一个类来实现该类型咖啡的计价。这还好说,假如我有多种类型的咖啡呢?而且每种类型的咖啡都可以加奶或者糖,这样排列组合下去,我估计要写吐血…..

下面,我们就使用装饰者模式来解决该问题。

样例

首先来一个咖啡的抽象类

1
2
3
4
5
6
7
8
9
10
 public abstract class AbsCoffee {
//咖啡描述
String description = "Unknown Type";

public String getDescription() {
return description;
}
//咖啡加个
public abstract double cost();
}

接下来两种咖啡的实现

1
2
3
4
5
6
7
8
9
10
 public class MochaCoffee extends AbsCoffee{
public MochaCoffee() {
description = "摩卡咖啡";
}
@Override
public double cost() {
//摩卡咖啡10元一杯
return 10;
}
}

1
2
3
4
5
6
7
8
9
10
public class LatteCoffee extends AbsCoffee{
public LatteCoffee() {
description = "拿铁咖啡";
}
@Override
public double cost() {
//拿铁咖啡15元一杯
return 15;
}
}

装饰器

1
2
3
4
5
//装饰器
public abstract class Decorator extends AbsCoffee {
//强制装饰者增加自己的描述信息
public abstract String getDescription();
}

两个装饰类,这里分别为咖啡的调调:糖、牛奶

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Sugar extends Decorator{
AbsCoffee coffee;
public Sugar(AbsCoffee coffee){
this.coffee = coffee;
}

@Override
public String getDescription() {
return coffee.getDescription()+"+糖";
}

@Override
public double cost() {
// TODO Auto-generated method stub
return coffee.cost()+2;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Milk extends Decorator{
AbsCoffee coffee;
public Milk(AbsCoffee coffee){
this.coffee = coffee;
}

@Override
public String getDescription() {
return coffee.getDescription()+"+牛奶";
}

@Override
public double cost() {
// TODO Auto-generated method stub
return coffee.cost()+1;
}
}

测试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
//摩卡咖啡
AbsCoffee coffee = new MochaCoffee();
//打印:摩卡咖啡=10.0
System.out.println(coffee.getDescription()+"="+coffee.cost());
//摩卡+糖+牛奶
coffee = new Milk(new Sugar(coffee));
//打印:摩卡咖啡+糖+牛奶=13.0
System.out.println(coffee.getDescription()+"="+coffee.cost());
/*同理,可以把不同类型的咖啡与不同类型的调料任意组合,解决了硬编码时一种组合就需要对应写一套逻辑的尴尬*/
}
}

java.io中的装饰者模式

这里的代码样例来自与《Head First设计模式》一书,演示了FileInputStream中装饰者模式的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//实现字符的大小写转换
public class LowerCaseInputStream extends FilterInputStream {

public LowerCaseInputStream(InputStream in) {
super(in);
}

public int read() throws IOException {
int c = in.read();
return (c == -1 ? c : Character.toLowerCase((char)c));
}

public int read(byte[] b, int offset, int len) throws IOException {
int result = in.read(b, offset, len);
for (int i = offset; i < offset+result; i++) {
b[i] = (byte)Character.toLowerCase((char)b[i]);
}
return result;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class InputTest {
public static void main(String[] args) throws IOException {
int c;

try {
//层层装饰,最终输入大写字符
InputStream in =
new LowerCaseInputStream(
new BufferedInputStream(
new FileInputStream("test.txt")));

while((c = in.read()) >= 0) {
System.out.print((char)c);
}

in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

Javascript实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
(function () {
function MochaCoffee() {
this.description = "摩卡咖啡"
}
MochaCoffee.prototype.cost = function () {
return 10;
};
MochaCoffee.prototype.getDescription = function () {
return this.description;
};

//调料:糖
function Sugar(coffee) {
this.coffee = coffee;
this.description = "糖";
}
Sugar.prototype.cost = function () {
return this.coffee.cost()+1;
};
Sugar.prototype.getDescription = function () {
return this.coffee.getDescription()+"+"+this.description
};
//调料:牛奶
function Milk(coffee) {
this.coffee = coffee;
this.description = "牛奶";
}
Milk.prototype.cost = function () {
return this.coffee.cost()+2;
};
Milk.prototype.getDescription = function () {
return this.coffee.getDescription()+"+"+this.description
};

/*************************测试代码*************************/
var coffee = new MochaCoffee();
//这里将打印:摩卡咖啡=10
console.log(coffee.getDescription()+"="+coffee.cost());
coffee = new Milk(new Sugar(coffee));
//这里将打印:摩卡咖啡+糖+牛奶=13
console.log(coffee.getDescription()+"="+coffee.cost());
})();

上一篇:设计模式系列之三观察者模式
下一篇:设计模式系列之五工厂模式

留言

欢迎交流想法。留言会通过 GitHub Issues 保存,首次使用需要登录 GitHub。