JS 外观模式

外观模式

简介

适配器模式、装饰者模式、代理模式和外观模式。这几种模式都属于“包装模式”,都是由一个对象来包装另一个对象。区别它们的关键仍然是模式的意图。

  • 适配器模式主要用来解决两个已有接口之间不匹配的问题,它不考虑这些接口是怎样实 现的,也不考虑它们将来可能会如何演化。适配器模式不需要改变已有的接口,就能够 使它们协同作用。

  • 装饰者模式和代理模式也不会改变原有对象的接口,但装饰者模式的作用是为了给对象增加功能。装饰者模式常常形成一条长的装饰链,而适配器模式通常只包装一次。

  • 代理模式通常也只包装一次。代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。 代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求做出一些处理之后,再把请求转交给本体对象。

  • 外观模式的作用倒是和适配器比较相似,有人把外观模式看成一组对象的适配器,但外观模式显著的特点是定义了一个新的接口。

上面的几种模式都封装了一个中间数据结构,用来作为实际访问者的中间层,在使用的时候注意区分中间数据结构的意图,才能选择合适的模式。

最少知识原则

最少知识原则(LKP)说的是一个软件实体应当尽可能少地与其他实体发生相互作用。这里的软件实体是一个广义的概念,不仅包括对象,还包括系统、类、模块、函数、变量等。下面引用《面向对象设计原理与模式》一书中的例子来解释最少知识原则:

某军队中的将军需要挖掘一些散兵坑。下面是完成任务的一种方式:将军可以通知上校让他叫来少校,然后让少校找来上尉,并让上尉通知一个军士,最后军士唤来一个 士兵,然后命令士兵挖掘一些散兵坑。

这种方式十分荒谬,不是吗?不过,我们还是先来看一下这个过程的等价代码:

gerneral.getColonel( c ).getMajor( m ).getCaptain( c ) .getSergeant( s ).getPrivate( p ).digFoxhole();

让代码通过这么长的消息链才能完成一个任务,这就像让将军通过那么多繁琐的步骤才能命令别人挖掘散兵坑一样荒谬!而且,这条链中任何一个对象的改动都会影响整条链的结果。

减少对象之间的联系

单一职责原则指导我们把对象划分成较小的粒度,这可以提高对象的可复用性。但越来越多的对象之间可能会产生错综复杂的联系,如果修改了其中一个对象,很可能会影响到跟它相互引用的其他对象。对象和对象耦合在一起,有可能会降低它们的可复用性。在程序中,对象的“朋友”太多并不是一件好事,“城门失火,殃及池鱼”和“一人犯法,株连九族”的故事 时有发生。

最少知识原则要求我们在设计程序时,应当尽量减少对象之间的交互。如果两个对象之间不必彼此直接通信,那么这两个对象就不要发生直接的相互联系。常见的做法是引入一个第三者对象,来承担这些对象之间的通信作用。如果一些对象需要向另一些对象发起请求,可以通过第三者对象来转发这些请求。

外观模式

外观模式是最少知识原则的体现。

外观模式主要是为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使子系统更加容易使用,如下图所示:

外观模式抽象图

外观模式的作用是对客户屏蔽一组子系统的复杂性。外观模式对客户提供一个简单易用的高层接口,高层接口会把客户的请求转发给子系统来完成具体的功能实现。大多数客户都可以通过请求外观接口来达到访问子系统的目的。但在一段使用了外观模式的程序中,请求外观并不是强制的。如果外观不能满足客户的个性化需求,那么客户也可以选择越过外观来直接访问子系统。

拿全自动洗衣机的一键洗衣按钮举例,这个一键洗衣按钮就是一个外观。如果是老式洗衣机,客户要手动选择浸泡、洗衣、漂洗、脱水这 4 个步骤。如果这种洗衣机被淘汰了,新式洗衣机的漂洗方式发生了改变,那我们还得学习新的漂洗方式。而全自动洗衣机的好处很明显,不管洗衣机内部如何进化,客户要操作的,始终只是一个一键洗衣的按钮。这个按钮就是为一组子系统所创建的外观。但如果一键洗衣程序设定的默认漂洗时间是 20 分钟,而客户希望这个漂洗时间是 30 分钟,那么客户自然可以选择越过一键洗衣程序,自己手动来控制这些“子系统”运转。

外观模式容易跟普通的封装实现混淆。这两者都封装了一些事物,但外观模式的关键是定义一个高层接口去封装一组“子系统”。子系统在 C++或者 Java 中指的是一组类的集合,这些类相互协作可以组成系统中一个相对独立的部分。在 JavaScript 中我们通常不会过多地考虑“类”,如果将外观模式映射到 JavaScript 中,这个子系统至少应该指的是一组函数的集合。

最简单的外观模式应该是类似下面的代码:

var A = function(){ a1(); a2(); }
var B = function(){ b1(); b2(); }
var facade = function(){ A(); B(); }
facade();

外观(Facade)模式是“迪米特法则”的典型应用,它有以下主要优点。

  1. 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
  2. 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
  3. 降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。

外观(Facade)模式的主要缺点如下。

  1. 不能很好地限制客户使用子系统类,很容易带来未知风险。
  2. 增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。

参考文章

  • [1]曾探.JavaScript设计模式与开发实践[M].北京:高等教育出版社,2015:258-262,250.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值