`
solonote
  • 浏览: 89981 次
  • 性别: 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原则是必然的.

分享到:
评论
20 楼 pipilu 2009-09-17  
ldinh 写道
solonote 写道
ldinh 写道
开闭原则太隐晦了。针对接口编程和针对实现编程,前者只是用初期的较小付出为不可知的系统变化买的一份保险。这个例子应该算是典型的策略模式吧?


1.开闭原则隐晦
我写这篇文章就是想说说自己的认识,如果你不明白,能否说一下是哪里有困惑,讨论讨论

2.这个例子应该算是典型的策略模式吧?
策略模式的定义就是依赖于抽象,不依赖于具体实现.所以你只要依赖于抽象的地方都可以理解为策略模式.(个人感觉很无聊的一个模式)


隐晦的是它的名字。开闭原则在我看来就是针对接口编程。而策略模式最直接的解释了它。


开放封闭原则跟针对接口编程并不是直接关联的,javascript开发中可以一个接口都没有,照样遵守开放封闭原则。
针对接口编程了,但这个接口不是内聚的,类的设计不遵守“单一职责原则”,类的内聚性很差,照样违反了开放封闭原则。
19 楼 solonote 2009-09-17  
开闭是一个原则,遵循这个原则不一定要用哪一个模式,策略模式是一种解决办法,但不同场景下也有不同的解决方法.
18 楼 ldinh 2009-09-17  
solonote 写道
ldinh 写道
开闭原则太隐晦了。针对接口编程和针对实现编程,前者只是用初期的较小付出为不可知的系统变化买的一份保险。这个例子应该算是典型的策略模式吧?


1.开闭原则隐晦
我写这篇文章就是想说说自己的认识,如果你不明白,能否说一下是哪里有困惑,讨论讨论

2.这个例子应该算是典型的策略模式吧?
策略模式的定义就是依赖于抽象,不依赖于具体实现.所以你只要依赖于抽象的地方都可以理解为策略模式.(个人感觉很无聊的一个模式)


隐晦的是它的名字。开闭原则在我看来就是针对接口编程。而策略模式最直接的解释了它。
17 楼 solonote 2009-09-17  
ldinh 写道
开闭原则太隐晦了。针对接口编程和针对实现编程,前者只是用初期的较小付出为不可知的系统变化买的一份保险。这个例子应该算是典型的策略模式吧?


1.开闭原则隐晦
我写这篇文章就是想说说自己的认识,如果你不明白,能否说一下是哪里有困惑,讨论讨论

2.这个例子应该算是典型的策略模式吧?
策略模式的定义就是依赖于抽象,不依赖于具体实现.所以你只要依赖于抽象的地方都可以理解为策略模式.(个人感觉很无聊的一个模式)
16 楼 ldinh 2009-09-17  
开闭原则太隐晦了。针对接口编程和针对实现编程,前者只是用初期的较小付出为不可知的系统变化买的一份保险。这个例子应该算是典型的策略模式吧?
15 楼 solonote 2009-09-17  
理解,
接口有时候并不用作它本来的意义,而是作为一种协作之间的规约,类似API文档的东西.
这种做法容易误导新手对于接口在JavaOO中实际意义的理解.不去评论这种做法的对错了,它既然是有效的,那么就有存在的价值.或许我们有更好的方式来做这件事情,只是我们还没有去尝试.

这确实有些跑题了~

异常哥提出的这一点后,搞得我的命题变得复杂了,那么不谈接口的额外作用,从它本来应该做的事情上来说,我们应该消除很多没有必要的接口."依赖抽象,不依赖于具体实现"这句话并非对所有情况都是真理.
14 楼 抛出异常的爱 2009-09-17  
service与dao分开
是框架决定的
是选型问题
为了考虑重用性
(这个问题在现在分工方式是个伪命题)

约束是指代码与文档

PS:保持同步在eclipse里只要抽一下就可以了.
并没多麻烦.
13 楼 pipilu 2009-09-17  
xly_971223 写道
敏捷本质上跟开闭原则就是冲突的

何出此言?
12 楼 solonote 2009-09-17  
感觉都有点跑题了,说到用什么东西来做手册了.
private方法我也是一堆一堆的用,生成doc可以只生成public method.(另外我private不太喜欢写注释,程序结构合理一些的话,其实方法的名字往往就可以当做注释了)
我确实不太赞成使用接口来做用户手册.我们可以有其他东西来做.

另外分工的这一点,大家都用一个DAO,大家都不用自己去写DAO
张三做 xxxService method -> jsp
李四做 xxxService method -> jsp
这样就省去了大家重复写只是名字不同的DAO的劳动,也让DAO的职责更单一一些.

引用
3没有约束,变更后.成本更高

变更有时候是无法约束的.
我想说多一个接口并不能改变变更的可能性,而少一个接口,面对变更时需要进行的修改将会更简单.
比如:
UserService 现在要增加一个计算User总量的函数,如果我有没有IUserService接口,我只需要去增加一个函数,如果有接口,我必须让他们保持同步,这就要花费更多的力气.

11 楼 抛出异常的爱 2009-09-17  
2........
3没有约束,变更后.成本更高
4.你作的应用中有没有大量的private 方法呢?我作的项目有不少.这些方法也是有注释的,客户不关心这些东西,而且还有很多工具类.增加视觉负担
5.问题是客户不关心颁,也不关心实现.把这些无用的信息去掉.接口的内容就少了.

6.这个问题在于为了适应框架与社会分工,
有些应该合并的DAO没合并在一起
应该拆分的service没拆分.
张三要作dao + service  + action + jsp + js
李四要作dao + service  + action + jsp + js

这里dao的名子与意义根本无法确定
导至接口一点意义没有
没有意义的接口不要了吧.
10 楼 solonote 2009-09-17  
2.在没OO之前,就是用你这种方式抽象的.已经有人用过了.
这个没什么好说的,函数本来就可以隔离具体实现,但函数本身不具备继承和多态的特性.

3.维护成本?代码行数少了,注释密度增大后,人眼一次看到全图的可能性提高了...
如果只是为了可读性,那么生成一个doc文档不就可以了吗?即使你用接口来做这件事情,要做的工作也不会轻松多少

4.我可以保证世界上99%以上的应用使用对象不是程序员.所以我相信用户读懂接口可能性更高一些出错可能性小
我想我没有讨论到交付一个产品给用户,而是一个程序在开发过程中的设计,对于你提出的这个场景,读一份doc文档或许也不必读接口源文件要困难.

以上我想说的是,接口应该有它本身的作用,而不是作为文档.

5.怎么对未来进行判断?你是说算卦么?
这就举两个例子吧:
5.1我现在要实现一个对User Entity的分页,那么我考虑到系统中其他的实体或者记录都需要分页(这个需求是明显的),那么我不应该只做User Entity的分页,应该把它抽象出来,其他的记录也可以重用这一个分页组件,而只需要做一些必要的实现.
5.2我现在用Hibernate来做ORM,我考虑了一下我以后会不会换ORM框架呢?这种需求概率不大,因此我不需要现在做抽象,我只做一个具体实现.
以上就是我所说的判断,我觉得不应该算作是算卦,算卦的话是随机概率事件,而以上是确实有客观依据的,这些依据让你决定是否做抽象.
一个新理论:"如果你只能用算卦来决定未来是否会产生变化,那么你不应该现在就做抽象"

6.你可以把你的理论延生出的实践拿出来我们来试一下.
在我的应用中,我连XXXDAO都不要,就只有一个DAO
UserService -> DAO
XXXService -> DAO

这一点是因为我发现XXXDAO和YYYDAO提供的功能基本是一致的,只是名字不一样.DAO就实现一些数据存储的东西,我并不把业务逻辑放到里边.

其实我这篇文章想说的就是,不要做没有必要的抽象,维护接口和具体实现比单单维护具体实现的代价是要高的,另外说一下我理解的OCP概念.
非常希望异常哥能够给我指出我这样的实践方式会有什么样的问题.谢谢
9 楼 抛出异常的爱 2009-09-17  
solonote 写道
异常哥 写道

接口是人类思考行为的一种抽象.而不是对代码行为的抽象.....(如果你不以这个为出发点设计接口那么变更是必然的) 同意


1.很多情况下我们只会用一种ORM实现,在这种情况下去做抽象本身是无意义的.
2.不单单只有接口可以隔离实现,每一个函数也可以作为一种抽象,隔离了具体的实现,人类思考行为的抽象一定要是接口吗?
3.引入接口必定造成更多的维护成本,如果没有两种以上的实现,这是毫无意义的.
4.我们只能相信接口吗?
5.没有需求变化不是银弹而是判断,判断抽象的价值,判断需求变化的可能性,考虑项目本身的目标,是一个框架,还是仅仅是一个应用,引入抽象就意味着更多的维护成本,因此必须警惕


1.本题问题出在XML上
2.在没OO之前,就是用你这种方式抽象的.已经有人用过了.
3.维护成本?代码行数少了,注释密度增大后,人眼一次看到全图的可能性提高了...
4.我可以保证世界上99%以上的应用使用对象不是程序员.所以我相信用户读懂接口可能性更高一些出错可能性小
5.怎么对未来进行判断?你是说算卦么?

6.对于现实与软件有很多理论.
你可以把你的理论延生出的实践拿出来我们来试一下.
好的话二话不说同意你.跟随你.
不好的的话.
你说出天花乱坠我也不会信的.

-\com.mimi.dao\
              +AbcDAO.java
              +impl\
                   +AbcDAOImpl.java

8 楼 jnoee 2009-09-17  
“半个月后,客户有了新的需求,它们需要将一部分用户对象存储到XML文件.”
这个需求我觉得没必要做接口多实现把自己套死,UserDAOTXTImp和UserDAOXMLImp不是替代关系。
如果仅仅只是存储需求可以不修改原UserDAO的方法,增加saveUserToXml方法。如果涉及更多操作(save/load/query)等,可以另建一个XmlUserDAO类。

赞同楼主对提前设计的看法。
7 楼 solonote 2009-09-16  
异常哥 写道

接口是人类思考行为的一种抽象.而不是对代码行为的抽象.....(如果你不以这个为出发点设计接口那么变更是必然的) 同意


1.很多情况下我们只会用一种ORM实现,在这种情况下去做抽象本身是无意义的.
2.不单单只有接口可以隔离实现,每一个函数也可以作为一种抽象,隔离了具体的实现,人类思考行为的抽象一定要是接口吗?
3.引入接口必定造成更多的维护成本,如果没有两种以上的实现,这是毫无意义的.
4.我们只能相信接口吗?
5.没有需求变化不是银弹而是判断,判断抽象的价值,判断需求变化的可能性,考虑项目本身的目标,是一个框架,还是仅仅是一个应用,引入抽象就意味着更多的维护成本,因此必须警惕

石中玉 写道
OCP是理想设计原则,没有绝对的对修改关闭的


OCP不是理想,是很实际的设计原则,我觉得OCP原则是用来检验之前的设计是否合理的一种标准.比如那个例子"我们现在有两种DAO的实现需要,那么增加第三种的时候,应当是遵循OCP原则的,如果不能遵循,说明原来的设计存在问题".
所以遵循OCP原则进行设计时,考虑的事情是,以后发生了这类变化,应当遵循OCP原则.
6 楼 抛出异常的爱 2009-09-15  
引用
IUserDAO方案:
1.我要做更多的工作,如果以后没有两种以上的实现,这个接口将毫无意义.
2.如果接口不稳定,比如要增加一个读取用户的操作,那么我们必须修改IUserDAO与UserDAO两个地方,增加了额外的工作量.
3.增加了程序的复杂度.
这个方案如果以后没有需求变化,将给系统带来无用的设计以及更多的维护成本.

1.如果没有需求变更,这个前题本身就是银弹?
2.如果连接口我们都不能相信的话,我们能相信谁呢?(很想电影台词?)
接口是人类思考行为的一种抽象.而不是对代码行为的抽象.....(如果你不以这个为出发点设计接口那么变更是必然的)
5 楼 石中玉 2009-09-15  
OCP是理想设计原则,没有绝对的对修改关闭的
4 楼 solonote 2009-09-08  
敏捷只是一种价值观与一些开发的建议,和OCP怎么会有冲突呢?
3 楼 xly_971223 2009-09-08  
敏捷本质上跟开闭原则就是冲突的
2 楼 solonote 2009-09-08  
kysdvnm 写道
哦。亲爱的  solonote  对于这情况,你为什么不一开始的时候就使用工厂方法模式呢?

问题总有局限性的,我们需要关注的是一个整体,如果你一开始就想到这个问题,那么就不会出现这种尴尬了



Why Factory Method?我实在看不出这个场景和工厂方法有什么关系,什么东西需要被制造出来呢? 你指的整体是什么?
1 楼 kysdvnm 2009-09-08  
哦。亲爱的  solonote  对于这情况,你为什么不一开始的时候就使用工厂方法模式呢?

问题总有局限性的,我们需要关注的是一个整体,如果你一开始就想到这个问题,那么就不会出现这种尴尬了

相关推荐

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

    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