// com口操作类 package xyz.game; class ComOpera { public void openPort() throws Exception {...} // 打开com public void closeProt() {...} // 关闭com private String readMsg() {...} // 读取com消息 private void writeMsg(String msg) {...} // 写com消息 public boolean getComStatus() {...} private String unicode2gb(String hexString) {...} public boolean writeMsgWithResultOK(String cmd) public String writeMsgWithResult(String cmd) {...} public String getSmsCode() {...} //短信操作接口通过上述com接口实现 public boolean deleteMsg() {...} public void sendsms(String sms, String receivers) {...} }; // 业务逻辑模块 package xyz.game; class GameCatch{ public void catch(ComOpera comOpera){ comOpera.openPort(); //打开com端口 comOpera.getSmsCode() } } // 程序入口 package xyz.game; class GameMain{ public ComOpera comOpera = new ComOpera(); public void run(){ GameCatch gameCatch = new GameCatch() gameCatch.catch(comOpera); //传递给其他对象 // other code } public static void main(String[] args) { GameMain game = new GameMain(); game.run(); game.comOpera.closePort(); //关闭com端口 } }
这是简化后的代码,我初次读到这些代码的时候发现问题有二:一个类里可以有多达27个属性;代码长度可以平均达到每个文件400+行,那么我第一反应是这个程序在一个类里放入多个功能,从而导致类的臃肿。
再读下去,发现印证了我的猜测,仅仅在原有项目里并不算最复杂的ComOpera类里就有多个功能放入一个类的情况,原设计将短信操作和COM操作放到同一个类里去实现了,原因是短信是通过com口实现通信的,但是我很明显的感觉到SMS操作和COM操作明显是不同的功能模块,理由有二:发送短信未必只能采用COM口,而COM口也未必只能发送短信。独立开来这两个模块会有更好的复用性,清晰性。
但是这样做也勉强可以接受:就是当需求未清楚时,可以采用最简单的设计方案,毕竟研发成本也是有限的,例如在这个项目中,假如就只需要COM口收发送短信,如果提前解耦那么成本就会增加,用不到的话就是浪费。而且增加接口也会增加理解的难度
然而需求变更还是出现了:短信的通信方式从COM口变更成为从数据库进行读写,那么朋友打算怎么干:
1) 写一个读写数据库的类
2) 在GameCatch里替换ComOpera;
3) aObjectOfOtherClass.method里传入一个新对象
这完全是打补丁的方案!!!敏捷设计之所以可以不做任何设计即可进入开发,是因为敏捷设计在需求变更之际。会不断的提炼接口,抽象操作,按照敏捷的理论,需求变更不是让程序越改越烂,而是越改越好。也就是说好的程序员和烂的程序员在开始的时候可以写出差不多的代码,但是随着项目的发展,需求的变更,好的程序员可以把代码越改越好而烂的程序员只能让代码越来越烂(忏悔下,俺原来就是传说中的烂程序员啊)当然,话是这么说,需求变更对于程序员的考验还是很严峻的,有追求的人还是应该直面压力和挑战
好。下面是我的解决方案:
// 短信接口 package xyz.game; public class SmsCodeMgr { public String getSmsCode() { return ""; } } // 短信接口的数据库实现 package xyz.game; public class SmsCodeByDb extends SmsCodeMgr { public String getSmsCode() { // todo } } // 短信接口的COM实现 package xyz.game; public class SmsCodeByCom extends SmsCodeMgr { private ComOperNew comOper; public SmsCodeByCom(){ comOper = new ComOperNew() comOper.openPort(); } public String getSmsCode() { // todo } protected void finalize() { // 对象消亡时需要关闭com口 comOper.closeProt(); } } // COM接口 为一切需要COM通信的对象服务 package xyz.game; public class ComOperNew { public void openPort() throws Exception {...} // 打开com public void closeProt() {...} // 关闭com public String readMsg() {...} // 读取com消息 public void writeMsg(String msg) {...} // 写com消息 public boolean getComStatus() {...} public String unicode2gb(String hexString) {...} public boolean writeMsgWithResultOK(String cmd) public String writeMsgWithResult(String cmd) {...} } // 业务逻辑模块 package xyz.game; class GameCatch{ public void catch(SmsCodeMgr smsmgr){ smsmgr.getSmsCode() } } // 程序入口 package xyz.game; class GameMain{ public SmsCodeMgr smsmgr = new SmsCodeMgr(); public void run(){ GameCatch gameCatch = new GameCatch() gameCatch.catch(smsmgr); //传递给其他对象 // other code } public static void main(String[] args) { GameMain game = new GameMain(); game.run(); } }
其实整个方案里最可不控制的是什么呢?就是原来ComOpera类里读写COM的接口被暴露出去,原来的设计是Com只为短信服务,结果main和其他模块还是知道了短信是需要com的,而且需要为这个事实进行服务,现在通过DB来实现,这些打开关闭COM端口的服务就不在需要了,但是这个时候外界已经知道这件事情了,所以需要在整个系统里去掉openPort,closePort这样的语句,其实这就是设计的僵化
软件预构说的好:软件开发有两个极端:一是瀑布模型二是敏捷,瀑布模型是极端的分析设计,敏捷是极端的不设计,其实现实生活里的软件项目处于这两个极端中的某个位置,如何把握度是个艺术