今天开始正式学习设计模式,上一篇Blog【Java设计模式 零】前言:我为什么要学习设计模式在阐述了为什么这个阶段和时机比较适合学习设计模式之后,就正式开始学习了,依据总分总的专栏文章体系,本篇Blog先来个概述,对设计模式定义、基本原则和分类进行深入的认知,一步一个脚印开始探索。
设计模式定义
首先来了解下什么是设计模式?应用一段设计模式的基本定义,以下三段描述分别从定义、目的、使用范围将设计模式从不同的角度解读:
- 设计模式代表了最佳实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。
- 使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。
- 项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。
综合上边三段话其实我们可以认为【设计模式】是一套可以让我们统一认知、最佳实践的代码设计思路。它的作用正如同【Java编程语言规范】是一套让我们统一认知、最佳实践的代码编写方式。他们二者一个从代码设计角度、一个从代码编写角度将我们不同人的杂七杂八的代码正规化、统一化、工程化。我们程序员彼此之间就能据此行业优选的共识基础之上进行高效、便捷的交流,所以其实我认为设计模式和编程语言规范是我们进阶的必经之路,在会用各种框架胜任工作之后,再通过提高自己的设计思维和代码质量,把工作做的更好。
设计模式目标
内聚性,又称块内联系,指模块的功能强度的度量,即一个模块内部各个元素彼此结合的紧密程度的度量。内聚性是对一个模块内部各个组成元素之间相互结合的紧密程度的度量指标。模块中组成元素结合的越紧密,模块的内聚性就越高,模块的独立性也就越高。理想的内聚性要求模块的功能应明确、单一,即一个模块只做一件事情。
耦合性也叫块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,其耦合性就越强,模块的独立性则越差,模块间耦合的高低取决于模块间接口的复杂性,调用的方式以及传递的信息
模块的内聚性和耦合性是两个相互对立且又密切相关的概念。所以随着时间的推移代码难以扩展和维护的原因无外乎就是内聚性太差耦合性太高。所以这是解决问题的目标,也是设计模式的目标:高内聚,低耦合。其中高内聚指的是类内成员之间的关系,而低耦合则是类之间的关系。
设计模式原则
和任何要扩展的规范一样,我们需要一套原则来描述我们规范的核心诉求,规范的内容就是为了满足核心原则使用而提供的内容,所以这里就不得不提到设计模式的六大核心原则:
- 开闭原则(Open Close Principle):对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要尽可能使用接口和抽象类高内聚 低耦合
- 里氏代换原则(Liskov Substitution Principle),任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。 高内聚 低耦合
- 依赖倒转原则(Dependence Inversion Principle):开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体高内聚 低耦合
- 接口隔离原则(Interface Segregation Principle):使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。低耦合
- 合成复用原则(Composite Reuse Principle):尽量使用合成/聚合、组合的方式,而不是使用继承低耦合
- 单一职责原则:一个类应该只有一个引起变化的原因,其实就是为了提高内聚性一个说明,它的目的就保证了内聚性,只有一个引起变化的原因,说明类内成员之间的关系较高,内聚性不强的就不要写到一个类中 高内聚
还有一个法则:
- 迪米特法则(Demeter Principle):也叫最少知道原则,一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立低耦合
这六个原则一个法则其实都是基于设计模式目标而提出的。
这些原则的目的只有一个:降低对象之间的耦合,增加程序的可复用性、可扩展性和可维护性。说实话此时对于六个原则的理解还较为模糊,只能大概体会下浅层意思,在接下来的学习过程中体会原则的重要性吧。
设计模式分类
根据设计模式的参考书 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 中所提到的,总共有 23 种设计模式。这些模式可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。
模式分类 | 模式分类描述 | 模式 |
创建型 | 这些模式关注实例对象的创建 ,共5种 | 工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式 |
结构型 | 这些模式关注类和对象的组合 ,共7种 | 适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式 |
行为型 | 这些模式关注对象之间的通信 ,共11种 | 策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式 |
当然,在Web开发过程中衍生了一些基于基础设计模式的复合设计模式,也形成了一定的范式:J2EE 设计模式。例如MVC模式,这类模式在掌握了基本的设计模式之后再进行探究
创建型模式
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成。就像我们去商场购买商品时,不需要知道商品是怎么生产出来一样,因为它们由专门的厂商生产。创建型模式分为以下几种。
- 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
- 工厂方法(FactoryMethod)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
- 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
- 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
- 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
以上 5 种创建型模式,除了工厂方法模式属于类创建型模式(继承形式),其他的全部属于对象创建型模式(组合形式),接下来会一一学习到
结构型模式
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。
由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。结构型模式分为以下 7 种:
- 适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
- 装饰(Decorator)模式:动态地给对象增加一些职责,即增加其额外的功能。
- 代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
- 桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现的,从而降低了抽象和实现这两个可变维度的耦合度。
- 外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
- 享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
- 组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
以上 7 种结构型模式,除了适配器模式分为类结构型模式和对象结构型模式两种,其他的全部属于对象结构型模式
行为型模式
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。
行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。行为型模式是 GoF 设计模式中最为庞大的一类,它包含以下 11 种模式。
- 模板方法(Template Method)模式:定义一个操作中的算法骨架,将算法的一些步骤延迟到子类中,使得子类在可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
- 策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
- 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
- 职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
- 状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。
- 观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
- 中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
- 迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
- 访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
- 备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
- 解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。
以上 11 种行为型模式,除了模板方法模式和解释器模式是类行为型模式,其他的全部属于对象行为型模式
总结一下
研究问题的方法,提出问题:随着时间推移,系统越来越难以维护,越来越臃肿;提出解决问题的目标:如果代码的设计是高内聚、低耦合的就好了;基于目标设定处理原则:开闭原则、里式替换原则、依赖倒置原则、接口隔离原则、合成复用原则、单一职责原则、迪米特法则;基于处理原则总结在类的使用过程中如何贯彻原则、实现目标的代码设计最佳实践:5种创建型设计模式、7种结构型设计模式、11种行为型设计模式。这样就厘清了前因后果了,知道为什么而去学习设计模式。