软件开发中遇到的一些问题

软件开发中遇到的一些问题

 

对近日来一些问题进行思考,希望能有个解决方案。

 

1、数据库方面

每个项目都离不开数据库,而数据库的建立过程是个问题,如何将我们的开发成果移动到运营环境中是个问题,如何维护以后的更新更加是个问题,所有的东西都看起来是那么简单而缺乏技术含量,但真的尝试把它做好却是非常不容易,人工手动来维护这些文档是可以的,但恐怕这是一个专门的工作,如果干这个的人还同时做别的事情,总会无法避免地出现疏漏,我想一定需要借助工具了,而与这些工具的磨合,也是个不小的成本。

 

1)往往忘记写修改履历,不知道哪个是新的,哪个是旧的

通病了,希望修改履历都别忘了写,包括修改日期、作者和内容,这样就不至于到出了问题找人负责的时候手忙脚乱,至于用CVS来管理怎样,我没经验。

 

2)多个人同时直接修改数据库,管理这个的人很痛苦

没有好的解决方案。用CVS来维护如何?

 

3)Oracle 9i开发出的程序不一定能在Oracle 8i上使用

千万别使用高版本的开发环境和低版本的运营环境,理想情况是一样的环境,不行的话反过来用低版本的开发环境和高版本的运营环境也可以。切记,产品的成功不在于开发它的工具是高级还是低级。

图1

由于版本不一致,原先设计好的注释到新的环境就变成了问号,不堪忍受

 

4)外键约束使用不当导致很多问题

外键约束很大程度上保证了数据的完整性,但世界上所有的事情都是一分为二的,在我们开发过程中,外键约束往往约束的是我们而不是数据,当我们需要添加一行测试数据的时候,当我们需要修改一个字段属性的时候,当我们需要更新一个单元格的时候……到最后,我发现我们的项目去处了很多外键约束,最后的成果和设计相去甚远了。对于小型项目,很多时候没必要使用外键约束,当你确定真的需要使用的时候,仔细思考,它真的合理吗?

 

5)冗余与效率问题

总结过程中我发现了一些数据冗余,这是很正常的,比如一个用户表中包括了用户性别、出生日期和身份证号码,其实这就是冗余,因为我们完全可以通过身份证号码来确定用户性别和出生日期,但这种冗余是必要的。一定的冗余能提高我们的效率,我们往往需要在冗余与效率之间选择一个平衡点。参考数据库三范式。

 

6)命名问题

表面上看又是一个没有技术含量的小问题,其实是最令我头疼的问题之一,一张user表中,出现了多个“state”,state表示一种状态,每个用户有若干种状态,很正常,我稍微列一下我能看到的:super_assistant_state、login_state、multi_user_state、forbidden_state、lock_state、score_lock_state……如果和别的表关联起来,恐怕还有更多的state,这些state如果命名得不好,往往容易引起误会,比如lock_state和forbidden_state,两个都表示对用户的限制,如果注释中没有进一步详细的说明,谁又会知道那么多呢?

 

7)类型混乱的问题

字段的类型有时候看起来确实混乱,比如什么时候用integer,什么时候用number,我想这绝对不是随意的,至于number究竟有多长,最长可以指定多长?谁研究过呢?integer和C++中32位的int类型有什么异同吗?而long类型和C++的long是一样的吗?字符串到底用char、varchar还是varchar2?我想需要进一步研究。

 

8)过于复杂的单句SQL语句

Oracle的功能是很强大的,几乎支持所有的SQL风格,当然包括了复杂的联合查询和子查询,但过于庞大的select语句使后来的维护者感觉非常费劲,一个语句往往需要阅读很长时间,而且根据我的经验,调试时候出问题多的存储过程就是包含了这些复杂SQL语句的存储过程,我们有办法将它简化吗?如果不能简化,务必谨慎,测试测试再测试,确保其正确性,然后添加清晰的注释。

 

2、应用程序方面

应用程序的问题主要还是集中在版本的控制上,包括消息头文件的版本,运营与维护的不同版本程序,测试与发行的不同版本。当然还有别的问题,不一定是技术上的原因了,使得后来做出来的成品和原先设计的相差很大,很多设计文档实际上已经废弃,加大了以后维护的难度。

 

1common_lib的放置

common_lib是我们使用的一套公共类库(与之类似的还有aeslib),主要的功能是网络通信、写loghash表和队列等。几乎所有的程序都用到了这些功能,不考虑common_lib本身版本上的差距,我们有不同的使用方法,以Linux环境开发为例,一种是把common_lib与项目分离,放置在$(HOME)目录下,而在make文件中也指定了“$(HOME)/common_lib”的搜索路径;另一种是把common_lib放置在项目目录中,make文件指定的路径可能是“../common_lib”。前一种方法有可能把项目从一台机器移动到另一台机器(或从一个账号移动到另一个帐号)的时候发现common_lib找不到的情况,因为“$(HOME)/common_lib”不一定有嘛;后一种方法有可能会将common_lib所作的一些额外更改忽略掉,它用的还是自己的库。但考虑到common_lib趋于稳定,因此我们尽量使用后一种方法,程序能正确,稳定地运行就是我们的追求了,最新的代码并不是必须的。另外,使用common_lib的同时,我们也通常会用到aeslib,我觉得是不是将它们合并起来更好呢?

 

2)程序中的错别字

这是个不重要又重要的问题,也许大家都默认之后不会觉得有什么不妥,但站在用户的角度,会不会觉得开发人员水平很差呢?我列举一下常见错误(括号内是正确用法):登陆(登录)、AcountAccount)、超连接(超链接)、SucessSuccess)。

 

3)数据库?配置文件?写死?

我们可能需要修改一些程序运行的参数,比如开分最大金额、心跳时间、最大连接客户端数目……等等。这些参数的改变究竟如何实现?我观察了下程序,普遍有三种情况,一是将参数存放在数据库中(可以在运行中生效),二是写配置文件(重新启动程序生效),三是写死在程序中(需要重新编译程序,再运行方生效)。这个除了看需要外,我还想提些建议:1、如果需要网页方面控制,只能使用数据库了;2、只要以后有可能需要修改就不要写死在程序中,要知道,编译的环境不是哪里都有,就算有也不是什么人都会,况且代码是保密的;3、对于较多的批量配置数据,尽量使用数据库;4、程序初始化的配置数据使用配置文件通常更为恰当,因为初始化好之前往往无法访问数据库嘛。最后,贪方便把东西都写死是不负责任的表现,结果往往带来很多不方便。

 

4)版本依然混乱

我经常说的一句话是:“XXX,请把YYY的最新版本代码,给我一份。”或者说:“XXX,你这个是最新版本吗?最近一次改了什么内容?”其实老说这句话我都觉得丢脸,做了这么久开发,版本控制问题还是搞不好,不排除制度和开发模式上也有些问题,但考虑到自己有时候都不能很好执行,就不用怪别人了。通常表现为:1、修改随意,常常忽略修改标识,无日期,无内容,改错了回头再寻找困难;2、经常忘记CVS检入前先同步一次,导致内容混乱;3、责任不明确,程序到底谁在负责啊?比如有人离开了公司,他的代码究竟谁来管?4、旧版本经常不留备份,修改过程无踪迹可寻(CVS可能经历很多修改才检入一次)。

 

5)目录结构安排

一个工程,安排怎样的目录结构?单个目录?或者许多?我想这应该不是随意的,我认为通常可以这样:将公共模块放置一个目录,将类库(比如数据库操作的类库,图形类库,加密狗类库)放置各自的目录,剩下自己编写的代码放一个目录即可,但如果自己写的代码模块独立性强,也可以考虑把他们分开,以便以后的复用。还有就是bin目录的建立,现在想想还是有必要的,将生成的可执行文件放置bin目录下(VC++自己有debug目录和release目录就另外讲了),配置文件也放置bin目录下,发布时候只需要发布bin目录嘛,我们通常写loglog目录呢?我认为放置在bin目录下,这样发布的时候也没忘记带上log目录,当然啦,要先将里面的log文件清空。

 

6)系统设计的问题

在做概要设计的时候,我们往往有很多不错的想法,比如构建一个比较完美的游戏平台,以后只需要在平台上添加各种不同的游戏即可,这样就产生了对应的不同数据库,平台自身一个数据库,每个游戏都有自己的数据库,理论上没问题,实际操作起来问题就大了。先是web方面根本没考虑过这种情况,只设计了一个数据库连接,之后重新添加了新的数据库连接,但可扩充性恐怕就不好了,远没达到我们期预的效果,再就是控制管理部分程序权力过大,或者说设计不合理,往往逾越了平台和具体游戏之间的鸿沟,进一步加强了偶合,使平台和游戏越发不可分离,扩展性更差,最后做出来的产品已经很难把平台和游戏区别开来了,一个平台就是一个游戏,一个游戏包括一个平台。

 

7)考虑上的疏漏

举个例子吧,我们在实际操作中需要创建新的服务器,但发现不成功,查原因,发现是因为数据库里需要添加新的服务器的条目才可以,添加条目是web的功能,结果发现web没做好,等web补上了,条目添加了,发现还是不行,因为服务器的运行需要数据库的很多信息参数,而这些参数目前都没有在添加条目的时候被添加,由于这次web的工作量较大,一时改不好,只能手动在数据库里添加,一张表添加的条目可能有数十条,相当繁琐,稍微不留神就可能出错。考虑上的疏漏可能会伴随我们一生,但每一点一滴都是宝贵的经验,起码我们不能再犯同样的错误。

 

8log文件处理

程序离不开log,按照我们的做法,每天产生一个log文件,时间一长,log文件就越来越多,占用空间越来越大,我想我们应该改进一下,比如每天自动删除3个月以前的log

  图2

一天加一天,log泛滥成灾:)

9exit的使用

程序碰到异常情况我们通常喜欢用exit()来结束程序的运行,在单线程中这样做是没问题的,但到了多线程则未必,根据我的经验,滥用exit()很容易导致程序结束的时候出现“非法操作”,甚至数据库写入不完整。下面是我认为的以后的做法(不一定很正确):只有主线程能调用exit(),其它线程运行遇到致命错误后返回错误值,一层层往上返回,直至主线程,(或者将致命错误消息发至主线程)由主线程调用exit()。当然主线程也可以完全不用exit(),我更偏向于能不用就不用,因为exit()会不分黑红皂白强制结束程序,它不能让对象正常释构,另外它有类似goto语句,破坏程序的结构化,使程序条理变得不清晰。

 

10)多线程中MsgBox的问题

测试/维护过程中发现过一些很奇怪的现象,程序莫名其妙地到了其它电脑就出现“非法操作”,后究其原因发现是使用MsgBox(这里通指消息框)的问题,该调用会弹出对话框请求用户确定取消等,或者仅仅将一些消息反映给用户。在多线程中使用MsgBox我认为存在隐患,一方面是开发工具的问题,MsgBoxC++ Builder中的实现和WINAPIMessageBox是不一样的;另一方面MsgBox并不能每时每刻都能工作得很好,我就发现过多线程程序中C++ BuilderMsgBox不起作用的情况;再一方面MsgBox会对线程造成堵塞,如果让逻辑处理线程直接调用MsgBox则可能导致一些没有预料到的情况发生。我认为,MsgBox和用户界面相关,尽量只在主线程中调用。

  图3

这种MsgBox恐怕难以令人接受

 

3、文档方面

这里指的文档包括了各种设计文档、配置文件、说明书及程序注释,是程序可维护性的重要依据,但往往容易被忽略,它不能影响程序的性能,但我觉得从现在的角度来说,一个程序的可维护性往往比性能更重要。

 

1)配置文件的问题

到底把配置文件放入CVS呢还是不放呢?都各有道理,放的话检出中有这个文件,用户知道去修改,但如果一个用户修改了配置文件并检入,然后另一个用户更新,那另一个用户的配置文件也跟着被改动了,可能导致错误;如果不放,那用户第一次检出时候没有这个配置文件,无法运行程序,但获得这个文件后不会因为之后的更新而导致文件被修改。我看还是不放的好,不经意地被改动配置文件是件很郁闷的事情,宁愿找不到配置文件自己另外去找一个。但有没有其它更完美的办法?

 

2)安装配置说明的问题

我第一次把我认为不错的安装说明交给测试部让他们去执行的时候,我说:“尽量参照说明,不要问我,看看是否能按部就班完成。”结果是很令人沮丧的,一天跑来问我许多次,因为实在不知道下一步怎么弄,或是出了些意外。当然不排除是因为Linux易用性差的缘故,但无论怎么说,我的说明文档也十分糟糕,我一直在想怎么才能写出合格的说明文档呢?我想应该写好之后,把自己当成一个用户,尝试按说明去操作一遍,这是最起码的了。当然要写得好,真不亚于程序设计的难度,你考虑过意外出错吗?还有各种你看起来平常的术语用户是否就清楚?仔细仔细再仔细,难道说明真的很完美,没有任何错误了吗?我没有进一步的解决,只有苦功,在此提一下这其实并不简单,仅此而已。

 

3)说明书的问题

先参考一下上面所说的安装配置说明的问题,是否存在,还有就是以下的一些问题了:1、格式不统一,章节不对齐;2、图片过大,调整后模糊,影响阅读;3、内容混乱,针对性不强,到底是一个针对网络管理员的说明书,还是针对一个普通用户的说明书?我想内容肯定相差很大。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值