装饰者模式定义:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(OCP)

    装饰者模式类图:
    image.png

    角色:
    1、抽象构件(Component):给出一个抽象接口,以规范准备接收附加责任的对象
    2、具体构件(ConcreteComponent):定义一个将要接收附加责任的类
    3、装饰(Decorator):持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口
    4、具体装饰(ConcreteDecorator):负责给构件对象贴上附加的责任

    应用举例:用装饰者模式解决咖啡订单,咖啡有Espresso、LongBlack、ShortBlack三种,每种咖啡都可以加巧克力、牛奶、豆浆三种调味品,根据不同的咖啡和咖啡添加的调味品计算订单价格
    代码示例:

    1. @Data
    2. public abstract class Drink {
    3. public String des;
    4. private Double price = 0.0;
    5. /**
    6. * 计算费用的抽象方法
    7. * @return 费用
    8. */
    9. public abstract Double cost();
    10. }
    11. public class Coffee extends Drink {
    12. @Override
    13. public Double cost() {
    14. return super.getPrice();
    15. }
    16. }
    17. public class Espresso extends Coffee {
    18. public Espresso(){
    19. setDes("意大利咖啡");
    20. //意大利咖啡6元
    21. setPrice(6.0);
    22. }
    23. }
    24. public class LongBlack extends Coffee {
    25. public LongBlack(){
    26. setDes("long black");
    27. //long black价格5元
    28. setPrice(5.0);
    29. }
    30. }
    31. public class ShortBlack extends Coffee {
    32. public ShortBlack(){
    33. setDes("short black");
    34. //short black价格4元
    35. setPrice(4.0);
    36. }
    37. }
    38. public class Decorator extends Drink {
    39. private Drink drink;
    40. public Decorator(Drink drink){
    41. this.drink = drink;
    42. }
    43. @Override
    44. public Double cost() {
    45. return super.getPrice() + drink.cost();
    46. }
    47. @Override
    48. public String getDes(){
    49. //输出装饰者信息
    50. return super.des + " " + super.getPrice() + "&&" + drink.getDes();
    51. }
    52. }
    53. public class Chocolate extends Decorator {
    54. public Chocolate(Drink drink) {
    55. super(drink);
    56. setDes("巧克力");
    57. setPrice(3.0);
    58. }
    59. }
    60. public class Milk extends Decorator {
    61. public Milk(Drink drink) {
    62. super(drink);
    63. setDes("牛奶");
    64. setPrice(2.0);
    65. }
    66. }
    67. public class Soy extends Decorator {
    68. public Soy(Drink drink) {
    69. super(drink);
    70. setDes("豆浆");
    71. setPrice(1.5);
    72. }
    73. }
    74. public class CoffeeBar {
    75. public static void main(String[] args) {
    76. //点一份long black
    77. Drink longBlack = new LongBlack();
    78. System.out.println(longBlack.getDes() + "费用:" + longBlack.cost());
    79. //加一点牛奶
    80. longBlack = new Milk(longBlack);
    81. System.out.println(longBlack.getDes() + "费用:" + longBlack.cost());
    82. //再加一份巧克力
    83. longBlack = new Chocolate(longBlack);
    84. System.out.println(longBlack.getDes() + "费用:" + longBlack.cost());
    85. //再加一份巧克力
    86. longBlack = new Chocolate(longBlack);
    87. System.out.println(longBlack.getDes() + "费用:" + longBlack.cost());
    88. }
    89. }

    运行结果:

    1. long black费用:5.0
    2. 牛奶 2.0&&long black费用:7.0
    3. 巧克力 3.0&&牛奶 2.0&&long black费用:10.0
    4. 巧克力 3.0&&巧克力 3.0&&牛奶 2.0&&long black费用:13.0

    装饰者模式的优缺点:
    1、优点

    • 比继承更灵活。

    从为对象添加功能的角度看,装饰者模式比继承模式更为灵活。继承是静态的,一旦继承所有的子类都有一样的功能。装饰者模式采用把功能分离到每个装饰器当中,通过对象组合的方式,在运行时动态地组合功能,被装饰对象最终有哪些功能是由运行时动态组合的功能决定的。

    • 复用功能更容易

    装饰模式把一系列复杂的功能分散到每个装饰器中,一般情况下每个装饰器只实现一个功能,使得实现装饰器变得简单,有利于装饰器功能的复用,可以给一个对象添加 多个装饰器,也可以把一个装饰器装饰多个对象,从而实现复用装饰器的功能。

    • 简化高层定义

    装饰者模式可以通过组合装饰器的方式,为对象添加任意多的功能;因此在高层定义的时候,不必把所有的功能都定义处理,只需要定义最基本的就可以了,在需要的时候可以通过组合装饰器的方式来完成所需的功能。
    2、缺点
    装饰模式把一系列复杂的功能分散到每个装饰器中,一般情况下每个装饰器只实现一个功能,这样会产生很多细粒度的对象,并且功能越复杂,细粒度对象越多。
    3、使用场合

    • 如果需要再不影响其他对象的情况下,以动态、透明的方式给对象增加职责,可以使用装饰者模式。
    • 如果不适合使用子类进行扩展的时候,可以考虑使用装饰者模式。装饰者模式使用的是对象组合的方式。
    • 不适合子类扩展:比如扩展功能需要的子类太多,造成子类数量呈爆炸性增长。

    注意:装饰者模式只是改变组件对象的外观Facde,并没有改变其内核