装饰者模式的定义:
装饰者模式:动态的将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
Java实现
借用《Head First设计模式》 中的咖啡店卖咖啡的样例。便于说明,这里简洁一下。假如我要写一个咖啡点单计价的软件。业务逻辑是这样的,一杯咖啡10元,如果加糖另外加1元,如果加奶另外加两元。实际来讲,采用硬编码的方式我可以写三个类来实现该需求:1.咖啡单买;2.咖啡+糖;3.咖啡+奶。然后就完事儿,任务完成。但是如果哪一天,老板打算卖即加糖又加奶的咖啡的话,就得再增加一个类来实现该类型咖啡的计价。这还好说,假如我有多种类型的咖啡呢?而且每种类型的咖啡都可以加奶或者糖,这样排列组合下去,我估计要写吐血…..
下面,我们就使用装饰者模式来解决该问题。
样例
首先来一个咖啡的抽象类
public abstract class AbsCoffee {
//咖啡描述
String description = "Unknown Type";
public String getDescription() {
return description;
}
//咖啡加个
public abstract double cost();
}
接下来两种咖啡的实现
public class MochaCoffee extends AbsCoffee{
public MochaCoffee() {
description = "摩卡咖啡";
}
@Override
public double cost() {
//摩卡咖啡10元一杯
return 10;
}
}
public class LatteCoffee extends AbsCoffee{
public LatteCoffee() {
description = "拿铁咖啡";
}
@Override
public double cost() {
//拿铁咖啡15元一杯
return 15;
}
}
装饰器
//装饰器
public abstract class Decorator extends AbsCoffee {
//强制装饰者增加自己的描述信息
public abstract String getDescription();
}
两个装饰类,这里分别为咖啡的调调:糖、牛奶
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;
}
}
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;
}
}
测试结果
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
中装饰者模式的使用
//实现字符的大小写转换
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;
}
}
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实现
(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());
})();
上一篇:设计模式系列之三观察者模式
下一篇:设计模式系列之五工厂模式