设计模式(一)

一、简介

1、模式由来

与很多软件工程技术一样,模式起源于建筑领域,毕竟与只有几十年历史的软件工程相比,已经拥有几千年沉淀的建筑工程有太多值得学习和借鉴的地方。每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心,通过这种方式,我们可以无数次地重用那些已有的成功的解决方案,无须再重复相同的工作,在特定环境下人们解决某类重复出现问题的一套成功或有效的解决方案。

软件模式是将模式的一般概念应用于软件开发领域,即软件开发的总体指导思路或参照样板。软件模式并非仅限于设计模式,还包括架构模式、分析模式和过程模式等。

分析模式:分析包括透过需求的表面去了解本质问题。分析模式就是业务模型中的一组表达了通用基础结构的概念。
架构模式:架构模式表达了软件系统的基础结构组织模型。它提供了一套预先确定的子系统和组件,说明了其职责,还包括为组织它们之间的关系而规定的规则和指导原则。
设计模式:一个设计模式提供了一个模型,此模型提炼了一个软件系统的子系统或组件。它描述了一个通用的可复用的组件,此组件解决了某特定上下文的一般性的设计问题。
软件模式的基础结构主要由四部分构成,包括问题描述【待解决的问题是什么】、前提条件【在何种环境或约束条件下使用】、解法【如何解决】和效果【有哪些优缺点】。

2、设计模式

设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解并且保证代码可靠性。

设计模式高频率使用的模式有23种,可分为创建型,结构型和行为型三种,其中创建型模式主要用于描述如何创建对象,主要特点将对象的创建与使用分离;结构型模式主要用于描述如何实现类或对象的组合,主要特点是结构的壮大,将类或对象按某种布局组成更大的结构;行为型模式主要用于描述类或对象怎样交互以及怎样分配职责,主要特点就是处理的是对象之间的通信方式,控制类与类之间的通讯与相互控制,解决类之间的复杂的交互项操作。根据某个模式主要是用于处理类之间的关系还是对象之间的关系,设计模式还可以分为类模式和对象模式,类模式是用继承而对象模式是用关联关系进行组合。

3、模式用处

设计模式提供了一套通用的设计词汇和一种通用的形式来方便开发人员之间沟通和交流,使得设计方案更加通俗易懂。是从许多优秀的软件系统中总结出的成功的、能够实现可维护性复用的设计方案,使用这些方案将可以让我们避免做一些重复性的工作。大部分设计模式都兼顾了系统的可重用性和可扩展性,这使得我们可以更好地重用一些已有的设计方案、功能模块甚至一个完整的软件系统,避免我们经常做一些重复的设计、编写一些重复的代码。此外,随着软件规模的日益增大,软件寿命的日益变长,系统的可维护性和可扩展性也越来越重要,许多设计模式将有助于提高系统的灵活性和可扩展性,让我们在不修改或者少修改现有系统的基础上增加、删除或者替换功能模块。学习设计模式将有助于更加深入地理解面向对象思想。

 

二、设计原则

面向对象设计原则为支持可维护性复用而诞生,这些原则蕴含在很多设计模式中,它们是从许多设计方案中总结出的指导性原则。在面向对象设计中,可维护性的复用是以设计原则为基础的。每一个原则都蕴含一些面向对象设计的思想,可以从不同的角度提升一个软件结构的设计水平。

1、单一职责原则(Single Responsibility Principle, SRP)

一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。单一职责原则是实现高内聚、低耦合的指导方针,它是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关实践经验。

2、开闭原则(Open-Closed Principle, OCP)

一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。软件实体可以指一个软件模块、一个由多个类组成的局部结构或一个独立的类。抽象化是开闭原则的关键。在扩展时无须修改现有代码,使得软件系统在拥有适应性和灵活性的同时具备较好的稳定性和延续性,随着软件规模越来越大,软件寿命越来越长,软件维护成本越来越高,设计满足开闭原则的软件系统也变得越来越重要。在很多面向对象编程语言中都提供了接口、抽象类等机制,可以通过它们定义系统的抽象层,再通过具体类来进行扩展。如果需要修改系统的行为,无须对抽象层进行任何改动,只需要增加新的具体类来实现新的业务功能即可,实现在不修改已有代码的基础上扩展系统的功能,达到开闭原则的要求。

3、里氏代换原则(Liskov Substitution Principle, LSP)

所有引用基类(父类)的地方必须能透明地使用其子类的对象。在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法,尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口。里氏代换原则是实现开闭原则的重要方式之一。

4、依赖倒转原则(Dependency Inversion  Principle, DIP)

抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。为了确保该原则的应用,一个具体类应当只实现接口或抽象类中声明过的方法,而不要给出多余的方法,否则将无法调用到在子类中增加的新方法。

开闭原则是目标,里氏代换原则是基础,依赖倒转原则是手段,它们相辅相成,相互补充,目标一致,只是分析问题时所站角度不同而已。

5、接口隔离原则(Interface  Segregation Principle, ISP)

使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。当一个接口太大时,我们需要将它分割成一些更细小的接口。这里的“接口”往往有两种不同的含义:一种是指一个类型所具有的方法特征的集合,仅仅是一种逻辑上的抽象;另外一种是指某种语言具体的“接口”定义,有严格的定义和结构。一方面导致该接口的实现类很庞大,在不同的实现类中都不得不实现接口中定义的所有方法,灵活性较差;另一方面由于客户端针对大接口编程,将在一定程序上破坏程序的封装性,客户端看到了不应该看到的方法,没有为客户端定制接口。

6、迪米特法则(Law of  Demeter, LoD)

一个软件实体应当尽可能少地与其他实体发生相互作用。迪米特法则要求限制软件实体之间通信的宽度和深度,迪米特法则可降低系统的耦合度,使类与类之间保持松散的耦合关系。在迪米特法则中,对于一个对象与当前对象本身,以参数形式传入到当前对象方法中的对象,当前对象的成员对象,当前对象集合里的对象,当前对象所创建的对象等可以直接通信。在对其他类的引用上,一个对象对其他对象的引用应当降到最低,可以通过引入一个专门用于控制界面控件交互的中间类(Mediator)来降低界面控件之间的耦合度。

7、合成复用原则(Composite Reuse Principle, CRP)

尽量使用对象组合,而不是继承来达到复用的目的。复用时要尽量使用组合/聚合关系(关联关系),少用继承,组合/聚合可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少;其次才考虑继承,在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,而且继承会将基类的实现细节暴露给子类,由于基类的内部细节通常对子类来说是可见的,如果基类发生改变,那么子类的实现也不得不发生改变,因此需要慎重使用继承复用。

你可能感兴趣的:(设计模式)