博客园  :: 首页  :: 联系 :: 管理

设计模式中的开闭原则

Posted on 2014-07-04 16:02  雪庭  阅读(7037)  评论(2编辑  收藏  举报

 

设计模式中的开闭原则

1 设计模式中的开闭原则

 

1.1 基本原则

系统的可扩展性由开-闭原则、里氏代换原则、依赖倒转原则、组合/聚合复用原则保证;系 统的灵活性由开-闭原则、迪米特原则、接口隔离原则保证;系统的可插入性由开-闭原 则、里氏代换原则、依赖倒转原则、组合/聚合复用原则保证。

当一个软件复用有道、易于维护,新功能加入到系统,或修改一个已有的功能将是容易 的,因此,代码高手就没有用武之地;而当软件是设计低劣、可维护性差,代码高手就必 须用各种非常规的方式,连继作战,加班加点以达到目的。

2 模式中的开-闭原则

一个软件应对扩展开放、对修改关闭,用head first中的话说就是:代码应该如晚霞中 的莲花一样关闭(免于改变),如晨曦中的莲花一样开放(能够扩展);英文原文:Software entities should open for extension, but closed for modification.

这个原则说的是,在设计一个模块时,应当使这个模块可以在不被修改的前提下被扩展, 换言之,应当可以在不必修改源代码的情况下改变这个模块的行为;因为所有软件系统中 有一个共同的特性,即它们的需求都会随时间的推移而发生变化,在软件系统面临新的需 求时,满足开-闭原则的软件中,系统已有模块(特别是最重要的抽象层)不能再修改,而 通过扩展已有的模块(特别是最重要的抽象层),可以提供新的行为,以满足需求。

开-闭原则如果从另一个角度讲述,就是所谓可变性封装原则(Principle of Encapsulation of Variation,略写作EVP),找到系统的可变因素,将之封装起来。 用[GOF95]的话说:考虑你的设计中有什么可能发生变化,允许这些变化而不让这些变化 导致重新设计。可变性封装原则意味着:

  1. 一种可变性不应当散落在代码的很多角落,而应当被封装到一个对象中,同一种可变性 的不同表现可以体现在子类中,继承应当被看做是封装变化的方法,而不仅仅看做是从 父类派生子类
  2. 一种可变性不应当与另一种可变性混合在一起,所以一个设计模中,类图的继承层次 不会超过两层,不然就意味着将两种可变性混在一起

    做到开闭原则不是件容易的事,但也很多规律可循,这些规律也同样以设计原则的身份 出现,它们都是开-闭原则的手段和工具,是附属于开-闭原则的。

2.1 策略模式

策略模式讲的是,如果有一组算法,那么就将每一个算法封装起来,使得它们可以互 换,显然,策略模式就是从可变性的封装原则出发,达到开-闭原则的一个范例。

这个模式完全支持开-闭原则。

策略模式

策略模式

2.2 简单工厂

简单工厂模式中,开-闭原则要求允许新产品加入系统时,无需对业务类进行修改,但 需要修改工厂类。

这个模式并没有很好的体现开-闭原则。

简单工厂

简单工厂

2.3 工厂方法

工厂方法模式在业务类(Creator)中定义了一个创建对象的接口(FactoryMethod()),使 用继承,由子类(ConcreteCreator)决定实例化的类是哪一个,工厂方法模式把实例化 推迟到子类。体现开-闭原则上,允许向系统加入新的产品类型,而不必修改已有代 码,而只需扩展一个相应的具体业务类(ConcreteCreator);业务类(Creator)通常包含 依赖于抽象产品的代码,而这些抽象产品由子类制造,业务类不需要真的知道哪种具体 产品。

业务类在一个方法中定义产品处理框架,其中的工厂方法一般是抽象方法,框架中调用 创建对象的方法(FactoryMethod())得到产品,由子类决定创建哪个对象。工厂模式有 点类似于模板模式,但含义不同,模板模式在一个方法中定义一个算法的骨架,其中算 法一般是抽象方法,而将一些实现步骤延迟到子类中;工厂方法返回的是生产出来的产 品,一般算法不需要返回对象。

这个模式完全支持开-闭原则。

工厂方法

工厂方法

2.4 抽象工厂

抽象工厂模式定义提供了一个接口(AbstractFactory),用于创建相关或依赖对象对象 的家族,而不需要明确指定具体类,抽象工厂模式封装了产品对象家族的可变性,从而 一方面使系统动态决定产品家族产品实例化,另一方面可以在新产品引入到己有系统中 时不必修改已有系统。

抽象工厂与工厂方法定位上是完全不同的模式,抽象工厂提供一个产品家族的抽象类 型,这个类型的子类定义了产品被具体生成方法,要使用这个工厂,必须先实例化它, 传入一些针对抽象类型所写的代码中,抽象工厂使用对象组合达到解耦;工厂方法通过 子类创建对象,父类只需知道抽象类型就可以了,工厂方法使用的是继承达到解耦。

抽象工厂的实现上,具体的工厂继承抽象工厂,一般用工厂方法(FactoryMethod)实 现,也可以用原型(Prototype)实现。

抽象工厂与策略模式类似,都是使用组合,策略模式中的算法可以互换,抽象工厂模式 中的工厂可以互换,它们的区别在于在于使用目的,策略重点是算法的实现,抽象工厂 是不同产品同一风格的实现。

这个模式完全支持开-闭原则。

抽象工厂

抽象工厂

2.5 建造者模式

建造者模式将一个复杂对象的构建与它的表示分离,使得相同的构建过程可以创建不同 的表示,这个模式封装了建造一个有内部结构的产品对象的过程,这样的系统是向产品 内部表示的改变开放的。

建造者

建造者

建造者模式将构造代码和表示代码分开,它通过封装一个复杂对象的创建和表示方式提 高了对象的模块性,每个ConceteBuilder包含了创建和装配一个特定产品的所有代码, 然后不同的Director可以复用它以在相同部件组合的基础上构建不同的Product。建造 者模式与一下子就生成产品的创建型模式不同,它是在Director的控制下一步一步构造 产品的,仅当该产品完成时,仅当该产品完成时,Director才从Builder中取回它。

这个模式完全支持开-闭原则。

2.6 桥梁模式

桥梁模式是"开-闭原则"极好例子,在桥梁模式用意是将抽象化(Abstraction)与实现化 (Implemention)解耦,使二者可以独立地变化,从UML图中可以看出,这个系统含有两个 等级结构,也就是:

  • 抽象化(Abstraction)角色和修正抽象化(RefinedAbstraction)角色组成的抽象化等级 结构
  • 由实现化(Implementor)角色和具体实现化(ConcreteImplementor)角色所组成的实现 化等级结构

桥梁模式所涉及的角色有:

  • 抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现的引用
  • 修正抽象化(RefinedAbstraction)角色:扩展抽象化角色,改变和修正对抽象化的定 义
  • 实现化(Implementor)角色:实现化给出的定义,必须指出的是,这个接口和抽象化 接完全不一样,实现化角色应当只给出底层操作,而抽象化角色应当给出基于底层操 作更高一层的操作
  • 具体实现化(ConcreteImplementor)角色:给出实现化接口的具体实现

抽象化角色就象是一个水杯的手柄,而实现化角色相当于水杯的杯身,手柄控制杯身, 这就是此模式别名"柄体"的来源,如果用中国语言描述,应当是"纲目模式",而这两个 等级是纲与目的关系,纲举则目张。

桥梁模式

桥梁模式

抽象化等级结构中的方法通过对应的实现化对象的委派实现自己的功能,抽象化角色可 以通过不同的实现化对象委派,达到动态转换自己功能的目的。

桥梁模式和策略模式,这个模式类图相似,但它们是用来解决完全不同问题的,策略是 关于算法的封装,而桥梁模式是关于怎样把抽象角色和实现角色的强耦合解除掉,桥梁 模式目的是要为同一个抽象化角色提供不同的实现。

2.7 外观模式

外观模式是将细粒度的对象包装成粗粒度的对象,应用程序通过这个外观对象来完成细 粒度对象的调用;假如一个系统开始的时候与某一个子系统耦合在一起,后来又不得不 换成另一个子系统,那么外观模式可以发挥适配器的作用,将新子系统仍然与本系统耦 合在一起,这样,外观模式便可以改变子系统内部功能而不会影响到客户端,也就是说 从客户端代码不用修改(对修改关闭),子系统可以更换(对扩展开放)。

外观模式

外观模式

在这个对象图中,出现两个角色:

  • 外观(Facade)角色:客户端可以调用这个角色的方法,此角色知晓相关(多个对象)子 系统的功能和责任,本角色会将所有从客户端发出来的请求委派到相应的子系统去
  • 子系统(Subsystem Classes)角色:这是类的集合,每一个子系统可以被客户端直接 调用,或者被外观角色调用,子系统并不知道外观的存在,对于子系统来说,外观只 是另一个客户端而己

2.8 中介模式

中介模式使用一个中介对象协调各个同事对象的相互作用,这些同事对象不再发生直接 的相互作用;这样,一旦有新的同事类添加到系统中来,这些已有的同事对象不会受到 任何影响,但是中介对象本身却需要修改;换言之中介模式并不是以一种完美方式支持 开-闭原则。

中介模式

中介模式

2.9 迭代子模式

迭代子模代将访问聚合元素的逻辑封装起来,并且使它独立于聚集对象的封装,这就提 供了聚集存储逻辑与迭代逻辑独立演变的空间,使系统可以无需修改消费迭代子的客户 端的情况下对聚集对象的内部结构进行扩展。这个模代完全支持开-闭原则。

中介模式

中介模式

Date: 2014-07-18 16:09:33

Author:

Org version 7.8.06 with Emacs version 23

Validate XHTML 1.0