`
solonote
  • 浏览: 89967 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

敏捷思考 遵循与破坏"开闭原则"

阅读更多

开闭原则的定义

Software entities should be open for extension, but closed for modification.

软件实体应该对扩展是开放的,但是对于修改应当是关闭的.凡是定义一般都是很晦涩的,那么下面举一个应用场景来说明开闭原则的价值.

 

第一天,客户提出一个需求,我们需要将用户对象存储到文件的UserDAO.

解决方案:

 

public class UserDAO{
	
	public void saveUser(User user)
	{
		//将一个用户对象存储到文件.
	}
} 
 

半个月后,客户有了新的需求,它们需要将一部分用户对象存储到XML文件.

方案1(破坏OCP原则):

public class UserDAO {

	public void saveUser(User user, String saveMethod) {
		if (saveMethod.equals("txt")) {
			// 将一个用户对象存储到文本文件.
		} else if (saveMethod.equals("xml")) {
			// 将一个用户对象存储到xml文件.
		}
	}
}

这个方案带来的变化:

1.我要修改依赖UserDAO的类,让其多传入一个参数saveMethod

2.我要维护if...else判断的正确性,如果传入的字符不是txt和xml,那么我还要进行异常处理.

3.加入存储到xml的代码后我无法预知对原来存储到txt的功能是否造成了影响.

4.如果需求再一次变化,那么我必须继续修改saveUser方法,甚至可能因为需要更多的参数而修改了依赖UserDAO的类.

 

方案2(遵循OCP原则):

public interface IUserDAO {

	public void saveUser(User user);
}

public class UserDAOTXTImp implements IUserDAO
{
	public void saveUser(User user)
	{
		//存储到txt
	}
}

public class UserDAOXMLImp implements IUserDAO
{
	public void saveUser(User user)
	{
		//存储到xml
	}
}

这个方案带来的变化:

1.我要修改依赖UserDAO的类,让其依赖IUserDAO接口.

2.我重新实现一个存储到xml的UserDAO.

3.即使需求发生变化,需要存储到数据库的,我只需要重新实现IUserDAO接口.

4.这样的修改不会破坏原有程序.

 

以上看来是一个标准的OCP原则教程,那么下面我们进行进一步的思考:

 

来看看我们破坏了几次OCP原则.

第一天的开发,没有破坏OCP原则,我们只是单纯的增加了一个新功能.

半个月后的开发,无论我们采用方案一还是方案二我们都破坏了OCP原则.

如果我们使用方案一,在以后的变化中我们就会继续破坏OCP原则,并且带来很多负面的影响.

 

如果我在第一天就采用IUserDAO,整个过程都不会破坏OCP原则,听起来很不错.

那么我来对比一下第一天就采用IUserDAO所带来的不同:

 

IUserDAO方案:

1.我要做更多的工作,如果以后没有两种以上的实现,这个接口将毫无意义.

2.如果接口不稳定,比如要增加一个读取用户的操作,那么我们必须修改IUserDAO与UserDAO两个地方,增加了额外的工作量.

3.增加了程序的复杂度.

这个方案如果以后没有需求变化,将给系统带来无用的设计以及更多的维护成本.

 

UserDAO方案:

1.即使有了需求变化,要抽取出一个借口也是非常方便的,相对于IUserDAO方案并没有多出太多的工作量.

2.使用UserDAO方案破坏了OCP原则,破坏了DIP原则,没有依赖于抽象而依赖于具体实现.但是,我们一定要遵循这些原则吗?

3.程序简单直接,易维护.

 

我无法预期半个月后的那次需求变动,因此我不应该做提前的设计.

 

我对OpenClose原则的解释:

你对一类问题需要 一个以上的解决方案, 那么当加入新的解决方案时,你的修改不应该对已有模块产生影响,而你可以通过对抽象的实现来进行扩展.

OpenClose原则对于此外的场景是无意义的,比如你要加入一个新功能,IUserDAO现在需要读取用户的方法,你肯定要打开IUserDAO进行修改,这个时候破坏OpenClose原则是必然的.

分享到:
评论
40 楼 sunway00 2010-05-06  
unsid 写道
whaosoft 写道
xly_971223 写道
敏捷本质上跟开闭原则就是冲突的


为什么啊


使用敏捷方法“极限编程”确实有一条原则是“简单设计”,这个原则是说在设计的时候应该保持最简单,不要为未来可能发生的变化预留各种变化的空间。这个听起来是对设计模式的一种否定,其实不是,敏捷不与软件设计相冲突,敏捷崇尚的是重构,即需要的时候才用,按照楼主的例子,运用XP,很可能是这样的一种情况:开始的时候确实是方案一设计,当用于提出XML需求的时候,重构原有代码成方案二;如果客户始终不提出,那么最终就以方案一告终。

敏捷是淡化设计,但是并不反对和抵制设计,又怎么会冲突,它只是强调“需要的时候才要”。


    我同意这位大侠的意见。我觉得楼主用方案一本身没有问题。问题是在项目的进展过程中有没有持续的或者当需求变更时进行重构的要求和意识。《重构》说:“事不过三,三则重构”,我个人的理解是:一个问题第一次出现,我们直接去做,出现变化,我们去改,如果再出现变化,那一定要重构了。模式的一项基本原则就是分离变化与不变的。有一些变化是需求能够提出的,有一些变化需求没指出来但是我们可以通过常识或者经验判断的,但是如果在有些时候无法判断是否变化,设计过多是没有意义的,连变不变都不确定,又如何去分离呢。灵活性、复杂性、易用性有时候是矛盾的。
39 楼 抛出异常的爱 2010-05-06  
solonote 写道
liwenjie 写道
[quote="solonote其实你的做法只是把if else提到了外部,变成这样:
UserService
-----------------------
SaveUser(user,isXml)
{
if(isXml)
    DAO.saveUserToXML(user);
else
    DAO.saveUserTODatabase(user);
}



难道一定本要在service 判定存储方式吗???对于系统来讲难道,存储为xml或者使数据库中,不是客户端做的事情吗??这个问题没有那么复杂,2个接口很清晰。


呵呵,其实我并没有说你的做法是错的,我只是说这和if,else写在DAO里是一样的效果,本质的问题在于这违反了OCP,当然,我们并不一定需要遵循OC原则,这要看你具体的要解决的问题,我举得例子并不是说这个场景要使用OCP,我只是想表达敏捷设计的OCP我觉得应该是这样做的,希望你能理解

需求是什么?
不用SQL用XML?
那好办把注入的DAO换成对应的xml类型
只要改一个地方写一个类就结束了
由一个开关来决定用哪个DAO?
那也好办把开关改成XML
都不用编译
由客户决定用哪个方式存贮?
你疯了?数据完整性怎么保证?
38 楼 solonote 2010-05-05  
通过我们的讨论。我更清晰了这样一个东西:
OCP只针对一个特定的对象,这只是一个if/else放在哪里的问题,我们始终逃不开判断哪一个具体实现对应哪一个业务。比如我们用第二种方式,扩展更多的DAO,并不需要改变Service。If/Else只是交给容器去做了,它或许不是那么明显,比如是一个配置文件,或者你说的js/jsp.
OCP的针对的就是一个依赖于抽象的对象,比如Service依赖于抽象的DAO接口,那么Service就可以被重用。而你是否值得这样做是谁也说不清的。遵循敏捷设计,在你中弹时,你可以考虑遵循OCP。
另外我们应当充分考虑遵循OCP所要付出的代价,If/Else还是要写的,只是挪了地方,或者换了方式,所以这种方式会明显增加系统的复杂性,付出更多的人力。遵循敏捷设计,我们一开始不必要做这样的过度设计。
这就又可以讨论到容器的作用,比如Spring,它让If/Else的规则定义变得非常简单,所以在这样的环境下用OCP成本相对是较小的,如果没有这样的容器环境,我们就应该更加注意这一点。


37 楼 liwenjie 2010-05-05  
[quote="solonote,我只是说这和if,else写在DAO里是一样的效果,本质的问题在于这违反了OCP,当然,我们并不一定需要遵循OC原则

呵呵,在调用的客户端也就是js或者jsp中,就可以判定是哪种存储方式了,没有必要写出if else 这样很可能是代码坏味,无论是在service还是dao中,这样完全避免if else。
36 楼 solonote 2010-05-05  
liwenjie 写道
[quote="solonote其实你的做法只是把if else提到了外部,变成这样:
UserService
-----------------------
SaveUser(user,isXml)
{
if(isXml)
    DAO.saveUserToXML(user);
else
    DAO.saveUserTODatabase(user);
}



难道一定本要在service 判定存储方式吗???对于系统来讲难道,存储为xml或者使数据库中,不是客户端做的事情吗??这个问题没有那么复杂,2个接口很清晰。


呵呵,其实我并没有说你的做法是错的,我只是说这和if,else写在DAO里是一样的效果,本质的问题在于这违反了OCP,当然,我们并不一定需要遵循OC原则,这要看你具体的要解决的问题,我举得例子并不是说这个场景要使用OCP,我只是想表达敏捷设计的OCP我觉得应该是这样做的,希望你能理解
35 楼 liwenjie 2010-05-02  
[quote="solonote其实你的做法只是把if else提到了外部,变成这样:
UserService
-----------------------
SaveUser(user,isXml)
{
if(isXml)
    DAO.saveUserToXML(user);
else
    DAO.saveUserTODatabase(user);
}



难道一定本要在service 判定存储方式吗???对于系统来讲难道,存储为xml或者使数据库中,不是客户端做的事情吗??这个问题没有那么复杂,2个接口很清晰。
34 楼 solonote 2010-04-30  
刃之舞 写道

OCP的修改不是针对代码级的,而是系统设计级上的,对原有系统不构成影响的修改就是扩展,原有系统对新增加的修改不会失去原有的功能和稳定性。
不过最好还是不修改原有代码类


系统设计级别?系统不就是程序吗?程序不就是代码吗?
33 楼 solonote 2010-04-30  
liwenjie 写道
这个帖子很好,正是我正在思考的问题。
其实对于开闭原则,是对修改的封闭是对扩展的开放,那么什么是修改呢??对一个类文件增加了一个新的方法这是修改吗?修改是对原有设计的修改,原有代码受到影响,这才是修改,如果只是增加了一个方法原有设计未受一点影响这不叫修改。

对于增加保存用户到xml,我个人会把它看做新增的需求 而不是对原有需求的修改,因为原来的保存到数据库的逻辑一点都不动,并且一点都没有影响到其他代码,那么这只是修改了dao文件,对原有文件增加了新的动作或者逻辑而已,所以我会增加一个方法saveUserToXMl之类。

我不会建立一个接口,i因为使设计更加复杂呢
更不会采取ifelse方式这个明显就是代码的坏味。


其实你的做法只是把if else提到了外部,变成这样:
UserService
-----------------------
SaveUser(user,isXml)
{
if(isXml)
    DAO.saveUserToXML(user);
else
    DAO.saveUserTODatabase(user);
}
32 楼 刃之舞 2010-04-28  
liwenjie 写道
这个帖子很好,正是我正在思考的问题。
其实对于开闭原则,是对修改的封闭是对扩展的开放,那么什么是修改呢??对一个类文件增加了一个新的方法这是修改吗?修改是对原有设计的修改,原有代码受到影响,这才是修改,如果只是增加了一个方法原有设计未受一点影响这不叫修改。

对于增加保存用户到xml,我个人会把它看做新增的需求 而不是对原有需求的修改,因为原来的保存到数据库的逻辑一点都不动,并且一点都没有影响到其他代码,那么这只是修改了dao文件,对原有文件增加了新的动作或者逻辑而已,所以我会增加一个方法saveUserToXMl之类。

我不会建立一个接口,i因为使设计更加复杂呢
更不会采取ifelse方式这个明显就是代码的坏味。


OCP的修改不是针对代码级的,而是系统设计级上的,对原有系统不构成影响的修改就是扩展,原有系统对新增加的修改不会失去原有的功能和稳定性。
不过最好还是不修改原有代码类
31 楼 liwenjie 2010-04-27  
这个帖子很好,正是我正在思考的问题。
其实对于开闭原则,是对修改的封闭是对扩展的开放,那么什么是修改呢??对一个类文件增加了一个新的方法这是修改吗?修改是对原有设计的修改,原有代码受到影响,这才是修改,如果只是增加了一个方法原有设计未受一点影响这不叫修改。

对于增加保存用户到xml,我个人会把它看做新增的需求 而不是对原有需求的修改,因为原来的保存到数据库的逻辑一点都不动,并且一点都没有影响到其他代码,那么这只是修改了dao文件,对原有文件增加了新的动作或者逻辑而已,所以我会增加一个方法saveUserToXMl之类。

我不会建立一个接口,i因为使设计更加复杂呢
更不会采取ifelse方式这个明显就是代码的坏味。
30 楼 unsid 2010-03-02  
whaosoft 写道
xly_971223 写道
敏捷本质上跟开闭原则就是冲突的


为什么啊


使用敏捷方法“极限编程”确实有一条原则是“简单设计”,这个原则是说在设计的时候应该保持最简单,不要为未来可能发生的变化预留各种变化的空间。这个听起来是对设计模式的一种否定,其实不是,敏捷不与软件设计相冲突,敏捷崇尚的是重构,即需要的时候才用,按照楼主的例子,运用XP,很可能是这样的一种情况:开始的时候确实是方案一设计,当用于提出XML需求的时候,重构原有代码成方案二;如果客户始终不提出,那么最终就以方案一告终。

敏捷是淡化设计,但是并不反对和抵制设计,又怎么会冲突,它只是强调“需要的时候才要”。
29 楼 刃之舞 2010-03-01  
依赖接口不依赖实现,接口实现分离,压根就是个习惯了。应该属于OCP的最开始的一个级别的遵守了
28 楼 seeker 2010-02-22  
简单和灵活性本来就是矛盾的,想要灵活势必会需要增加额外的付出,OO的目的就是为了解耦、扩展和维护,工作量一定会增加,如果做简单的应用是不用这么麻烦,但当开发一套复杂的系统,或进行重构,就能体会到接口的好处了,至于说到的增加接口的代码量,现在很多工具都能直接自动生成,能让机器来做的就不用人肉了。
27 楼 wandou 2010-02-21  
提高代码质量才是王道。至于预测未来,纯属扯淡。基于已有问题进行设计,而不是基于预测进行设计。设计的目的,是解决已存在的问题,而不是对未来的变化赌大小。
修改未必不可接受。软件的变化,必然会导致修改代码。配置文件,也是代码。从实际情况来看,修改配置文件,有时候还不如修改代码效率高。
怎么样能用最快的方法解决问题,保证软件质量,就是最好的设计。
好的设计,就是让编程变成打字。在保证正确性的前提下,开发和测试成本最低的设计就是最好的设计。
好的设计就是提高工作效率,其他都是扯淡。
26 楼 qjzhyf 2009-09-18  
这样的讨论很精彩,我在开发当中也经常遇到这样的问题。大部分的解决办法都是在后面加些参数,多些判断语句完事。就像LZ的方案一一样。因为,觉得这种办法最直接有效,但导致的结果是说,代码越来越多,维持起来越来越麻烦。偶尔也尝试用扩展的方式去新增一个类来解决。所以,我现在的认为是说,扩展与修改之间,做出两者间的平衡,如何取舍这点上,前期的分析或者判断,很难。
25 楼 solonote 2009-09-18  
嘿嘿,这不就是我一直想说的,我们应该敏捷起来吗?
24 楼 iaimstar 2009-09-18  
solonote 写道
andyyehoo 写道
思维太oo了,不是好事情,容易把简单的事情复杂化



无法理解你的话,能否给个例子


比如如下代码
如果事情很简单
打开一扇门,我们可以,
isDoorOpen = true

如果oo点
就是
 function openDoor(){
         this.isDoorOpen  =true;
}
    

如果更oo点
Door door = Door.getInstance();
 door.open();


如果整个工程就是为了开门,显然搞的太复杂没有意义

23 楼 whaosoft 2009-09-18  
xly_971223 写道
敏捷本质上跟开闭原则就是冲突的


为什么啊
22 楼 solonote 2009-09-18  
andyyehoo 写道
思维太oo了,不是好事情,容易把简单的事情复杂化



无法理解你的话,能否给个例子
21 楼 andyyehoo 2009-09-18  
思维太oo了,不是好事情,容易把简单的事情复杂化

相关推荐

    敏捷软件开发 原则模式与实践

    2. **设计原则**:书中涵盖了设计模式和SOLID原则,如单一职责原则(SRP)、开闭原则(OCP)、里氏替换原则(LSP)、接口隔离原则(ISP)和依赖倒置原则(DIP)。这些原则有助于创建可维护、可扩展的代码结构。 3. ...

    敏捷软件开发原则、模式与实践.pdf

    《敏捷软件开发原则、模式与实践》一书是由著名软件开发专家、软件工程大师Robert C. Martin所著。这本书自出版以来,就被视为敏捷开发领域内的经典之作,对于软件开发人员、项目经理以及软件项目领导者来说,它提供...

    敏捷软件开发:原则、模式与实践(带书签+源码)

    《敏捷软件开发:原则、模式与实践》是一本深度探讨敏捷开发理念和技术的权威著作,由业界知名专家Robert C. Martin(简称Uncle Bob)撰写。这本书不仅提供了丰富的理论知识,还结合实际案例,深入浅出地介绍了如何...

    敏捷软件开发:原则、模式与实践(带书签,源码)

    《敏捷软件开发:原则、模式与实践》是一本深度探讨敏捷开发理念、方法和技术的权威著作。这本书由著名软件开发专家Robert C. Martin撰写,旨在帮助开发者和团队更有效地进行软件开发,提升软件项目的成功率。书中...

    敏捷软件开发:原则、模式与实践.pdf

    敏捷开发的核心是一系列原则、模式和实践,这些内容在《敏捷软件开发:原则、模式与实践》这本书中得到了详细的阐述。 首先,敏捷开发的基石是“敏捷宣言”,它提出了四个核心价值观: 1. 个体和互动高于流程和...

    敏捷开发-敏捷软件开发:原则、模式与实践

    在本书中,享誉全球的软件开发专家和软件工程大师Robert C.Martin将向您展示如何解决软件开发人员、项目经理及软件项目领导...这本综合性、实用性的敏捷开发和极限编程方面的指南,是由敏捷开发的创始人之一所撰写的。

    敏捷软件开发:原则、模式与实践(Agile.software.development:Principles,Patterns,and.Practices)中英版

    《敏捷软件开发:原则、模式与实践》是Robert C. Martin(也被业界称为Uncle Bob)的经典著作,这本书深入探讨了敏捷开发的核心理念,并通过实际案例介绍了如何在项目中运用这些原则、模式和最佳实践。这本书分为两...

    敏捷软件开发:原则、模式与实践清晰扫描中文版PDF(503页完整版)

    本书《敏捷软件开发:原则、模式与实践》是由全球知名的软件开发专家和软件工程大师Robert C. Martin所著,该书是关于敏捷开发与极限编程的综合性、实用性指南。书中深入探讨了软件开发人员、项目经理以及软件项目...

    敏捷软件开发:原则、模式与实践(全)

    敏捷软件开发:原则、模式与实践(全) 敏捷软件开发:原则、模式与实践(全) 敏捷软件开发:原则、模式与实践(全) 敏捷软件开发:原则、模式与实践(全) 敏捷软件开发:原则、模式与实践(全)

    敏捷软件开发:原则、模式与设计.pdf

    敏捷软件开发:原则、模式与设计.pdf敏捷软件开发:原则、模式与设计.pdf

    敏捷开发原则

    ### 敏捷开发原则详解 #### 一、引言 敏捷开发是一种强调适应性和灵活性的软件开发方法论,旨在提高团队的工作效率并确保项目的成功。本文将深入探讨敏捷开发的核心原则,包括其背景、核心价值观以及十二项指导原则...

    敏捷软件开发原则 模式与实践 c#源码

    《敏捷软件开发原则、模式与实践》是一本深入探讨敏捷开发方法论的著作,它强调在快速变化的需求和不确定性中,如何高效地进行软件开发。C#源码的提供为读者提供了实际操作的可能,帮助理解理论在实际项目中的应用。...

    敏捷软件开发:原则、模式与实践

    《敏捷软件开发:原则、模式与实践》是软件开发领域一本经典的著作,它深入探讨了敏捷开发的方法、理念以及在实际工作中的应用。本书对于新手来说,是一本极佳的入门指南,它不仅介绍了敏捷开发的基本概念,还通过...

    《敏捷软件开发 原则.模式与实践》PDF电子书

    《敏捷软件开发:原则.模式与实践》是一本由Robert C. Martin(也被称为Uncle Bob)撰写的经典著作,该书深入探讨了敏捷开发方法论及其在实际项目中的应用。敏捷开发是一种以人为核心、迭代、增量的软件开发方法,...

    传统企业敏捷转型的策略与思考.pdf

    然而,我仍可以就【标题】和【描述】中提及的主题——“传统企业敏捷转型的策略与思考”——提供一些详细的背景信息和知识点。 首先,传统企业在面临快速变化的市场和技术压力时,往往需要采取更加灵活和响应市场...

    敏捷软件开发+原则、模式与实践.pdf

    例如,他强调面向对象设计的重要性,提倡使用设计原则,如单一职责、开闭原则、依赖倒置原则等,以提高软件设计的质量。 此外,书中还包含了大量可复用的C++和Java源代码示例。这些代码示例不仅展示了如何实现特定...

    敏捷软件开发:原则、模式与实践(高清PDF)

    敏捷软件开发:原则、模式与实践(高清PDF)

    敏捷软件开发:原则、模式与实践(全部)

    《敏捷软件开发:原则、模式与实践》是敏捷开发领域的一部经典著作,全面而深入地探讨了敏捷方法的核心理念、关键原则、实用模式以及实践经验。这本书由Robert C. Martin撰写,他是一位知名的软件工程师和敏捷开发的...

Global site tag (gtag.js) - Google Analytics