学计算机的那个

不是我觉到、悟到,你给不了我,给了也拿不住;只有我觉到、悟到,才有可能做到,能做到的才是我的.

0%

设计模式六大原则

设计模式六大原则

单一职责原则(Single Responsibility Principle,简称SRP )

  • 核心思想:一个类应该有且只有一个变化的原因(There should never be more than one reason for a class to change.)
  • 职责的定义:”一个变化的原因(a reason for change)”,如果你能想出多于一种动机来更改一个类,则这个类就包含多于一个职责。
  • 需注意:
  1. 单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计得是否优良,但是“职责”和“变化原因”都是不可以度量的,因项目和环境而异。
    
  2. 一个较好的设计是将两个职责完全地隔离到不同的类当中。

开放封闭原则(Open Close Principle,简称OCP)

  • 引用:1988年Bertrand Meyer 就给出了指导建议,他创造了当下非常著名的开放封闭原则。套用他的原话:”软件实体(类、模块、函数等)应对扩展开放,但对修改封闭。”(Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.)
  • 核心思想:尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化
  • 通俗来讲:
    1. 一个软件产品在生命周期内,都会发生变化,既然变化是一个既定的事实,我们就应该在设计的时候尽量适应这些变化,以提高项目的稳定性和灵活性。
      
  1. 更改符合开放封闭原则的程序是通过增加新的代码,而不是修改已存在的代码.
  2. 这个原则是面向对象设计的核心,是构建可维护性和可重用性代码的基础。抽象(Abstraction)和多态(Polymorphism)是实现这一原则的主要机制,而继承(Inheritance)则是实现抽象和多态的主要方法。
  • 问题:符合开放封闭原则的模块都有两个主要特性:1. 它们 “面向扩展开放(Open For Extension)”,2. 它们 “面向修改封闭(Closed For Modification)”。通常扩展模块行为的常规方式就是修改该模块,如何使这两个相反的特性共存呢?

    1
    2
    抽象是关键
    使用面向对象设计技术时,可以创建固定的抽象和一组无限界的可能行为来表述。这里的抽象指的是抽象基类,而无限界的可能行为则由诸多可能衍生出的子类来表示。为了一个模块而篡改一个抽象类是有可能的,而这样的模块则可以对修改封闭,因为它依赖于一个固定的抽象。然后这个模块的行为可以通过创建抽象的衍生类来扩展.
  • 策略:

  1. 使用抽象来获取显示地闭合
  2. 使用 “数据驱动(Data Driven)” 的方法来达成闭合。

里氏替换原则(Liskov Substitution Principle,简称LSP)

  • 核心思想:使用基类对象指针或引用的函数必须能够在不了解衍生类的条件下使用衍生类的对象。(Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it)。
  • 需注意:
  1. 如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系 采用依赖、聚合、组合等关系代替继承。通常来说,继承关系是 is-a 的关系。换句话讲,如果一种新的对象与一种已有对象满足 is-a 的关系,那么新的对象的类应该是从已有对象的类继承来的.
  2. is-a 关系是与行为有关的。不是内在的私有的行为,而是外在的公共的行为,是使用者依赖的行为。
  • 说明:LSP是实现 OCP 原则的重要方式。只有当衍生类能够完全替代它们的基类时,使用基类的函数才能够被安全的重用,然后衍生类也可以被放心的修改了。

  • 契约式设计:使用契约式设计,类中的方法需要声明前置条件和后置条件。前置条件为真,则方法才能被执行。而在方法调用完成之前,方法本身将确保后置条件也成立。

接口分离原则(Interface Segregation Principle,简称ISP)

  • 说明:ISP 原则承认了对象设计中非内聚接口的存在。但它建议客户类不应该只通过一个单独的类来使用这些接口。取而代之的是,客户类应该通过不同的抽象基类来使用那些内聚的接口。在不同的编程语言中,这里所指的抽象基类可以指 “接口(interface)”、”协议(protocol)”、”签名(signature)” 等。

  • 核心思想:类间的依赖关系应该建立在最小的接口上,换句话说客户类不应被强迫依赖那些它们不需要的接口。(Clients should not be forced to depend upon interfaces that they do not use.)

  • 使用场景:用于处理胖接口(fat interface)所带来的问题

  • 通俗来讲:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

  • 策略:让对象的客户类不通过对象的接口来访问,而是通过委托(delegation)或者基类对象来访问。

  1. 通过委托进行分离(或者使用 Adapter 设计模式)
    弊端:都需要创建一个新的对象。创建对象显然多了些开销
  2. 通过多继承进行分离

采用委托(delegation)或多继承方式,胖接口可以被分离成多个抽象的基类接口,从而打破客户类之间的不必要的耦合。

依赖倒置原则(Dependence Inversion Principle,简称DIP)

  • “Bad Design” 的定义:是什么让设计变得僵化、脆弱和难以复用呢?答案是模块间的相互依赖。
  • 僵化性:体现在,如果对相互依赖严重的软件做一处改动,将会导致所有依赖的模块发生级联式的修改。
  • 脆弱性:是指一处变更将破坏程序中多个位置的功能。
  • 核心思想:
  1. 高层模块不应该依赖低层模块,二者都该依赖于抽象;
  2. 抽象不应该依赖于具体实现细节,而具体实现细节应该依赖于抽象。
  3. 要面向接口编程,不要面向实现编程
  • 说明:高层模块就是调用端,低层模块就是具体实现类。抽象就是指接口或抽象类。细节就是实现类。
  • 通俗来讲:依赖倒置原则的本质就是通过抽象(接口或抽象类)使个各类或模块的实现彼此独立,互不影响,实现模块间的松耦合。

eg: socket设计
别管用户怎么用你的socket(高层不应该依赖底层模块);也别管socket将来要如何实现(抽象不应该依赖细节)——先问问自己,我,应该把socket做成什么样子?

  • 核心:我们其实想重用的是高层模块。我们已经通过子程序库等方式很好地重用了低层模块了。如果高层模块依赖于低层模块,将导致高层模块在不同的环境中变得极难被复用。而如果高层模块完全独立于与低层模块,高层模块就可以很容易地被复用。这就是这个原则的核心所在。
  • 分层:所有结构良好的面向对象架构都有着清晰明确的层级定义,每一层都通过一个定义良好和可控的接口来提供一组内聚的服务集合。(All well-structured object-oriented architectures have clearly-defined layers, with each layer providing some coherent set of services though a well-defined and controlled interface.)
  • 说明:依赖倒置原则(Dependency Inversion Principle)是很多面向对象技术的根基。它特别适合应用于构建可复用的软件框架,其对于构建弹性地易于变化的代码也特别重要。并且,因为抽象和细节已经彼此隔离,代码也变得更易维护。

迪米特法则(Law of Demeter,简称LoD)(Least Knowledge Principle)

  • 前言:最少知识原则(Least Knowledge Principle),或者称迪米特法则(Law of Demeter),是一种面向对象程序设计的指导原则,它描述了一种保持代码松耦合的策略。
  • 核心思想:每个单元对其他单元只拥有有限的知识,只了解与当前单元紧密联系的单元;(Each unit should have only limited knowledge about other units: only units “closely” related to the current unit.),
  • 类应该与其协作类进行交互但无需了解它们的内部结构。(A class should interact directly with its collaborators and be shielded from understanding their internal structure.)
  • 优点:更好的信息隐藏和更少的信息重载,将降低模块间的耦合,提升了软件的可维护性和可重用性
  • 缺点:可能会导致不得不在类中设计出很多用于中转的包装方法(Wrapper Method),这会提升类设计的复杂度。
  • 通俗来讲: 一个类对自己依赖的类知道的越少越好。自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。

文章参考

  1. iOS设计模式四部曲
  2. C#设计模式翻译