装饰者模式
装饰者模式(Decorator Pattern):动态地给一个对象增加一些额外的职责,增加对象功能来说,装饰模式比生成子类实现更为灵活。装饰模式是一种对象结构型模式。
在装饰者模式中,为了让系统具有更好的灵活性和可扩展性,我们通常会定义一个抽象装饰类,而将具体的装饰类作为它的子类
角色
Component(抽象构件):它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
ConcreteComponent(具体构件):它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。
Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。
由于具体构件类和装饰类都实现了相同的抽象构件接口,因此装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。
装饰模式的核心在于抽象装饰类的设计。
示例
煎饼抽象类
public abstract class ABattercake {
protected abstract String getDesc();
protected abstract int cost();
}
煎饼类,继承了煎饼抽象类,一个煎饼 8 块钱
public class Battercake extends ABattercake {
@Override
protected String getDesc() {
return "煎饼";
}
@Override
protected int cost() {
return 8;
}
}
抽象装饰类,需要注意的是,抽象装饰类通过成员属性的方式将 煎饼抽象类组合进来,同时也继承了煎饼抽象类,且这里定义了新的业务方法 doSomething()
public abstract class AbstractDecorator extends ABattercake {
private ABattercake aBattercake;
public AbstractDecorator(ABattercake aBattercake) {
this.aBattercake = aBattercake;
}
protected abstract void doSomething();
@Override
protected String getDesc() {
return this.aBattercake.getDesc();
}
@Override
protected int cost() {
return this.aBattercake.cost();
}
}
鸡蛋装饰器,继承了抽象装饰类,鸡蛋装饰器在父类的基础上增加了一个鸡蛋,同时价格加上 1 块钱
public class EggDecorator extends AbstractDecorator {
public EggDecorator(ABattercake aBattercake) {
super(aBattercake);
}
@Override
protected void doSomething() {
}
@Override
protected String getDesc() {
return super.getDesc() + " 加一个鸡蛋";
}
@Override
protected int cost() {
return super.cost() + 1;
}
public void egg() {
System.out.println("增加了一个鸡蛋");
}
}
香肠装饰器,与鸡蛋装饰器类似,继承了抽象装饰类,给在父类的基础上加上一根香肠,同时价格增加 2 块钱
public class SausageDecorator extends AbstractDecorator{
public SausageDecorator(ABattercake aBattercake) {
super(aBattercake);
}
@Override
protected void doSomething() {
}
@Override
protected String getDesc() {
return super.getDesc() + " 加一根香肠";
}
@Override
protected int cost() {
return super.cost() + 2;
}
}
测试,购买煎饼
1、购买一个煎饼
public class Test {
public static void main(String[] args) {
ABattercake aBattercake = new Battercake();
System.out.println(aBattercake.getDesc() + ", 销售价格: " + aBattercake.cost());
}
}
输出
煎饼, 销售价格: 8
1
2、购买一个加鸡蛋的煎饼
public class Test {
public static void main(String[] args) {
ABattercake aBattercake = new Battercake();
aBattercake = new EggDecorator(aBattercake);
System.out.println(aBattercake.getDesc() + ", 销售价格: " + aBattercake.cost());
}
}
输出
煎饼 加一个鸡蛋, 销售价格: 9
1
3、购买一个加两个鸡蛋的煎饼
public class Test {
public static void main(String[] args) {
ABattercake aBattercake = new Battercake();
aBattercake = new EggDecorator(aBattercake);
aBattercake = new EggDecorator(aBattercake);
System.out.println(aBattercake.getDesc() + ", 销售价格: " + aBattercake.cost());
}
}
输出
煎饼 加一个鸡蛋 加一个鸡蛋, 销售价格: 10
1
4、购买一个加两个鸡蛋和一根香肠的煎饼
public class Test {
public static void main(String[] args) {
ABattercake aBattercake = new Battercake();
aBattercake = new EggDecorator(aBattercake);
aBattercake = new EggDecorator(aBattercake);
aBattercake = new SausageDecorator(aBattercake);
System.out.println(aBattercake.getDesc() + ", 销售价格: " + aBattercake.cost());
}
}
输出
煎饼 加一个鸡蛋 加一个鸡蛋 加一根香肠, 销售价格: 12
1
画出UML类图如下所示
小结一下
由于具体构件类和装饰类都实现了相同的抽象构件接口,因此装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。
譬如我们给煎饼加上一个鸡蛋可以这么写 aBattercake = new EggDecorator(aBattercake);,客户端仍然可以把 aBattercake 当成原来的 aBattercake一样,不过现在的 aBattercake已经被装饰加上了鸡蛋
装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。
透明装饰模式与半透明装饰模式
在上面的示例中,装饰后的对象是通过抽象构建类类型 ABattercake 的变量来引用的,在鸡蛋装饰器这个类中我们新增了 egg() 方法,如果此时我们想要单独调用该方法是调用不到的
除非引用变量的类型改为 EggDecorator,这样就可以调用了
EggDecorator eggBattercake = new EggDecorator(aBattercake);
eggBattercake.egg();
在实际使用过程中,由于新增行为可能需要单独调用,因此这种形式的装饰模式也经常出现,这种装饰模式被称为半透明(Semi-transparent)装饰模式,而标准的装饰模式是透明(Transparent)装饰模式。
(1) 透明装饰模式
在透明装饰模式中,要求客户端完全针对抽象编程,装饰模式的透明性要求客户端程序不应该将对象声明为具体构件类型或具体装饰类型,而应该全部声明为抽象构件类型。
(2) 半透明装饰模式
透明装饰模式的设计难度较大,而且有时我们需要单独调用新增的业务方法。为了能够调用到新增方法,我们不得不用具体装饰类型来定义装饰之后的对象,而具体构件类型还是可以使用抽象构件类型来定义,这种装饰模式即为半透明装饰模式。
半透明装饰模式可以给系统带来更多的灵活性,设计相对简单,使用起来也非常方便;但是其最大的缺点在于不能实现对同一个对象的多次装饰,而且客户端需要有区别地对待装饰之前的对象和装饰之后的对象。
装饰模式注意事项
(1) 尽量保持装饰类的接口与被装饰类的接口相同,这样,对于客户端而言,无论是装饰之前的对象还是装饰之后的对象都可以一致对待。这也就是说,在可能的情况下,我们应该尽量使用透明装饰模式。
(2) 尽量保持具体构件类是一个“轻”类,也就是说不要把太多的行为放在具体构件类中,我们可以通过装饰类对其进行扩展。
(3) 如果只有一个具体构件类,那么抽象装饰类可以作为该具体构件类的直接子类。
原文:https://blog.csdn.net/wwwdc1012/article/details/82764333