解读设计模式--开闭原则
认识开闭原则
开闭原则是面向对象设计中最重要的原则之一,它也是设计模式的核心原则,其他一切原则都是实现开闭的手段。
开闭原则是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;但对于原有代码的修改是封闭的,即不应该修改原有的代码。
开闭原则由来
我们在做任何系统的时候,都不要指望系统一开始时需求就确定,就再也不变化,这是不科学的想法,而且需求也一定会变化的,当我们面对需求的变化时候,设计的软件可以相对容易修改,不至于说需求一变推倒重建。
这样,我们怎样的设计才能面对需求的改变可以保持相对的稳定,从而使得系统可以在第一个版本之后不断更新版本呢?
这时候开闭原则就应运而生。1988年,Bertrand Meyer在他的著作《Object Oriented Software Construction》中提出了开闭原则,它的原文是这样:“Software entities should be open for extension,but closed for modification”。翻译过来就是:“软件实体应当对扩展开放,对修改关闭”。
开闭原则的好处
开闭原则是面向对象的核心所在,遵循这个原则当然可以带来面向对象技术的巨大好处,即可维护性好、可扩展性好、可复用性高、灵活性好。
因为我们可以利用抽象类和接口来对软件进行扩展,加入新的功能,非常灵活,这样系统就会不断增加新的组件来适应不断变化需求,也就是以变应万变;另外,我们不用去修改原先系统的一些组件,这样软件的稳定性是很高的,当然我们可以进行扩展,所以软件系统也具有一定的延续性。其实这也是可持续发展态度来解决软件问题。
开闭原则在实践中如何体现?
讲了这么多开闭原则的理论性的东西,可能会有读者问,开闭原则在实践中是如何体现?
在csdn论坛中看到了这样一个贴子就是问的这个问题,感觉里面有几个回复还是很有说服力的。截图如下
关于#2楼的回答,应该是从非常理论的层次来说的,这个回答很笼统,机会所有关于开闭原则的问题,用这句话回答都正确。虽然这个回答对楼主的提问没有什么用处,但是对我这篇博客介绍开闭原则还是很有用处的。
多用抽象类和接口来编程才能很好的体现开闭原则。详细见下面介绍:
1)多使用抽象类
在设计类时,对于拥有共同功能的相似类进行抽象化处理,将公用的功能部分放到抽象类中,所有的操作都调用子类。这样,在需要对系统进行功能扩展时,只需要依据抽象类实现新的子类即可。如图所示,在扩展子类时,不仅可以拥有抽象类的共有属性和共有函数,还可以拥有自定义的属性和函数。
2)多使用接口
与抽象类不同,接口只定义子类应该实现的接口函数,而不实现公有的功能。在现在大多数的软件开发中,都会为实现类定义接口,这样在扩展子类时实现该接口。如果要改换原有的实现,只需要改换一个实现类即可。如图所示,各子类由接口类定义了接口函数,只需要在不同的子类中编写不同的实现即可,当然也可以实现自有的函数。
#13楼的回答是很中肯的,他强调了所有事情都是有范围的,就是要有个度,没有最好的,只有最合适的。就想开闭原则和其他原则、还有所有的设计模式的使用都是有范围的,所有说编程不仅是一门技术也是一门艺术;我们设计人员不是神,不能以不变应万变,但是我们要通过学习设计模式原则和设计模式做到以变应万变。
项目中来分析开闭原则
打开EA,画一个简略三层架构的图,我们从这个rose图中来分析开闭原则。
生成源文件:
1: '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
2: ''
3: '' Factory.vb
4: '' Implementation of the Class Factory
5: '' Generated by Enterprise Architect
6: '' Created on: 14-五月-2011 14:58:35
7: '' Original author: cjq
8: ''
9: '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
10: '' Modification history:
11: ''
12: ''
13: '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
14:
15:
16:
17: Option Explicit On
18: Option Strict On
19:
20: Imports Model.b.Package1
21:
22: Namespace b.Package1
23: Public Class Factory
24:
25:
26: Private ReadOnly strAssemblyName As String = System.Configuration _
.ConfigurationSettings.AppSettings("DAL")
27:
28: Private ReadOnly strDB As String = System.Configuration _
.ConfigurationSettings.AppSettings("sql")
29:
30:
31: '''
32: ''' 实例化用户表类
33: '''
34: ''' 用户表类
35: ''' 2011-3-23 9:57 by cjq
36: Public Function CreateUserInfo() As IUserInfo
37: Dim className As String = strAssemblyName + "." + strDB + "UserInfo"
38: Return CType(Assembly.Load(strAssemblyName).CreateInstance _
(className), IUserInfo)
39: End Function
40:
41: '''
42: ''' 实例化学生信息类
43: '''
44: ''' 学生信息类
45: ''' 2011-3-23 11:18 by cjq
46: Public Function CreateStudentInfo() As IStuInfo
47: Dim className As String = strAssemblyName + "." + strDB + "StuInfo"
48: Return CType(Assembly.Load(strAssemblyName).CreateInstance _
(className), IStuInfo)
49: End Function
50:
51:
52: End Class ' Factory
53:
54: End Namespace ' Package1
将生成的代码完善之后(上面的代码是Factory类的代码),我们就能发现工厂类是完成实例化的,而sqlserverUserInfo和AccessUserInfo是两个不同数据库的DAL层类,这样我们如果变数据库的时候,我们就只需要添加相应的数据库DAL层类即可,这就是扩展,而且根本就不用修改。
通过这个例子的说明相信大家对开闭原则有了一定的认识。当然读者如果认为这篇文章有哪里写的不好,或有什么不对的地方,望读者指正。