Skip to content

Latest commit

 

History

History
53 lines (36 loc) · 9.65 KB

ending-of-structural-patterns.md

File metadata and controls

53 lines (36 loc) · 9.65 KB

关于结构型模式的讨论


结构型模式共有 7 个,他们分别是适配器(Adapter)、桥(Bridge)、组合(Composite)、装饰器(Decorator)、门面(Facade)、享元(Flyweight)和代理(Proxy)模式。结构型模式重在描述如何将类或对象按照某种布局方式来组成更大的结构,相对于行为型模式来说,结构型模式更重要的是如何组织现有类或对象之间的关系,以此来满足我们所需的特性和功能。

一、结构和目的讨论

1.1 结构对比

七个模式的类图结构(简化版)汇总情况如上图所示,从类图结构上来看,结构型模式差异较大。适配器模式强调兼容两个不匹配的接口,所以客户端发出的请求需要经过适配器Adapter的转发,桥模式则将两个互不影响的维度通过对象组合(Abstraction#implementor)的方式进行解耦。代理模式强调对象访问控制,为每一个真实对象anRealSubject提供一个代理对象anProxy,代理对象将在合适的时机将客户端的请求转发给真实对象。享元模式则是为所有可共享的对象提供一个缓存池FlyweightFactory,客户端可在缓存池中获取复用对象,以此降低空间占用。门面模式没有固定的结构,任何时候,只要你的系统内部足够复杂,都可以为外界提供一个门面,在门面内部处理好所有内部的交互,降低客户端的使用复杂度。

组合模式和装饰器模式在一定程度上呈现出来相似性,例如他们都能实现对象的递归组合,并且他们的类图结构也极为相似。他们都有具体的组件(装饰器的ConcreteComponent、组合的叶子节点Leaf),并且组件都可被聚合对象(装饰器Decorator、组合对象Composite)包装。甚至当装饰器模式的抽象装饰器Decorator只有一个实现类时,我们省略抽象的装饰器,此时装饰器模式和组合模式的类图几乎一模一样。

1.2 目的对比

接下来,让我们再回顾一下结构型模式的意图。

  • 适配器模式将一个类的接口转换成客户希望的另外一个接口,适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
  • 桥模式将抽象部分与它的实现部分分离,使它们都可以独立地变化。
  • 组合模式将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
  • 装饰器模式在不改变原有对象结构的基础情况下,动态地给该对象增加一些额外功能的职责。
  • 门面模式为子系统中的一组接口提供一个一致的界面,门面模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
  • 享元模式运用共享技术有效地支持大量细粒度的对象。
  • 代理模式为其他对象提供一种代理以控制对这个对象的访问。

在上面我们提到组合模式和装饰器模式在结构上是如此的相似,尽管如此,他们还是在某些方面表现出各自的特性。从意图来看,装饰器模式依靠层层包装的机制,动态的给原始对象增加一些职责,每个装饰器对象内部仅维护一个组件(或者包装器)对象;组合模式的主要目的是为了表示部分-整体的树形关系,每一个组合对象内部均可维护多个叶子节点(或者组合对象),相较于装饰器模式来说,组合模式的侧重点是对象的聚集(比如获取子节点列表的行为Component#getChildren()、添加子节点的行为Component#add(Component)),是如何将一系列对象组织成一个树形结构,以便我们可以像处理单个对象一样来处理多个组合的对象。我们可以武断的认为装饰器模式是退化的(不注重对象的聚集)、内部仅维护一个组件的组合模式,但你必须牢记他们的出发点完全不一样。

二、为何属于结构型模式

在前面我们说,结构型模式更多的是处理类或对象之间的组合关系,这看起来如此简单的概括理解起来却相当不容易。或许你能很轻易的记住代理模式属于结构型模式,但你想过为什么代理模式就不属于行为型模式吗?

也许从一开始你就没有思考过这个问题,因为在潜意识里,你就觉得属于哪种类型的模式并不重要。你感兴趣的只是该如何弄明白一个模式,然后在你以后的项目中能自然而然地使用起来。遗憾的是,在那之后你再也没有想起过它,甚至你在所有的模式中挑来挑去,却不知道应该用哪些模式来解决你所面临的问题。事实上,如果模式的类型毫无用处,作者就不会多此一举的给他们划分类型。

有一种例外的情况是你已经对某些模式相当的熟悉,你清楚的知道他们能为你的项目带来什么收益和付出何种代价。你不需要翻阅字典,甚至你都说不清楚你用的是哪一种模式或者是哪些模式的组合,但你知道这样用能解决你的问题,并且会在另外的一些方面带给你惊喜,你也能很好的掌控引入他们后带来的的风险和不稳定性。如果你当前处在这一阶层,你在设计模式方面的造诣恐怕已经相当深厚了,本系列的文章你都可以不必再看了,因为你可能从这里得不到什么东西了。

学走先学爬,我个人认为弄清楚一个模式为什么属于某个分类很重要,因为模式的精髓往往就藏在这个刨根问底的过程中,弄明白模式为什么属于某个分类对于模式的理解和使用具有相当大的帮助。

三种模式分类中,创建型模式很容易鉴别,因为创建型模式的所有模式都与产品的创建有关,但结构型模式和行为型模式则显得不那么容易辨别。在讨论为什么这几种模式被划分为结构型模式之前,我们先说明一下什么是结构型模式和行为型模式。结构型模式在前面我们已经提到,重在处理类或对象之间的组合关系,换句简单的话说,结构型模式主要在于讨论现有的类或对象之间该如何进行组织,才能让整体结构发挥出更大的扩展性以及灵活性。而行为型模式则把重点放在行为上,他更关注类或者对象之间该如何交互与通信,该如何划分职责。但这不意味着结构型模式中并不存在任何行为,也不是说行为型模式能完全脱离结构的组织。例如我们在讨论一个模式为什么属于结构型模式时,有一个隐形的前提:相较于这个模式中所展现出来的行为,类或对象之间的结构特性更加重要,并且我们的出发点也是如何有效的组织这些类或对象之间的关系。

  • 适配器模式:适配器模式的结构性体现在通过继承或者依赖被适配者(adaptee)的组织方式,使得原本多个独立的类在一起能协调工作,如果没有这样的组织关系,现有的多个独立类将无法在一起工作;
  • 桥模式:桥模式建议我们从原本平行的两个维度中抽离出一个维度,通过组合的方式连接这两个维度。正如他的名字一样,桥模式在两个维度之间搭建一座桥,以此组织两个维度之间的关系,正因为这样的组织方式,使得多维度实现了解耦并且我们可以独立的改变其中的某个维度;
  • 组合模式:组合模式的结构性体现的比较明显,他的主要目的就是为了表示树形结构,并且其内部独特的组织方式使得叶子对象和容器对象在一定程度上表现出了一致的行为;
  • 装饰器模式:装饰器模式很容易被误解为行为型模式,但实际上有一个足够的理由证明他属于结构型模式:装饰器模式就像是俄罗斯套娃一样,一层一层的对现有对象结构进行包装;
  • 门面模式:门面模式的目的是为了给外界提供一个容易使用的高层接口,使得客户端与子系统的所有交互都可以直接和高层接口进行。为了达到这样的目的,我们需要提供一个对外的门面,并且在门面对象的内部屏蔽掉子系统内部的更多细节。总结来看,为了客户端的易用性,我们提供了门面,这是门面模式的结构性体现;
  • 享元模式:如果让你用一个机制来表述享元模式的核心,你会想到什么呢?没错,池化,为了共享现有的对象,我们提供了一个对象缓存池,客户端可以直接从缓存池中获取已有的对象进行复用。所以,享元模式的结构性就是在客户端和对象之间增加了一层缓存池;
  • 代理模式:代理模式强调的是对象访问的控制,为了达到目的,我们在客户端与真实的对象之间引入了一个代理对象,由代理对象负责在合适的时机将请求转发给被代理对象。和享元模式、门面模式类似,为了控制对象的访问,引入了代理,这就是代理模式的结构性。

总的来说,结构型模式的结构性主要体现在两个方面。一方面,出于某个目的考虑,我们在客户端和对象(系统)之间引入了一个独立的层次,比如适配器模式、门面模式、享元模式和代理模式;另一方面,依赖于某种特殊的类或对象的组织关系,使得整体结构在某些方面呈现出我们所需的特质,就像是桥模式、组合模式和装饰器模式那样。

附录

回到主页