- 装饰者模式:
又名包装模式,顾名思义,就是通过一定的方式,包装一个对象。给他附加上各种不同的属性,但是又不改变被包装对象的结构。通过定义可以看出,装饰者模式其实是子类继承的一种替代方式。 - 装饰者模式包含的角色:
- 被装饰对象的抽象角色: 给出一个抽象类或者接口,可以动态的给对象增加属性,规范对象属性的内容。
- 具体被装饰对象角色: 定义一个具体的对象,用于接受附加的属性。对于开发来说就是定义一个具体的类,用户实现不同的方法
- 抽象装饰者角色: 维护指向被装饰对象的抽象角色的引用,定义与被装饰对象的抽象角色一致的接口,换句话说就是要继承或者实现被装饰对象抽象角色的方法。
- 具体装饰者角色: 继承或实现抽象装饰者角色,并且包含具体属性的内容,也就是开发中的方法实现。
- 装饰者模式示意图:
- 装饰者模式的代码示例:
场景:计算奶茶店的奶茶添加不同配料的价格。
- 被装饰对象的抽象角色:
public interface Drink {
String name();
float price();
}
- 具体被装饰对象角色:
public class Tea implements Drink {
@Override
public String name() {
return "only Tea";
}
@Override
public float price() {
return 10;
}
}
- 抽象装饰者角色:
public abstract class Supplement implements Drink {
//传入一个被装饰对象
private Drink drink;
public Supplement(Drink drink) {
this.drink = drink;
}
@Override
public String name(){
return drink.name();
}
@Override
public float price() {
return drink.price();
}
}
- 具体装饰者角色:
装饰角色:珍珠
public class Bubble extends Supplement {
public Bubble(Drink drink) {
super(drink);
}
@Override
public String name() {
return super.name()+">>>Add Bubble";
}
@Override
public float price() {
return super.price()+5;
}
}
装饰角色:布丁
public class Pudding extends Supplement {
public Pudding(Drink drink) {
super(drink);
}
@Override
public String name() {
return super.name()+">>>>Add Pudding";
}
@Override
public float price() {
return super.price() + 3;
}
}
装饰角色:牛奶
public class Milk extends Supplement {
public Milk(Drink drink) {
super(drink);
}
@Override
public String name() {
return super.name()+">>>Add Milk!!!";
}
@Override
public float price() {
return super.price()+5;
}
}
- 测试主方法:
public class Main {
public static void main(String[] args) {
//初始化被装饰对象
Drink tea = new Tea();
//开始装饰
tea = new Pudding(tea);
tea = new Milk(tea);
System.out.println("添加配料表:"+tea.name()+"||最终价格:"+tea.price() );
}
}
总结:
通过以上代码,可以看出装饰者模式具有以下优点:
- 装饰者模式和继承都具有扩展对象的功能,但是相对来说,装饰者模式相比继承来说更加灵活,装饰者模式可以在需要的情况下,给对象添加一个属性或者取消一个属性。
- 装饰者更像是乐高积木,给你各种形状,你可以自己拼成不同的建筑或者其他内容。
装饰者模式的缺点:
由于排列组合会产生很多的对象之间的关系,导致后期处理bug时,查找对象之间的关系会很麻烦。
JAVA内置的装饰者模式:
在JAVA中最能体现装饰者模式的就是JAVA的I/O流的操作,这里使用极客学院的JAVA示例代码作为展示。
需求:将输入的字符或者文件以大写的形式输出。
UpperCaseInputStream类:
public class UpperCaseInputStream extends FilterInputStream {
/**
* Creates a <code>FilterInputStream</code>
* by assigning the argument <code>in</code>
* to the field <code>this.in</code> so as
* to remember it for later use.
*
* @param in the underlying input stream, or <code>null</code> if
* this instance is to be created without an underlying stream.
*/
protected UpperCaseInputStream(InputStream in) {
//输入被装饰者
super(in);
}
@Override
public int read() throws IOException {
int c = super.read();
return c==-1?c:Character.toUpperCase((char)c);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int result = super.read(b, off, len);
for (int i = 0; i < result; i++) {
b[i] = (byte) Character.toUpperCase((char)b[i]);
}
return result;
}
}
测试方法类:
public class Mian {
public static void main(String[] args) throws IOException {
int c;
InputStream io = new UpperCaseInputStream(new BufferedInputStream(new FileInputStream("G:\\test.txt")));
while ((c=io.read())>=0){
System.out.print((char)c);
}
}
}
-
被装饰对象的抽象角色:由InputStream扮演。这是一个抽象类,为各种子类型提供统一的接口。
-
具体被装饰对象角色:由ByteArrayInputStream、FileInputStream、PipedInputStream、StringBufferInputStream等类扮演。它们实现了抽象构件角色所规定的接口。
-
抽象装饰者角色:由FilterInputStream扮演。它实现了InputStream所规定的接口。
-
具体装饰者角色:由几个类扮演,分别是BufferedInputStream、DataInputStream以及两个不常用到的类LineNumberInputStream、PushbackInputStream。