介绍
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。
继承机制同样可以给现有类增加功能,通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法。但是这种方法是静态的,用户不能控制增加行为的方式和时机。
而装饰器模式是将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为。
结构图
时序图
案例
我这边就用街边小吃来举例子。客人可以选择面条、米粉等,然后可以在面条或者米粉上添加其他一些小吃,如火腿、丸子等等。
食物接口
public interface Food {
void getDescription();
int getMoney();
}
面条主食
public class NoodleFood implements Food{
@Override
public void getDescription() {
System.out.print("-面条-");
}
@Override
public int getMoney() {
return 4;
}
}
米粉主食
public class FlourFood implements Food{
@Override
public void getDescription() {
System.out.print("-米粉-");
}
@Override
public int getMoney() {
return 5;
}
}
装饰接口
public interface CondimentDecorator extends Food{
}
丸子装饰类
public class BallDecorator implements CondimentDecorator{
private Food food;
public BallDecorator(Food food) {
this.food = food;
}
@Override
public void getDescription() {
food.getDescription();
System.out.print("-丸子-");
}
@Override
public int getMoney() {
return food.getMoney()+3;
}
}
火腿装饰类
public class HamDecorator implements CondimentDecorator{
private Food food;
public HamDecorator(Food food) {
this.food = food;
}
@Override
public void getDescription() {
food.getDescription();
System.out.print("-火腿-");
}
@Override
public int getMoney() {
return food.getMoney()+3;
}
}
测试类
public class Client {
public static void main(String[] args) {
Food food = new HamDecorator(new BallDecorator(new HamDecorator(new NoodleFood())));
food.getDescription();
System.out.println();
System.out.println(food.getMoney());
Food food2 = new BallDecorator(new HamDecorator(new BallDecorator(new FlourFood())));
food2.getDescription();
System.out.println();
System.out.println(food2.getMoney());
}
}
测试结果
总结
使用装饰模式来实现扩展比继承更加灵活,它以对客户透明的方式动态地给一个对象附加更多的责任。如果使用继承会照成类爆炸。而且继承是一种耦合度较大的静态关系,无法在程序运行时动态扩展。
装饰模式包含四个角色:抽象构件定义了对象的接口,可以给这些对 象动态增加职责(方法);具体构件定义了具体的构件对象,实现了 在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法); 抽象装饰类是抽象构件类的子类,用于给具体构件增加职责,但是具 体职责在其子类中实现;具体装饰类是抽象装饰类的子类,负责向构 件添加新的职责。
在jdk中最典型的就是I/O了,感兴趣的可以去了解io的源码。
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("1.txt")));