- 浏览: 503887 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (329)
- [发布至博客园首页] (12)
- [随笔分类][01] .Net X (59)
- [随笔分类][20] Architecture (16)
- [随笔分类][21] Developer Logs (13)
- [网站分类]Windows 7 (1)
- [随笔分类][13] Oracle & .Net (7)
- [随笔分类][16] Love in China (14)
- [随笔分类][15] Development Tools (20)
- [随笔分类][18] Windows Phone (12)
- [随笔分类][12] Design & Pattern (17)
- [网站分类].NET新手区 (22)
- [网站分类]首页候选区 (2)
- [随笔分类][08] Windows (Server) (13)
- [随笔分类][02] CSLA.Net (3)
- [随笔分类][10] jQuery & javaScript (10)
- [随笔分类][11] SQL Server (4)
- [随笔分类][22] Enterprise Logs (3)
- [随笔分类][03] News (9)
- [随笔分类][19] Quality Assurance (2)
- [随笔分类][05] Silverlight (20)
- [随笔分类][14] Google Earth & .Net (6)
- [网站分类]非技术区 (9)
- [随笔分类][07] WWF (2)
- [随笔分类][04] SharePoint (1)
- [随笔分类][20] Analysis & Design (36)
- [随笔分类][06] WCF (5)
- [随笔分类][12] Architecture (1)
- [随笔分类][09] WPF (0)
- [随笔分类][17] VStudio & Expression (5)
最新评论
-
zhangyy130:
你好,我关于第二段的那个表视图、模型与图这三者的关系我没有看明 ...
UML模型的组成 -
guji528:
谢谢分享!
Enterprise Architect 基础应用 -
studentsky:
好文章,图文并茂!
WCF 第一个用 Visual Studio 2010 创建的WCF服务 -
chen975311486:
用哪个工具画的????
UML中对关系的描述 (二) -
frankies:
继续学习中。。
UML 交互概述图
2 开闭原则(Open-Closed Principle,OCP)
2.1 什么是开闭原则
开闭原则是面向对象设计中“可复用设计”的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。
1988年,Bertrand Meyer在他的著作《Object Oriented Software Construction》中提出了开闭原则,它的原文是这样:“Software entities should be open for extension,but closed for modification”。翻译过来就是:“软件实体应当对扩展开放,对修改关闭”。这句话说得略微有点专业,我们把它讲得更通俗一点,也就是:软件系统中包含的各种组件,例如模块(Modules)、类(Classes)以及功能(Functions)等等,应该在不修改现有代码的基础上,引入新功能。开闭原则中“开”,是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;开闭原则中“闭”,是指对于原有代码的修改是封闭的,即不应该修改原有的代码。
2.2 如何实现开闭原则
实现开闭原则的关键就在于“抽象”。把系统的所有可能的行为抽象成一个抽象底层,这个抽象底层规定出所有的具体实现必须提供的方法的特征。作为系统设计的抽象层,要预见所有可能的扩展,从而使得在任何扩展情况下,系统的抽象底层不需修改;同时,由于可以从抽象底层导出一个或多个新的具体实现,可以改变系统的行为,因此系统设计对扩展是开放的。
我们在软件开发的过程中,一直都是提倡需求导向的。这就要求我们在设计的时候,要非常清楚地了解用户需求,判断需求中包含的可能的变化,从而明确在什么情况下使用开闭原则。
关于系统可变的部分,还有一个更具体的对可变性封装原则(Principle of Encapsulation of Variation, EVP),它从软件工程实现的角度对开闭原则进行了进一步的解释。EVP要求在做系统设计的时候,对系统所有可能发生变化的部分进行评估和分类,每一个可变的因素都单独进行封装。
我们在实际开发过程的设计开始阶段,就要罗列出来系统所有可能的行为,并把这些行为加入到抽象底层,根本就是不可能的,这么去做也是不经济的,费时费力。另外,在设计开始阶段,对所有的可变因素进行预计和封装也不太现实,也是很难做得到。所以,开闭原则描绘的愿景只是一种理想情况或是极端状态,现实世界中是很难被完全实现的。我们只能在某些组件,在某种程度上符合开闭原则的要求。
通过以上的分析,对于开闭原则,我们可以得出这样的结论:虽然我们不可能做到百分之百的封闭,但是在系统设计的时候,我们还是要尽量做到这一点。
对于软件系统的功能扩展,我们可以通过继承、重载或者委托等手段实现。以接口为例,它对修改就是是封闭的,而对具体的实现是开放的,我们可以根据实际的需要提供不同的实现,所以接口是符合开闭原则的。
2.3 开闭原则能够带来什么好处
如果一个软件系统符合开闭原则的,那么从软件工程的角度来看,它至少具有这样的好处:
可复用性好。
我们可以在软件完成以后,仍然可以对软件进行扩展,加入新的功能,非常灵活。因此,这个软件系统就可以通过不断地增加新的组件,来满足不断变化的需求。
可维护性好。
由于对于已有的软件系统的组件,特别是它的抽象底层不去修改,因此,我们不用担心软件系统中原有组件的稳定性,这就使变化中的软件系统有一定的稳定性和延续性。
2.4 开闭原则与其它原则的关系
开闭原则具有理想主义的色彩,它是面向对象设计的终极目标。因此,针对开闭原则的实现方法,一直都有面向对象设计的大师费尽心机,研究开闭原则的实现方式。后面要提到的里氏代换原则(LSP)、依赖倒转原则(DIP)、接口隔离原则(ISP)以及抽象类(Abstract Class)、接口(Interace)等等,都可以看作是开闭原则的实现方法。
实例分析
开闭原则:设计一个模块的时候,应当使这个模块可以在不修改原有代码的前提下被扩展。
这个原则是保证系统具有扩展性的基本原则。我理解有几个要点:1、要能够复用;2、扩展时只增加新方法、新类;3、不得不修改代码时,修改的范围必须是局部的、隐藏的;
通常变更有三种方式,一种是横向变更,例如售票系统,原来只能售火车票,现在要可以售机票;第二种是纵向变更,例如在某个流程里插入新活动或跳过活动;第三种是局部修改,就是原有功能的业务规则发生了变化。对于前两种变更比较容易处理,只要在设计时注意抽象,通过接口、继承、override或event即可扩充。对于第三种变更,估计要修改代码了。虽然可以这样分类,但实际上以上三种变更通常是同时发生的、相互交织的。
以库存管理业务单据为例,有出库单、入库单、移库单等。通常单据结构都很相似,包含头表,行表,但个别字段有差异。新增一个单据,先在头表插入一条记录,然后在行表插入若干记录。更新单据时,先更新头表记录,然后清空词单据在行表里的原有记录,再插入新的行表记录,删除单据的过程也非常相似。此外,在单据增删改时要记录日志,在单据提交时,还要修改库存。所有这些操作十分相似,可以抽象出来。
现在要设计一个单据处理通用业务类,负责单据暂存、修改、提交、删除几个基本业务,以及相关的日志和库存操作。
1、分析
单据操作: Save()/Submit()/Delete()/Log()/ChangeStock()
相关数据: 头表实体、行表实体集;与日志有关的一些属性;与库存有关的一些属性;插入头表时,要有一个接口获取单据编号;因为插入行表时外键值需要用到头表记录的主键值,所以需要有个接口获取头表主键值,并有一个接口给行表实体的外键赋值;
可能的变更: 1、将来可能增加审批功能,单据提交后审判通过才改变库存;2、增加新的单据类型;3、将来增加订单管理,那么库存操作除了出入库、还会增加在途转入库存、库存转出在途等功能。
2、设计
方案1、抽象类+子类,只关注操作,不关注数据;
单据处理类 BillProcess (抽象类):
公有虚方法 Save()/Submit()/Delete()
私有虚方法 Log()/ChangeStock()
扩展:增加审批功能,只需在基类增加新的虚方法Audit,子类实现新的虚方法; 增加单据类型时,只需实现新的单据,增加在途功能,只需修改 ChangeStock;
评价:此方案虽然很容易扩展,除了需要对ChangeStock作修改外,基本符合开闭原则;但过于抽象,子类需要实现全部操作,基类仅仅起到规范方法名称的作用;此方案的复用度太低;
方案2、抽象类实现部分模板方法,所有方法都没有参数,抽象类没有任何字段,全部给子类实现;
单据处理类 BillProcess (抽象类):
公有虚方法 Save(): 调用IsNew()判断是新增还是更新,如果是新增,设置单据编号SetBillNumber,插入头表InsertHeader,设置行表外键SetBillLineHeaderID;若是更新,则更新头表UpdateHeader,删除行表DeleteLines(),设置行表外键SetBillLineHeaderID,插入行表InsertLines(); 调用 Log();
公有虚方法 Submit(): 调用 Save(),调用 ChangeStock(); 调用 Log();
公有虚方法 Delete(): 调用 DeleteHeader(),调用 DeleteLines(); 调用 Log();
保护虚方法 IsNew,判断是新增单据还是更新单据;
保护虚方法 SetBillNumber,设置头表单据编号;
保护虚方法 SetBillLineHeaderID, 设置行表所属头表的外键值;
保护虚方法 InsertHeader/UpdateHeader/DeleteHeader, InsertLines/DeleteLines
保护虚方法 Log,记录操作员、操作时间、操作类型、单据编号;
保护虚方法 ChangeStock,改变库存
扩展:增加审批功能,需在基类增加新的虚方法Audit和NeedAudit虚属性,子类实现新的虚方法和虚属性; 增加单据类型时,只需实现新的单据,增加在途功能,需修改 ChangeStock;
评价:此方案也很容易扩展,并且基类实现了部分操作,但子类要实现的方法过多,数据库访问方法其实只是表名有点差别,子类却要全部重写;另外子类 ChangeStock 仍要修改;此方案有一定的复用度,但也不高,而且每个子类的ChangeStock都要修改,不满足“开闭原则”的封闭性。
方案3、一些通用的操作尽量放到 BillProcess,库存操作独立出来成为一个类;抽象出头表实体、行表实体接口;BillProcess可以操作相关数据接口。
单据头实体接口 IBillHeader:
BillID 获取或设置主键ID, 如果获取的ID为0,表示为新单据;这样就不需要IsNew()方法了
BillType 获取单据类型
BillNumber 获取或设置单据编号, 如果是新单据,BillProcess类可以向单据编号生成器BillNumberGenerator.GetBillNumber(BillType)传入BillType参数,获得单据编号,赋值给此属性;
DBField[] 获取实体的字段值数组,便于插入和更新到头表;
单据行实体接口 IBillLine:
BillID 设置所属单据头ID
ProductID 商品ID
SiteID 库位ID
Count 商品数量
DBField[] 获取实体的字段值数组,便于插入到头表;
单据处理类 BillProcess (抽象类):
保护虚属性: 头表名HeaderTableName、行表名LineTableName、头表主键名HeaderTablePKName、行表外键名LineTableFKName、单据类型BillType、头表实体接口IBillHeader、行表集合接口List<IBillLine> BillLines;单据编号 BillNumber;
公有虚方法 Save(): 根据IBillHeader的BillID判断,如果是新增,调用BillNumberGenerator.GetBillNumber(BillType)获取新单据编号,并把单据号设置到头表实体,通过数据库会话类把头表实体DBField[]插入头表,设置行表外键BillID;若是更新,则数据库会话类用DBField[]更新头表,调用DeleteLines()删除行表记录,设置行表外键IBillLine的BillID,通过数据库会话类把行表实体DBField[]插入行表; 调用 Log();
公有虚方法 Submit(): 调用 Save(),调用 ChangeStock(); 调用 Log();
公有虚方法 Delete(): 调用数据库会话类删除头表记录,调用 DeleteLines(); 调用 Log();
保护虚方法 DeleteLines(): 调用数据库会话类根据LineTableName、LineTableFKName删除行表记录
保护虚方法 Log,记录操作员、操作时间、操作类型、单据编号;
保护虚方法 ChangeStock,改变库存, 先调用GenerateStockChanges()把 List<IBillLine> 转为 List<StockChange>, 调用 StockProcess 实现库存操作,便于库存操作扩展
保护虚方法 GenerateStockChanges(), 把 List<IBillLine> 转为 List<StockChange>;
单据号生成器 BillNumberGenerator:
生成单据号 GetBillNumber(BillType)
库存处理类 StockProcess:
属性 List<StockChange>, 库存操作数组
库存改变类 StockChange: 商品ID 库位ID 库存量改变个数 (可扩充增加“在途量改变个数”)
扩展:增加审批功能和新单据时,方法同方案1;增加在途量,需要扩展StockChange实体类和StockProcess业务类,BillProcess修改 GenerateStockChanges,子类不需要修改.
评价:基类包含了较多实现,代码复用度高; 子类只需要实现 IBillHeader, IBillLine 和 一些属性即可(这些属性还可以配置到XML文件中,由BillProcess根据BillType读取XML配置信息,这样子类就不必关心这些细节,方便开发); 同时又保留了较强的扩展性,ChangeStock 方法被细化了,在扩充时,要修改的部分也只是底层的、局部的。不足之处是,基类过多地关注了细节,限制了扩展能力和变更的自由度;另外因为是要修改基类,影响的子类较多,需要投入较多的回归测试时间。此方案基本满足开闭原则。
总体评价,方案3 提供了一个非常强大的业务基类BillProcess,但过于特殊化。目前的支持的流程是 暂存->提交->审批->改变库存,如果再增加一些中间环节,比如审批后增加发货环节,那么就又要修改BillProcess类了。还有,如果不同的单据有不同的流程,那么 BillProcess 就要增加大量属性信息来描述流程。有一种比较理想的设计思路是,把BillProcess变为“业务引擎”,把各种单据的流程配置到文件中,流程引擎读取这些配置信息执行对应的操作。这样增加一个单据或修改一个单据的流程,只需修改配置文件。实际上,这样有点像“依赖倒置”了,用框架来解决问题。
发表评论
-
UML 包图
2009-11-12 09:30 4670什么是包 包可直接理解为命名空间,文件夹,是用来组织图形的封 ... -
软件架构概念分类
2009-11-15 23:05 909软件架构对于每一个人的理解都是不同的,通过分类可以在包容细节差 ... -
UML中对关系的描述
2009-11-16 15:41 758来源网络,加注释修改了用词 依赖关系(Dependency) ... -
UML 用例图
2009-11-19 16:18 3208下面这个知识图片可参照 用例驱动开发 现代需求实践 ... -
UML 对象图
2009-11-22 09:21 2567内容图谱 •对象与类的关系 对象的概念与特性 ... -
UML 活动图
2009-11-22 12:50 7857内容路线 •活动图概述 活动图概述 •活动图和 ... -
UML 状态图
2009-11-22 16:18 4947•状态和状态机 状态、状态表示法及状态机 ... -
UML 构件图(组件图)
2009-11-24 11:35 7367•什么是构件 构件和构件的要素 •构件是系统中可 ... -
MDA(模型驱动架构)
2009-11-24 12:38 21581. 什么是MDA MDA(Model Driv ... -
DSM领域定义建模和MDA模型驱动架构分析
2009-11-24 12:43 1278Domain-Specific ModelingandMode ... -
企业的虚拟化早已上路--转自InfoQ
2009-08-05 11:57 933作者 Matthew Porter 译者 孙涛 发布于 ... -
简单实现缓存需求
2009-10-30 15:52 772读写缓存像变量一样很容易,但是维护缓存,判定缓存有效性,就要根 ... -
模型驱动的开发,回忆一年多前的一次开发
2009-11-07 12:59 819您有关于问题域、需求 ... -
UML建模实践概述
2009-11-11 11:05 920建模目的和原则 • 帮 ... -
UML模型的组成
2009-11-11 12:42 1457UML模型基本元素 模型组织元素 •图:最直接的一种 ...
相关推荐
开放封闭原则(Open-Closed Principle, OCP)作为面向对象设计的重要原则之一,旨在解决软件系统在生命周期内面对变化时的稳定性问题。本文将深入探讨开放封闭原则的概念、意义及其应用实践。 #### 开放封闭原则...
《UML与设计模式》 在软件开发领域,UML(Unified Modeling Language,统一建模语言)和设计模式是两个至关重要的概念。UML是一种通用的、可视化的建模工具,用于描述、可视化、构建和文档化软件系统。...
5 开闭原则-The Open-Closed Principle (OCP) 二 包的设计原则 6 重用发布等价原则-Release Reuse Equivalency Principle (REP) 7 无环依赖原则-The Acyclic Dependencies Principle (ADP) 8 稳定依赖...
开闭原则(Open-Closed Principle,OCP)的核心思想是:“软件实体(如类、模块、函数等)应当对扩展开放,对修改关闭”。这意味着,当需求改变时,我们应尽量通过扩展已有代码,而不是修改已有的代码来实现新功能。...
开闭原则(Open-Closed Principle,简称OCP)是面向对象设计中的一个核心原则,它在软件工程领域具有举足轻重的地位。该原则由 Bertrand Meyer 在其著作《Object-Oriented Software Construction》中提出,旨在提高...
开闭原则(Open-Closed Principle,简称OCP)是软件设计模式中的一个基本原则,由艾兹格·迪米特里斯·伯纳斯-李提出。这个原则指出,软件实体(类、模块、函数等)应当对扩展开放,对修改关闭。换句话说,当软件...
开闭原则(Open-Closed Principle,OCP)是软件工程中的一个基本原则,由Bertrand Meyer在1988年提出,它是面向对象设计的核心之一。这个原则规定了软件实体(如类、模块、函数等)应该对扩展开放,对修改关闭。这...
开闭原则(Open-Closed Principle,简称OCP)是面向对象设计中的一个核心原则,它由Bertrand Meyer在1988年提出,并被收录在SOLID(单一职责原则、开闭原则、里氏替换原则、接口隔离原则、依赖倒置原则)这五大设计...
1、“开-闭”原则(Open-Closed Principle,OCP) 2、里氏替换原则(Liskov Substitution Principle,LSP) 3、依赖倒置原则(Dependence Inversion Principle,DIP) 4、接口隔离原则(Interface Separate ...
开放-封闭原则(Open-Closed Principle, OCP)是软件工程中一个至关重要的设计原则,它由Bertrand Meyer于1988年提出,并由Robert C. Martin进一步阐述和发展。OCP的核心理念在于确保软件实体(如类、模块、函数等)对...
开放封闭原则(Open-Closed Principle,OCP):软件实体应该对扩展开放,对修改关闭。这意味着当需要添加新功能时,应该通过扩展现有代码来实现,而不是修改已有代码。这样可以保持现有功能的稳定性。 里氏替换原则...
开闭原则(Open-Closed Principle,OCP)是软件设计原则之一,它的核心思想是软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着在不修改原有代码的基础上,可以通过增加新的代码来扩展功能。在这个...
开闭原则即OCP(Open-Closed Principle缩写)原则,该原则强调的是:一个软件实体(指的类、函数、模块等)应该对扩展开放,对修改关闭。即每次发生变化时,要通过添加新的代码来增强现有类型的行为,而不是修改...
开闭原则(Open-Closed Principle,OCP)是面向对象设计原则中的一个核心原则,由Bertrand Meyer提出。它强调软件实体(类、模块、函数等)应当对扩展开放,但对修改封闭。这意味着软件系统的设计应当允许在不修改...
1. 开闭原则(Open-Closed Principle,OCP):一个软件实体(如类、模块、函数等)应该对扩展开放,对修改关闭。这意味着当需求变更时,我们可以通过添加新代码而不是修改现有代码来扩展功能。例如,Windows桌面主题...
以下将详细介绍面向对象设计的六大原则:单一职责原则(Single Responsibility Principle, SRP)、开放封闭原则(Open-Closed Principle, OCP)、里氏替换原则(Liskov Substitution Principle, LSP)、依赖倒置原则...
1. 开闭原则(Open-Closed Principle, OCP) 定义:软件实体应当对扩展开放,对修改关闭。这句话说得有点专业,更通俗一点讲,也就是:软件系统中包含的各种组件,例如模块(Modules)、类(Classes)以及功能...
6. 开闭原则(Open-Closed Principle, OCP) OCP指出,软件实体(类、模块、函数等)应当对扩展开放,对修改关闭。也就是说,当需要新增功能时,应尽量通过扩展已有代码,而不是修改已有的代码来实现。 遵循这些...
3. 开闭原则(Open-Closed Principle, OCP) OCP指出,软件实体(类、模块、函数等)应当对扩展开放,对修改关闭。也就是说,为了实现新的功能,我们可以扩展已有代码,而不是修改已有的实现。这样既保持了原有代码的...