程序员与coder的思考
面向对象设计中的继承、组合、聚合
在c++和java世界里,面向对象设计是老话题,基本上看过一两本书,做过一点开发的人,
你问他关于面向对象设计中的继承、组合、聚合,他肯定能回答出来,也会写基类、派生类、
java中的接口等等.
但实际情况是不是这样呢?在真实的系统开发中,他们设计出来的类层次,真的理解透了
面向对象设计吗,特别是继承、组合、聚合之间的区别吗?前段时间分析了下以前老员工写的
一个模块,这些老员工可是每月8000~9000元的基本工资,年底奖金都是几万,在广州一般国内
企业算很高了,道理上说这些高级c++程序言不论是编码水平还是设计水平,应该是很合格了吧,
应该把类设计的很好,有可扩展性和维护性。实际上呢,让人大跌眼睛,看来实际项目系统中
的设计没那么简单。
为什么看书时很多人都很明白,一到实际开发就乱套了。你看看书上的例子,每次不是拿
图形来举例,就是拿动物来举例。比如用动物来说明继承关系:
Animal(动物)
|
------------------
| |
Cat(猫) Dog(狗)
大家基本上都能明白,动物是抽象的概念,猫和狗是动物的一种,他们都有年龄、体重这些
共同的属性,都有吃东西这个行为, 我们自然知道把这些共同的东西体现在Animal类里.
拿这些很明白的例子来分析,大家都很容易搞清楚三种情况:是一种(继承),有一个(组合),
使用了(聚合). 真正的系统开发中,很难区分的清楚,比如我们做移动电信系统,其中就有一个
数据同步模块,管理我们的内部系统和移动boss系统之间的数据同步. 数据同步分为开户同步、
销户同步等等.
DataSync(基类)
|
--------------------
| |
OrderSync CancelSync
这些数据同步最后采用HTTP+SOAP协议发送给对方,因为SOAP打包是和具体参数以及XML schema
定义相关的,是各个子类特有的; 然而对HTTP的打包和发送,这块代码是大家共有的,因此实现的
人就把这块代码移动到基类DataSync来实现,然后采用模板方法设计模式来获取不同的HTTP body
内容。
看来一切都正常,是个好继承体系. 随着项目发展,现在要添加对某平台的数据同步,但是
它不采用HTTP协议来通讯,采用直接在TCP层上跑自己的自定义格式。因为以前把发送那部分代码
放到了基类里,现在这部分代码变化了,为了继续使用模板方法设计模式这个已经成型的框架,
结果只有修改基类的发送部分代码了。
这个解决方案造成我们引进一个新需求时,需要修改我们的基类!细细分析了<<java与模式>>
的第二部分: 面向对象的设计原则,就知道这个设计违反了“开-闭”原则。这个原则说的是,一个
软件实体应当对扩展开放,对修改关闭; 在设计一个模块的时候,应当使这个模块可以在不被修改
的前提下被扩展。
为什么会违反这一基本原则呢? 原因就是理解错了继承、组合、聚合在这一实际问题上的关系,
我们数据同步模块对上层客户模块来说,上层并不关心你采用什么协议发送数据,只管把该给你的
数据给你提供完备; 从数据同步模块本身来说,它的部分子类是采用了HTTP协议来发送数据,将来
的一些新加进来的子类,可能采用其它协议来发送数据,所以这里是一种聚合的关系。
如果早期我们就正确发现这一关系,把HTTP打包发送做成一个工具类,子类仅仅通过聚合的方式
来引用它,基类并不写死这块代码,那么我们现在新加入对某平台的数据同步,并不会造成修改基
类的现象,只是把新功能添加进去而已,这不就满足了对扩展开放、对修改关闭的“开-闭”原则吗。
从这一实际现象看出,很多人虽然拿着高薪,做着公司的高级程序员,其实并不都是很好理解了
面向对象设计的各个方面要点。为什么他们还是很厉害,拿高薪呢?一个是他们是老员工,对业务
十分熟悉,掌握着公司的业务和技术秘密, 不管设计的如何垃圾,他们总能把功能实现出来; 第二
就是老板根本就不看代码,也看不懂代码,只管最后功能实现就是了,他根本无法理解这种设计错误
造成的软件可扩展性、可维护性、可复用性的问题,增加了工作量,以为全部是业务逻辑复杂性造成
的。
关于面向对象的设计原则:“开-闭”原则、里氏代换原则、依赖倒转原则、接口隔离原则、组合
/聚合复用原则、迪米特法则, 请细细阅读《java与模式》第二部分,不管是c++程序员还是java
程序员. 理解了这一部分,才能把握全书,才能理解设计模式解决了什么,这是《设计模式》那本书
没有提到的关键点!
现在很多C++程序员,依然写着带类的C程序,C++因为兼容C,造成了拥有4种设计并存:结构化
设计、基于对象设计、面向对象设计、泛型设计。在这么复杂的环境里,偏偏这些程序员不学习,
很少看c++之父的《c++程序设计语言》、著名的《C++ primer》、经典的《Effective C++》和姊妹
篇《More Effective C++》、四巨头《设计模式》等等关于设计和编程的经典著作。
而java程序员呢,常常发现没看过《java编程思想》、《java与模式》、《重构》、《深入java
虚拟机》等等,就知道摆弄structs/hibernate/spring/jboss/weblogic这些框架,还不一定很好
理解其架构体系了,搞不清楚类动态装载机制和反射机制以及动态代理等等。
程序员分两种:一种是天才式的人物,不用学习就能在设计和实现上做的完美无缺; 另一种就是
智力平常的普通人,是需要通过读书学习、项目经验、同事朋友间交流沟通来提高技术的。我自己是
后一种人,所以不断的学习C++/java方面的东西,和其它同事沟通。你是那一种人呢?难道你是天才,
是天才请告诉我,我向你请教以便快速提高。
在c++和java世界里,面向对象设计是老话题,基本上看过一两本书,做过一点开发的人,
你问他关于面向对象设计中的继承、组合、聚合,他肯定能回答出来,也会写基类、派生类、
java中的接口等等.
但实际情况是不是这样呢?在真实的系统开发中,他们设计出来的类层次,真的理解透了
面向对象设计吗,特别是继承、组合、聚合之间的区别吗?前段时间分析了下以前老员工写的
一个模块,这些老员工可是每月8000~9000元的基本工资,年底奖金都是几万,在广州一般国内
企业算很高了,道理上说这些高级c++程序言不论是编码水平还是设计水平,应该是很合格了吧,
应该把类设计的很好,有可扩展性和维护性。实际上呢,让人大跌眼睛,看来实际项目系统中
的设计没那么简单。
为什么看书时很多人都很明白,一到实际开发就乱套了。你看看书上的例子,每次不是拿
图形来举例,就是拿动物来举例。比如用动物来说明继承关系:
Animal(动物)
|
------------------
| |
Cat(猫) Dog(狗)
大家基本上都能明白,动物是抽象的概念,猫和狗是动物的一种,他们都有年龄、体重这些
共同的属性,都有吃东西这个行为, 我们自然知道把这些共同的东西体现在Animal类里.
拿这些很明白的例子来分析,大家都很容易搞清楚三种情况:是一种(继承),有一个(组合),
使用了(聚合). 真正的系统开发中,很难区分的清楚,比如我们做移动电信系统,其中就有一个
数据同步模块,管理我们的内部系统和移动boss系统之间的数据同步. 数据同步分为开户同步、
销户同步等等.
DataSync(基类)
|
--------------------
| |
OrderSync CancelSync
这些数据同步最后采用HTTP+SOAP协议发送给对方,因为SOAP打包是和具体参数以及XML schema
定义相关的,是各个子类特有的; 然而对HTTP的打包和发送,这块代码是大家共有的,因此实现的
人就把这块代码移动到基类DataSync来实现,然后采用模板方法设计模式来获取不同的HTTP body
内容。
看来一切都正常,是个好继承体系. 随着项目发展,现在要添加对某平台的数据同步,但是
它不采用HTTP协议来通讯,采用直接在TCP层上跑自己的自定义格式。因为以前把发送那部分代码
放到了基类里,现在这部分代码变化了,为了继续使用模板方法设计模式这个已经成型的框架,
结果只有修改基类的发送部分代码了。
这个解决方案造成我们引进一个新需求时,需要修改我们的基类!细细分析了<<java与模式>>
的第二部分: 面向对象的设计原则,就知道这个设计违反了“开-闭”原则。这个原则说的是,一个
软件实体应当对扩展开放,对修改关闭; 在设计一个模块的时候,应当使这个模块可以在不被修改
的前提下被扩展。
为什么会违反这一基本原则呢? 原因就是理解错了继承、组合、聚合在这一实际问题上的关系,
我们数据同步模块对上层客户模块来说,上层并不关心你采用什么协议发送数据,只管把该给你的
数据给你提供完备; 从数据同步模块本身来说,它的部分子类是采用了HTTP协议来发送数据,将来
的一些新加进来的子类,可能采用其它协议来发送数据,所以这里是一种聚合的关系。
如果早期我们就正确发现这一关系,把HTTP打包发送做成一个工具类,子类仅仅通过聚合的方式
来引用它,基类并不写死这块代码,那么我们现在新加入对某平台的数据同步,并不会造成修改基
类的现象,只是把新功能添加进去而已,这不就满足了对扩展开放、对修改关闭的“开-闭”原则吗。
从这一实际现象看出,很多人虽然拿着高薪,做着公司的高级程序员,其实并不都是很好理解了
面向对象设计的各个方面要点。为什么他们还是很厉害,拿高薪呢?一个是他们是老员工,对业务
十分熟悉,掌握着公司的业务和技术秘密, 不管设计的如何垃圾,他们总能把功能实现出来; 第二
就是老板根本就不看代码,也看不懂代码,只管最后功能实现就是了,他根本无法理解这种设计错误
造成的软件可扩展性、可维护性、可复用性的问题,增加了工作量,以为全部是业务逻辑复杂性造成
的。
关于面向对象的设计原则:“开-闭”原则、里氏代换原则、依赖倒转原则、接口隔离原则、组合
/聚合复用原则、迪米特法则, 请细细阅读《java与模式》第二部分,不管是c++程序员还是java
程序员. 理解了这一部分,才能把握全书,才能理解设计模式解决了什么,这是《设计模式》那本书
没有提到的关键点!
现在很多C++程序员,依然写着带类的C程序,C++因为兼容C,造成了拥有4种设计并存:结构化
设计、基于对象设计、面向对象设计、泛型设计。在这么复杂的环境里,偏偏这些程序员不学习,
很少看c++之父的《c++程序设计语言》、著名的《C++ primer》、经典的《Effective C++》和姊妹
篇《More Effective C++》、四巨头《设计模式》等等关于设计和编程的经典著作。
而java程序员呢,常常发现没看过《java编程思想》、《java与模式》、《重构》、《深入java
虚拟机》等等,就知道摆弄structs/hibernate/spring/jboss/weblogic这些框架,还不一定很好
理解其架构体系了,搞不清楚类动态装载机制和反射机制以及动态代理等等。
程序员分两种:一种是天才式的人物,不用学习就能在设计和实现上做的完美无缺; 另一种就是
智力平常的普通人,是需要通过读书学习、项目经验、同事朋友间交流沟通来提高技术的。我自己是
后一种人,所以不断的学习C++/java方面的东西,和其它同事沟通。你是那一种人呢?难道你是天才,
是天才请告诉我,我向你请教以便快速提高。