热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入解析设计模式之开闭原则的应用与实践

本文深入探讨了设计模式中的开闭原则,详细解析了其核心理念及其在多种设计模式中的应用。文章首先介绍了开闭原则的基本概念,随后通过实例分析了该原则在策略模式、简单工厂模式、工厂方法模式、抽象工厂模式、建造者模式、桥梁模式以及外观模式中的具体实现。通过对这些模式的对比和讨论,旨在帮助读者更好地理解和应用开闭原则,提升软件系统的可扩展性和可维护性。

Table of Contents

  • 1 设计模式中的开闭原则
    • 1.1 基本原则
  • 2 模式中的开-闭原则
    • 2.1 策略模式
    • 2.2 简单工厂
    • 2.3 工厂方法
    • 2.4 抽象工厂
    • 2.5 建造者模式
    • 2.6 桥梁模式
    • 2.7 外观模式
    • 2.8 中介模式
    • 2.9 迭代子模式

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 迭代子模式

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

中介模式

中介模式






推荐阅读
  • 我正在使用 Ruby on Rails 构建个人网站。总体而言,RoR 是一个非常出色的工具,它提供了丰富的功能和灵活性,使得创建自定义页面变得既高效又便捷。通过利用其强大的框架和模块化设计,我可以轻松实现复杂的功能,同时保持代码的整洁和可维护性。此外,Rails 的社区支持也非常强大,为开发过程中遇到的问题提供了丰富的资源和解决方案。 ... [详细]
  • Sapphire 测试网上线:首个支持 EVM 的隐私 ParaTime 环境
    Sapphire 测试网上线:首个支持 EVM 的隐私 ParaTime 环境 ... [详细]
  • 理工科男女不容错过的神奇资源网站
    十一长假即将结束,你的假期学习计划进展如何?无论你是在家中、思念家乡,还是身处异国他乡,理工科学生都不容错过一些神奇的资源网站。这些网站提供了丰富的学术资料、实验数据和技术文档,能够帮助你在假期中高效学习和提升专业技能。 ... [详细]
  • 在Unity中进行3D建模的全面指南,详细介绍了市场上三种主要的3D建模工具:Blender 3D、Maya和3ds Max。每种工具的特点、优势及其在Unity开发中的应用将被深入探讨,帮助开发者选择最适合自己的建模软件。 ... [详细]
  • 超分辨率技术的全球研究进展与应用现状综述
    本文综述了图像超分辨率(Super-Resolution, SR)技术在全球范围内的最新研究进展及其应用现状。超分辨率技术旨在从单幅或多幅低分辨率(Low-Resolution, LR)图像中恢复出高质量的高分辨率(High-Resolution, HR)图像。该技术在遥感、医疗成像、视频处理等多个领域展现出广泛的应用前景。文章详细分析了当前主流的超分辨率算法,包括基于传统方法和深度学习的方法,并探讨了其在实际应用中的优缺点及未来发展方向。 ... [详细]
  • 在稀疏直接法视觉里程计中,通过优化特征点并采用基于光度误差最小化的灰度图像线性插值技术,提高了定位精度。该方法通过对空间点的非齐次和齐次表示进行处理,利用RGB-D传感器获取的3D坐标信息,在两帧图像之间实现精确匹配,有效减少了光度误差,提升了系统的鲁棒性和稳定性。 ... [详细]
  • 当前,众多初创企业对全栈工程师的需求日益增长,但市场中却存在大量所谓的“伪全栈工程师”,尤其是那些仅掌握了Node.js技能的前端开发人员。本文旨在深入探讨全栈工程师在现代技术生态中的真实角色与价值,澄清对这一角色的误解,并强调真正的全栈工程师应具备全面的技术栈和综合解决问题的能力。 ... [详细]
  • 深入解析Gradle中的Project核心组件
    在Gradle构建系统中,`Project` 是一个核心组件,扮演着至关重要的角色。通过使用 `./gradlew projects` 命令,可以清晰地列出当前项目结构中包含的所有子项目,这有助于开发者更好地理解和管理复杂的多模块项目。此外,`Project` 对象还提供了丰富的配置选项和生命周期管理功能,使得构建过程更加灵活高效。 ... [详细]
  • 在VC环境中,掌握高效的调试技巧和高级应用对于提高开发效率至关重要。本文详细介绍了如何通过检查程序中的括号匹配来避免常见的语法错误。具体操作包括将光标置于待检测的括号(如大括号 {}、方括号 [] 和圆括号 ())上,系统会自动高亮显示对应的配对括号,从而帮助开发者快速定位和修复问题。此外,文章还探讨了其他实用的调试工具和方法,如断点设置、变量监视和调用堆栈分析,以全面提升代码调试的准确性和效率。 ... [详细]
  • PHP中元素的计量单位是什么? ... [详细]
  • 在运行时动态获取Entity Framework中的ObjectSet可以通过反射机制实现。这种方法允许开发者在应用程序运行期间根据需要加载不同的实体集合,从而提高代码的灵活性和可扩展性。通过使用`DbContext`类的`Set`方法,结合类型信息,可以轻松地实现这一目标。此外,还可以利用`Type`对象和泛型方法来进一步增强动态性,确保在处理多种实体类型时更加高效和安全。 ... [详细]
  • BZOJ4240 Gym 102082G:贪心算法与树状数组的综合应用
    BZOJ4240 Gym 102082G 题目 "有趣的家庭菜园" 结合了贪心算法和树状数组的应用,旨在解决在有限时间和内存限制下高效处理复杂数据结构的问题。通过巧妙地运用贪心策略和树状数组,该题目能够在 10 秒的时间限制和 256MB 的内存限制内,有效处理大量输入数据,实现高性能的解决方案。提交次数为 756 次,成功解决次数为 349 次,体现了该题目的挑战性和实际应用价值。 ... [详细]
  • 在Linux环境下编译安装Heartbeat时,常遇到依赖库缺失的问题。为确保顺利安装,建议预先通过yum安装必要的开发库,如glib2-devel、libtool-ltdl-devel、net-snmp-devel、bzip2-devel和ncurses-devel等。这些库是编译过程中不可或缺的组件,能够有效避免编译错误,确保Heartbeat的稳定运行。 ... [详细]
  • Java 8 引入了 Stream API,这一新特性极大地增强了集合数据的处理能力。通过 Stream API,开发者可以更加高效、简洁地进行集合数据的遍历、过滤和转换操作。本文将详细解析 Stream API 的核心概念和常见用法,帮助读者更好地理解和应用这一强大的工具。 ... [详细]
  • 优化后的标题:校园互联新方案:10397连接教育未来 ... [详细]
author-avatar
扬帆900
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有