结构型设计模式之装饰器模式

装饰器模式

逢年过节,或者是遇上心仪的女孩,我们都会以送礼物的方式来让人与人间的关系得到升温。在送礼时,我们首先会先去挑一件合适的礼品,然后再对其进行包装,接着送到我们想送的人手上。那在包装时,有的人会选择一个很好看的礼盒,外面再套上一层礼纸,最后是加一条彩带。也有的人比较简单,直接加个礼盒就完事。当然,也有的人比较环保,直接套个礼纸,再给拧成一个糖果的形状就送人了。但是无论你怎么包装,都不会改变那件最本质的物品和它背后所代表的价值。并且,你在包装时,加了礼盒再包礼纸和加了礼纸再包礼盒都能够给这件礼物添加一样的外在条件。像这种给一件礼品(原始组件)添加各种外在条件(包装组件)并且不会改变礼品(原始组件)价值的方式在设计模式上成为装饰器模式。

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

动态地给一个对象赋予额外的职责。就扩展功能而言,装饰器提供了一种比使用子类更加灵活的替代方案。

装饰模式是一种用于替代继承的技术,通过一种无须定义子类的方式给对象动态添加职责,利用对象间的关联关系取代类之间的耦合关系。它可以动态的给对象添加或者移除相应的职责,需要装饰的具体构建类和用于装饰的装饰类都可以独立进行变化,具备了较为理想的可扩展性,同时也符合开闭原则,因为它可以通过配置的方式随时切换或者引入新的装饰模型。

装饰器模式的参与角色:① 抽象组件 Component 、② 被装饰组件 ConcreteComponent 、③ 装饰组件 Decorate、 ④ 具体装饰组件 ConcreteDecorate;

如下图,Component将被装饰组件和装饰组件抽象化成一个公共父类,这样能够使客户端统一的对抽象层进行编程,同时也能对一个组件进行多次装饰。Decorate和Component是一个聚合的关系,通过引用方式调用组件的方法,并在此之上调用具体装饰类的附加方法达到给组件装饰的效果,需要注意的是,Decorate的operation()方法只是调用了原始组件的operation()方法而已,真正的装饰是在具体装饰类中进行装饰。

在这里插入图片描述
根据上图的常见装饰器模式代码如下:
在这里插入图片描述
在这里插入图片描述
这里可以举一个前文的例子,就是对礼物进行包装的例子,下面对其进行编码。
在这里插入图片描述
对应代码:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
运行结果:
在这里插入图片描述
代码的执行流程即将Gift实例放到AddColouredRibbonDecorate中,再将添加了彩带的礼物放到AddGiftBoxDecorate中,最后是将添加了彩带和礼盒的礼物再放到了AddGiftPaperDecorate对象里面。

整体的方法调用链 (方法栈)
addGiftPaperDecorate.show()
addGiftPaperDecorate.addGiftPaper()
super.show()
super此时的component指向了addGifyBoxDecorate
addGifyBoxDecorate .addGiftBox()
super.show()
super的component指向了addColouredRibbonDecorate
addColouredRibbonDecorate.addColouredRibbon()
super.show()
super的component指向gift
gift.show()
结束

如果希望只是将礼物添加一个礼盒和彩带的话,则直接生成AddGiftBoxDecorate和AddColouredRibbonDecorate的实例,再将Gift实例setter到这两对象里面去就可以了。而如果这三种装饰行为还无法满足要求的话,则只需要再定义一个装饰类,并且将gift按你想要的顺序放到装饰器中就可以,不用修改原有的代码,扩展性较好。

装饰器模式又可以分为透明和非透明两种方式,具体区别如下

透明装饰器

所有的代码都是基于抽象进行编程,即所有的装饰器的方式方法都统一在抽象层中已定义好的方法里进行调用,这种方式对于客户端而言是透明(Transparent)的。在用透明装饰器方式进行书写代码时,不管是装饰对象还是被装饰对象都统一用抽象组件对其进行引用。

例如上面的礼物包装的main() 方法里的代码编写方式就是基于透明装饰器的方式。即用GiftComponent去引用Gift、AddGiftBoxDecorate、AddGiftPaperDecorate、AddColouredRibbonDecorate的对象,而不是用AddGiftPaperDecorate obj = new AddGiftPaperDecorate()这种指明具体类型的方式去接收

透明装饰器方式的话可以让客户端无需关心装饰前和装饰后的对象的区别,此外还可以对其进行多次装饰以得到功能更多、更复杂的装饰对象。但是每个装饰器中的装饰方法无法在客户端进行调用,因为抽象层并没有提供它们的相应方法。

半透明装饰器

相对于透明装饰器,非透明装饰器的设计方式较为简单。并且满足了有些时候需要单独的调用装饰方法的要求。被装饰组件依旧可以用抽象去进行引用,而装饰器则是需要指明具体类型去进行接收,即AddGiftPaperDecorate obj = new AddGiftPaperDecorate()这种方式,而不是GiftComponent comp = new AddGiftPaperDecorate(),否则就无法单独调用它的装饰方法。这种方式确实会更为灵活,但是客户端需要有区别的对待被装饰前和被装饰后的对象,无法透明的对其进行统一处理,因而也无法对被装饰组件进行多次装饰。

装饰器模式的适用场景:

  1. 在不影响其他类的情况下动态、透明的给单个对象添加职责。
  2. 如果系统当前存在大量的独立扩展 或者 类被设计为终类时可以使用此种方式给对象添加职责。

优点:

  1. 与继承扩展相比,装饰模式会更加灵活,不会导致类的个数急剧增加。
  2. 可以动态的给对象赋予职责,在配置文件中指明装饰类,从而使对象有不同的行为。
  3. 可对一个对象进行多次装饰,并且可以根据排列组合创建出许多功能更加强大的对象。
  4. 具体被装饰类和具体装饰器两者间可独立变化,可以根据具体情况增加新的被装饰类或者具体装饰器,且不会影响原有的代码,符合开闭原则。

缺点:

  1. 该模式会导致系统存在大量的小对象,占用资源,影响系统效率。
  2. 相比直接用继承进行扩展,此种方式会导致程序的复杂性增加,debugger时会更加困难。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值