携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第25天,点击查看活动详情
1.目录:
- 装饰模式(Decorator)
- 门面模式(Facade)
- 享元模式(Flyweight)
- 代理模式(Proxy)
- 组合模式(Composite)
- 桥接模式(Bridge)
- 适配器模式(Adapter)
2.设计模式
23种经典设计模式,但是不仅仅局限于23种经典设计模式,其他还有比如说MVC模式。
3.结构型模式总结:
结构型模式,顾名思义讨论的是类和对象的结构,它采用继承机制来组合接口或实现(类结构型模式),或者通过组合一些对象,从而实现新的功能(对象结构型模式)。这些结构型模式,它们在某些方面具有很大的相似性,仔细推敲,侧重点却各有不同。
4.结构型模式概述:
- Adapter模式通过类的继承或者对象的组合侧重于转换已有的接口;
- Bridge模式通过将抽象和实现相分离,让它们可以分别独立的变化,它强调的是系统沿着多个方向的变化;
- Decorator模式采用对象组合而非继承的手法,实现了在运行时动态扩展对象功能的能力,它强调的是扩展接口;
- Composite模式模糊了简单元素和复杂元素的概念,它强调的是一种类层次式的结构;
- Facade模式将复杂系统的内部子系统与客户程序之间的依赖解耦,它侧重于简化接口,更多的是一种架构模式;
- Flyweight模式解决的是由于大量的细粒度对象所造成的内存开销的问题,它与Facade模式恰好相反,关注的重点是细小的对象;
- Proxy模式为其他对象提供一种代理以控制对这个对象的访问,它注重于增加间接层来简化复杂的问题;
5.桥接模式与装饰模式:
这两个模式在一定程度上都是为了减少子类的数目,避免出现复杂的继承关系。但是它们解决的方法却各有不同,装饰模式把子类种比基类种多出来的部分放到单独的类里面,以适应新功能增加的需要,当我们把描述新功能的类封装到基类的对象里面时,就得到了所需要的子类对象,这些描述新功能的类通过组合可以实现更多的功能组合,装饰模式的简略图如下:
桥接模式则把原来的基类的实现化细节抽象出来,在构造到一个实现化的结构中,然后再把原来的基类改造成一个抽象化的等级结构,这样就可以实现系统在多个维度上的独立变化,桥接模式的简略图如下:
6.门面模式和代理模式:
门面模式和代理模式解决问题的侧重点不同,但是它们解决问题的手法却是一样的,即都是引入了间接层的手法,这也是我们软件系统中经常用的一种手法。门面模式虽然侧重于简化接口,但是在某些情况下,门面模式也可以兼任代理模式的职责,例如门面对象有可能是位于另一个地址空间对象的远程代理,这时候我们可以叫做门面代理模式,或者代理门面模式。它们的简略类图如下:
7.适配器模式:
适配器模式重点在转换接口,它能够使原本不能在一起工作的两个类一起工作,所以经常用在类库复用,代码迁移等方面,有一种亡羊补牢的味道。类适配器和对象适配器可以根据具体实际情况来选用,但一般情况建议使用对象适配器模式,如下图所示,左边是类适配器模式,右边是对象适配器模式:
8.对变化的封装:
如何应对变化,是软件开发的一个永恒的主题,也许我们不能够杜绝变化的发生,但至少我们可以通过一些手段让变化降到最低。“找到系统可变的因素,将之封装起来”,通常就叫做对变化的封装。
9.什么事“开-闭”原则:
开闭原则指的是一个软件实体应对扩展开放,对修改关闭(Software entities should be open for extension, but closed for modification)。这个原则是说在设计一个模块的时候,应该使这个模块可以在不被修改的前提下被扩展,换言之,应该可以不必修改源代码的情况下改变这个模块的行为。
10.满足开闭原则的软件系统的优越性:
- 通过扩展已有的软件系统,可以提供新的行为,以满足对软件的新需求,使变化中的软件系统有一定的适应性和灵活性。
- 已有的软件模块,特别是最重要的抽象层模块不能在修改,这就使变化中的软件系统有一定的稳定性和延续性。
11.实现开闭原则的关键:
- 抽象化是解决问题的关键,在面向对象的编程语言里,可以给系统定义出一套相对较为固定的抽象设计,此设计允许无穷无尽的行为在实现层被实现。在语言里,可以给出一个或多个抽象类或者接口,规定所有的具体类必须提供的可扩展性,因此,在任何扩展情况下都不会改变。这就使得系统的抽象不需要修改,从而满足了开闭原则的第二条,对修改关闭。
- 同时,由于从抽象层导出一个或多个新的具体类可以改变系统的行为,因此系统的设计对扩展是开放的,这就满足了开闭原则的第一条。
12.对可变性的封装原则:
这是对开闭原则的另外一种描述,它讲的是找到一个系统的可变因素,将之封装起来。该原则意味着两点:
- 一种可变性不应当散落在代码的很多角落,而应当封装到一个对象里面。继承应当被看做是封装变化的方法,而不应该被认为是一种从一般对象生成特殊对象的方法。
- 一种可变性不应当与另外一种可变性混合在一起。这意味着一般的继承层次不会超过两层。
13.抽象化与实现化的简单实现:
在这个继承结构中,第一层是抽象化,它封装了抽象的业务逻辑,这是系统中不变的部分;第二层是实现化,它是具体的业务逻辑的实现,封装了系统中变化的部分,这个实现允许实现化角色多态性的变化。
也就是说,客户端依赖的是业务逻辑的抽象化类型的对象,而与抽象化的具体实现无关,不在乎它到底是“实现化”,“实现化2”还是“实现化3”,如下图所示:
每一种继承关系都封装了一个变化因素,而一个继承关系不应当处理两个变化因素,换言之,这种简单继承关系不能处理抽象化与实现化都变化的情况,如下图所示:
上图中的两个变化因素应当是独立的,可以在不影响另一者的情况下独立的变化,如下面这两个等级结构分别封装了自己的变化因素,由于每一个变化因素都是可以通过静态关系表达的,因此分别使用继承关系实现,如下图:
在抽象化和实现化之间的联系怎么办呢?下面这种设计就是继续使用继承进行静态关系设计的类图:
这样的设计其实存在着很多的问题,首先出现的是多重的继承关系,随着具体实现化的增多,子类的继承关系会变得异常复杂;其次如果出现新的抽象化修正或者新的具体实现角色,就只好重新修改现有系统中的静态关系,以适应新的角色,这就违背了开放-封闭原则。正确的设计应该是使用两个独立的等级接口封装两个独立的变化因素,并在它们之间使用聚合关系,以达到功能复用的目的,这就回到了我们的桥接模式上,如下图所示:
14.总结:
从另一个角度讲,一个好的设计通常没有多于两层的继承等级结构,或者说,如果出现两个以上的变化因素,就需要找出哪一个因素是静态的,可以使用静态关系,哪一个是动态的,必须使用聚合关系。