春风博客

春天里,百花香...

导航

<2008年8月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
31123456

统计

公告

MAIL: junglesong@gmail.com
MSN: junglesong_5@hotmail.com

Locations of visitors to this page

常用链接

留言簿(11)

随笔分类(224)

随笔档案(126)

个人软件下载

我的其它博客

我的邻居们

最新随笔

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜

从模板方法模式到反射再到Spring的IoC

软件开发过程中,唯一不变的就是变化。这是一句老生常谈,也就是说软件开发中永恒的主题就是变化。当你把代码都写好了,测试也完成了,准备交付的时候客户忽然要求你在指定时间做出变化,这种情况在外包行业中很常见;而对一些银行金融项目,边调研边开发边测试屡见不鲜;对于一些Web项目,从来就只有永远的Beta版,改来改去的事更是家常便饭。对此,程序员一定要求清晰的认识,抱怨只能是嘴上痛快,不解决实际问题。真要解决实际问题,非要动一番脑筋不可,如果合理使用了设计模式,反射或是Spring的IoC,便能变修改噩梦为一次有趣的智慧之旅。

 

首先我们看原始要求:客户要求将一批雇员名单存入到CSV和XML两种文件中去,以后还有可能增加别的文件格式,比如PDF,XLS等,虽然这是下一期的内容,但这一期应该考虑到变化,客户要求扩展性一定要好。

没问题,有了设计模式响应变化不难。这时我们可以用到模板方法模式:

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中

 

先请看骨架抽象类:

  1. public abstract class FileMaker {
  2.   /**
  3.    * 雇员集合
  4.    */
  5.   private List<Employee> employees;
  6.   /**
  7.    * 生成雇员列表文件,公开接口,供外界调用
  8.    * 定义成final是不想让子类改写,因为调用的步骤在父类中就可以确定,子类只需实现makeFile函数
  9.    * @param employees
  10.    * @param fileName
  11.    */
  12.   public final void makeFile(List<Employee> employees,String fileName){
  13.     setEmployees(employees);
  14.     makeFile(fileName);
  15.   }
  16.   
  17.   /**
  18.    * 生成具体的雇员列表文件,此函数留待子类实现
  19.    * @param fileName
  20.    */
  21.   protected abstract void makeFile(String fileName);  
  22.   
  23.   public final void setEmployees(List<Employee> employees) {
  24.     this.employees = employees;
  25.   }
  26.   public List<Employee> getEmployees() {
  27.     return employees;
  28.   }   
  29. }

很好,固定的函数和步骤都在抽象基类中写定了,再看两个具体实现类,它们要实现的就是makeFile函数而已。

  1. public class CSVFileMaker extends FileMaker{
  2.   protected void makeFile(String fileName){
  3.     try {
  4.           BufferedWriter out = new BufferedWriter(new FileWriter(fileName));
  5.           
  6.           for(Employee emp:getEmployees()){
  7.             String line="";
  8.             line+=emp.getName()+",";
  9.             line+=(emp.isMale()?"男":"女")+",";
  10.             line+=emp.getAge()+",";
  11.             
  12.             out.write(line+"\r\n");
  13.           }
  14.           
  15.           out.close();
  16.       } catch (Exception e) {
  17.         e.printStackTrace();
  18.       }
  19.   }
  20. }
  1. public class XMLFileMaker extends FileMaker{
  2.   protected void makeFile(String fileName){
  3.     try {
  4.       Document document = DocumentHelper.createDocument();
  5.             Element root = document.addElement("employees");
  6.             
  7.             for(Employee emp:getEmployees()){
  8.             Element empElm=root.addElement("employee");
  9.             
  10.             Element nameElm=empElm.addElement("name");
  11.             nameElm.setText(emp.getName());
  12.             
  13.             Element sexElm=empElm.addElement("sex");
  14.             sexElm.setText(emp.isMale()?"男":"女");
  15.             
  16.             Element ageElm=empElm.addElement("age");
  17.             ageElm.setText(String.valueOf(emp.getAge()));
  18.           }
  19.             
  20.             OutputFormat format = OutputFormat.createPrettyPrint();
  21.             format.setEncoding("GBK");    // 指定XML编码        
  22.             XMLWriter writer = new XMLWriter(new FileWriter(fileName),format);
  23.             
  24.             writer.write(document);
  25.             writer.close();
  26.       } catch (Exception e) {
  27.         e.printStackTrace();
  28.       }
  29.   }
  30. }

这样昨完以后感觉很好,因为我们成功的把变化和不变分离开来,不变的部分放在了抽象基类中,而容易变化的部分放在了两个具体的子类中,这样如果再增加一种新文件格式,从抽象基类再扩展出一个子类即可。很好,这样就不怕变化了。客户对此也没有异议。

 

调用示例如下:

  1. List<Employee> emps=new ArrayList<Employee>();
  2. emps.add(new Employee("Andy",true,21));
  3. emps.add(new Employee("Bill",false,23));
  4. emps.add(new Employee("Cindy",true,25));
  5. emps.add(new Employee("Douglas",false,28));
  6. FileMaker fileMaker=new CSVFileMaker();
  7. fileMaker.makeFile(emps, "1.csv");
  8. fileMaker=new XMLFileMaker();
  9. fileMaker.makeFile(emps, "2.xml");

客户看到了我们的调用的例子,觉得应该更灵活一些,他说存成各种不同的文件是通过点击按钮来实现的,如果每个按钮的事件处理函数都要生成具体子类岂不是太死板了吗?这样做每个文件下载按钮的事件处理代码不是都不一样?

 

有点道理,如今理解到这一层的客户实在是不多见了。不过很容易满足他的需求,我们可以引入反射的方法:

  1. public static void main(String[] args) {
  2.   List<Employee> emps=new ArrayList<Employee>();
  3.   emps.add(new Employee("Andy",true,21));
  4.   emps.add(new Employee("Bill",false,23));
  5.   emps.add(new Employee("Cindy",true,25));
  6.   emps.add(new Employee("Douglas",false,28));
  7.   
  8.   callByReflect("csv",emps,"1.csv");
  9.   callByReflect("xml",emps,"2.xml");
  10. }
  11. public static void callByReflect(String type,List<Employee> emps,String fileName){
  12.   try{
  13.     Class cls=Class.forName("com.heyang."+type.toUpperCase()+"FileMaker");
  14.     FileMaker fileMaker=(FileMaker)cls.newInstance();
  15.     fileMaker.makeFile(emps, fileName);
  16.   }
  17.   catch(Exception ex){
  18.     ex.printStackTrace();
  19.   }
  20. }

因为按钮上的文字和类名是有关的,如下载CSV的按钮上就有CSV的文字,这可以通过正则表达式取道,再组合一下不就是类名了吗?csv到com.heyang.CSVFileMaker,xml到com.heyang.XMLFileMaker,其实变化就是三个字母而已。如果增加按钮,取出按钮中的三个字母再调用callByReflect函数即可,这个过程简直可以固化。

 

客户看到反射方法以后很是满意,没有意见了。待客户走后,项目经理把你拉到一边,说:

“你刚才的方法不错,确实很强,但看得懂反射并能灵活掌握的人水平要够一年经验才行,维护的活让一年经验的人去干太可惜了,最好改改,最好达到让新手也能掌握并修改的程度。”。

 

没办法,领导总有领导的考虑,他这么说也很合理,成本问题我可以不考虑,但如果把程序搞得复杂貌似NB,能让一些学艺不精的人产生云山雾罩的感觉,有时还能被人尊称一声“大侠”,但谁也不比谁傻多少,这声大侠不是白叫的,但是出了问题或是有了变化别人还是要找你,到头来还是给自己添乱,这些都是义务劳动,何苦来呢?还是应该改得容易些,让大家都能修改,我可不愿意半夜三更被人叫起来问问题。

 

用Spring的IoC就可以解决问题,写一个新类并配置到XML文件中对新手来说问题不大,这下可以让领导放心了,自己就更放心了。

 

IoC方案代码如下:

  1. public class Main {
  2.   public static void main(String[] args) {
  3.     List<Employee> emps=new ArrayList<Employee>();
  4.     emps.add(new Employee("Andy",true,21));
  5.     emps.add(new Employee("Bill",false,23));
  6.     emps.add(new Employee("Cindy",true,25));
  7.     emps.add(new Employee("Douglas",false,28));
  8.     
  9.     callByIoc("csv",emps,"1.csv");
  10.     callByIoc("xml",emps,"2.xml");
  11.   }
  12.   
  13.   public static void callByIoc(String type,List<Employee> emps,String fileName){
  14.     try{
  15.       ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
  16.       FileMaker fileMaker=(FileMaker)ctx.getBean(type);
  17.       fileMaker.makeFile(emps, fileName);
  18.     }
  19.     catch(Exception ex){
  20.       ex.printStackTrace();
  21.     }
  22.   }
  23. }

Bean。xml文件内容很简单吧:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
  3. <beans>
  4. <bean id="csv" class="com.heyang.CSVFileMaker"/>
  5. <bean id="xml" class="com.heyang.XMLFileMaker"/>
  6. </beans> 

好了。到这里问题就彻底结束了,终于满足了客户和上级的要求,可以回家睡个好觉了,不用担心别人打搅了。

 

态度改变一切,变化来了人总是要多做一些,心理当然是不愿意的,但抱怨或是消极抵制都不是解决问题之道;如果把它看做一个挑战的契机,凡事多思考一些,不但能解决问题,自己也会有所提高,这就是积极的态度带来的好处。

posted on 2008-08-08 12:55 sitinspring 阅读(1027) 评论(0)  编辑  收藏 所属分类: Object Orient ProgrammingSSH


只有注册用户登录后才能发表评论。


网站导航:
 
sitinspring(http://www.blogjava.net)原创,转载请注明出处.