让你的对象活起来

版权声明:你可以拷贝和传播本文档的全部和部分,但是请注明出处。

 

写作起因

在我们的软件开发的过程中,虽然很多程序员学习了Java语言这样的编程语言,但是还没有系统的认认真真的学习过关于面向对象(OO)方面的知识,特别是从C/C++(主要指国内的程序员)转过来作Java的人,很多的C/C++的过多的考虑性能的一些技巧本身不能起到作用,此外还会加大代码维护和调试的成本。

经过这些年的软件开发,自己经历过疯狂学习Java语言和设计模式的过程,也看到过很多年轻的程序员同样在犯当年我犯过的错误,所以我觉得应该把一些东西写出来,和大家共勉。

一般常见的对象或者任务处理(生产者/消费者模式)的设计

常见的生产者、消费者模式的处理一般如下:

以上是一个简化的生产者消费者的模型,作为开发人员,很多人的实现基本上就按照这样的方式进行实现,在简单问题的处理上,好像没有什么问题,结构简单,同时容易测试。

随着需求的变化,我们发现复杂度从两个方向开始变化:

l           对象本身变得复杂(即出现通用对象)

l           对象没有变复杂,但是对象的类型变得更加复杂

作为生产者自身来说可能由于对象产生的源点而不会变得特别复杂,但是很容易将消费者变得非常复杂,会出现很多的if …else语句和switch语句,关键switch语句还只支持int型的分支,这样,一个漂亮而丰富的通用消费者产生了。如下图:

当然,还有由于生产者本身慢慢变丰富的情况,当时作为敏感的开发人员,一般都会发现这方面的问题,使得生产者分离开来(一般处于业务或者功能的需要也会分开)。

这些变化最终导致了一个结果:消费者急剧变大,比如处理不同的值对象信息,这样就构成了消费者处理类的急剧扩张,我本人就见过超过5000行代码的处理类。其中穿插了很多的各种各样的调用,回调,递归等等。

 

那么,就有人会问了,问题出在哪里呢?无论我们的生产者,消费者,值对象,列表都是按照面向对象的方式组织的阿。

我想有一个问题很多人没有搞清楚,就是面向对象和基于对象的区别。我们上面的解决方式是基于对象的,只有对象的组装,没有抽象,接口分离的体现等等,除了知道设计模式之外,需要理解设计模式,这样才能比较平滑的向面向对象过渡。

下面我们讲讲基于对象和面向对象的区别。

基于对象和面向对象的区别

什么是基于对象?

基于对象,顾名思义,就是使用了对象,没有再直接使用基础类型,比如int,long,double等等基本类型以及建立全局方法等等。就象下面这样:

这样的组织方式有什么好处呢?

第一,把不同的数据封装到不同职责的类当中,使各自的数据得到了应有的保护,需要暴露的数据可以通过对应的get,set方法来实现对其的访问和更改。

第二,把不同的功能封装到不同职责的类当中,适当的对某些死用功能进行了对外的屏蔽,使用了比如privatedefault之类的访问限定符,一定程度上避免了过去面向过程的编程方式中难以避免的方法完全暴露的问题。

 

这样的组织方式有什么不足呢?

第一,对象之间是一个松散的组合,从模块的层次来说,模块之间的松耦合是很必要的,但是模块内部应该是高效的组织方式,尽量减少诸如代码拷贝,对象直接引用,通用方法或者通用类的产生。

第二,系统的维护的成本增高,有一个需求的变动就设计到很多的功能代码的变动,很有牵一发动全身的后果,代码越来越重,最后的结果呢,就是重新开发一个吧。

 

既然基于对象有很多的不足的地方,那么究竟什么是面向对象呢?

什么是面向对象?

面向对象本质上是为了在我们的系统中建立对真实世界的某个部分的抽象映射(从目前来看,《黑客帝国》中的Matrix还只是科幻而已)。既然是抽象,那么就存在两方面:一是信息的抽象取舍的问题,二是信息模型的建立的问题。如果这两方面处理好了,我们的系统就可以满足在给定的需求。

信息的抽象

信息的抽象指的是从现实世界中的对象中取出我们需要的信息的过程,比如:

员工,信息会包括:工号,姓名,住址,性别,出生年月,职位。。。。。。

要描述一个员工,需要很多的信息,然而我们的需求只是需要关注的一部分信息,是其中的一个非常小的子集。比如:工号,姓名,住址,性别,出生年月,职位

(关于信息的抽象请参考Jacobson的《UML指南》)

信息模型的建立

信息模型的建立相对来说是基于信息的抽象的基础上的,知道我们需要什么样的信息,然后才能进行信息模型的抽象。关于信息模型的抽象,请参考《UML精粹》。基本原则是对象间的导航关系(单项,双向),关联关系,依赖关系等等。

 

以上面的为例,我们传输的值对象本身只是负责把信息从生产者带到消费者,初次这外没有任何的额外的功能,我们想一下,从某种程度上说,就是一个“哑对象”,或者DTOData Translate Object.

 

如果我们换个角度看待这个需求,生产者负责产生需要消费的值对象,值对象带着信息传到了消费者,消费者消费不同的对象,等等,好像有个地方不太对劲,消费者这样下去很容易变成通用消费者,如何避免呢?

 

从面向对象的观点来说,对象由自己的属性和行为,作为值对象,目前只有负责封装数据的功能,没有额外的功能,这在分布式的情况下可以理解,如果不是分布式的情况呢?如果让对象自己能处理自己的数据会怎么样呢?

 

让我慢看看改进后的设计。

改进后的设计

本地版

生产者和消费者都在本地的好处是不必要考虑远程访问带来的运行环境和调用方式的约束,从而容易使你的程序在结构上更加面向对象话。

从上面看出,如果我们让对象自己能处理自己的数据,如下:

l           队列设计成只接受抽象值对象接口的队列

l           生产者负责生产数据,通过构造方法将相应的数据传进去

l           值对象通过实现抽象值对象接口的处理法方法(比如processData()),将数据的处理封装的这个方法里面

l           消费者负责从对象队列中取出各个对象,然后调用processData()处理对象

序列图如下:

通过这样的处理方式,将数据处理的职责分配到了各个值对象,好处如下:

l           值对象可以有自己的层次结构,以应对不同的数据处理

l           消费者由于功能变成了调用指定接口的方法这样的invoke类,可以通过多线程等方式进行更加方便的维护

 

通过这样的处理,在相对复杂的条件下,我们的设计的可维护性和性能都有了一定的提高。然而这样的方式好像不太适合远程版。

下面我们看看远程版。

远程版

针对远程版的约束,我们不太容易使用上面的方式,值对象的产生环境和消费环境并不相同,所以建议方式如下:

l           队列设计成只接受抽象值对象接口的队列

l           生产者负责生产数据,通过构造方法将相应的数据传进去

l           值对象不在有处理数据的功能,但是还是保持自己的数据的层次结构

l           消费者从队列中取出值对象,通过消费端的适配器组装成符合处理接口的实例,然后处理数据

序列图如下:

由于远程处理的原因,复杂的程度比原来高了点,但是在值对象的层次性和消费者的多线程提高效率方面还是可以方面的实现的。

 

从这两个例子可以看出,学习和使用设计模式之间有一些距离,此外,刚刚学习设计模式的程序员往往有过渡使用设计模式的冲动。下面我们看看一般情况下如何学习和使用设计模式。

如何学习Java进行程序设计

我们从学习第一门编程语言的时候,在国内一般都是C之类的过程语言,往往过程语言没有掌握熟练,就开始了面向对象语言的学习,在这里以Java为例(因为我只会Java,:-p),我们一开始如何学习易门语言呢?

先是从基本的语法入手,然后是基本的示例程序的了解,这些差不多了,就开始做程序了。这有什么不对么? 当然没有什么不对,不过漏了一个作为面向对象设计非常重要的一环――面向对象课程的培训。

这里说的不是指封装,多态,继承等等,指的是知道了这些之后,如何去使用的问题。了解围棋的人都知道,学完了基本的围棋的时候后,后面学的是开局和定式,否则,茫茫的19*19的棋盘,是无论如何也不可能通过自己的摸索全部搞定的。

同样,设计程序,我们需要通过设计模式的学习来丰富我们面向对象方面的能力,实践证明,通过有效的学习设计模式是学习面向对象思想的捷径。

 

一般来说,学习设计模式有三个阶段:

l           直接模仿设计模式的示例代码

l           学会针对实际环境设计模式进行适当的变通

l           开始的时候不考虑设计模式,在开发和重构的过程中逐渐找到适合情境的设计模式

 

在我们进行软件设计的时候,一方面我们需要深入的学习和理解设计模式,另一方面,通过TDD(测试驱动开发)和重构可以避免我们滥用设计模式。关于这两方面的资料,如果是初学者,建议看看《设计模式精解》,《敏捷软件开发》,《测试驱动开发》,《重构》这几本书,我就不在这里班门弄斧了。

 

参考资料

1.         《设计模式精解》

2.         《敏捷软件开发》

3.         《测试驱动开发》

4.         《重构》

5.         Quartz框架

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值