我们应该怎样看待框架

开门见山,我认为,正确使用框架的方式是,先了解你需要什么,然后再去选择适合你需要的框架。

      而在现实中,很多人往往违背了上述原则,因此便开始了无休止的争论。

      下面举例说明:

 

      持久化框架

      在众多的讨论中,对于持久化策略和持久化框架的讨论最多的,实际上我觉得如果你看过Martin Fowler的企业应用架构模式,并且开发过不同规模的项目,应该很容易权衡使用何种框架和模式。

      1 如果你厌烦了写繁冗的jdbc语句,try...catch以及判断各种数据库(如果你要面临多种数据库)的各种异常,还要担心没有正确关闭数据库连接,那么你可以使用Spring的JdbcTemplate,在出现Spring前,大多数专业程序应该都写过类似的东西,当你使用JdbcTemplate时,你应该有的感觉是:哇,我再也不用编写自己的通用代码了,而且Rod比我想的更周全(关于JdbcTemplate的详细讨论请参见《J2EE Development without EJB》)

      2 如果你觉得Insert语句中要对照十几个属性(insert into table(field1,field2,...,field30) values(value1,value2,...value30))写起来实在痛苦,而且如果这条insert语句被多次使用,你可能会写一个类,封装该条sql语句,然后通过传入参数,就像下面这样: 

 

Java代码
  1.  public class Student {  
    public class Student {
Java代码
  1. Long id;  
    Long id;
Java代码
  1.     String firstName;   
  2.     String lastName;   
  3.     int sex;   
  4.   
  5.     public void add() {   
  6.        Field[] fields=  BeanUtil.getFields(this);   
  7.        String sql="insert into "+this.getClass().getSimpleName()+" (";   
  8.        String fieldsSql="";   
  9.        String valuesSql="";   
  10.        for(Field field:fields){   
  11.            fieldsSql+=field.getName()+",";   
  12.            field.setAccessible(true);   
  13.               
  14.            if((field.getType()==String.class)||(field.getType()==char.class)){   
  15.                valuesSql+="'"+BeanUtil.getValues(field, this)+"',";   
  16.            }else if((field.getType()==Integer.class)||(field.getType()==Long.class)||field.getType()==int.class){   
  17.                   
  18.                valuesSql+=BeanUtil.getValues(field, this)+",";   
  19.            }   
  20.        }   
  21.        fieldsSql= fieldsSql.substring(0, fieldsSql.length()-1);   
  22.        valuesSql= valuesSql.substring(0, valuesSql.length()-1);   
  23.        sql=sql+fieldsSql+") values("+valuesSql+");";   
  24.        jdbcTemplate.execute(sql);   
  25.     }   
  26.         
  27. }  
    String firstName;
    String lastName;
    int sex;

    public void add() {
	   Field[] fields=	BeanUtil.getFields(this);
	   String sql="insert into "+this.getClass().getSimpleName()+" (";
	   String fieldsSql="";
	   String valuesSql="";
	   for(Field field:fields){
		   fieldsSql+=field.getName()+",";
		   field.setAccessible(true);
		   
		   if((field.getType()==String.class)||(field.getType()==char.class)){
			   valuesSql+="'"+BeanUtil.getValues(field, this)+"',";
		   }else if((field.getType()==Integer.class)||(field.getType()==Long.class)||field.getType()==int.class){
			   
			   valuesSql+=BeanUtil.getValues(field, this)+",";
		   }
	   }
	   fieldsSql= fieldsSql.substring(0, fieldsSql.length()-1);
	   valuesSql= valuesSql.substring(0, valuesSql.length()-1);
	   sql=sql+fieldsSql+") values("+valuesSql+");";
	   jdbcTemplate.execute(sql);
	}
     
}

  

    这看起来有点恶心,那就重构出一个基类,把add方法放到基类(Base.class)里面去,使得Student的代码如下:

 

 

Java代码
  1. public class Student extends Base {   
  2.     Long id;   
  3.     String firstName;   
  4.     String lastName;   
  5.     int sex;   
  6.         
  7. }  
public class Student extends Base {
    Long id;
    String firstName;
    String lastName;
    int sex;
     
}

 

 

     然后在调用代码如下:

 

       @Test

Java代码
  1. public void add() {   
  2.     Student s = new Student();   
  3.     s.id = 1l;   
  4.     s.firstName = "Xing";   
  5.     s.lastName = "Wan";   
  6.     s.sex = 0;   
  7.     s.add();   
  8. }  
public void add() {
	Student s = new Student();
	s.id = 1l;
	s.firstName = "Xing";
	s.lastName = "Wan";
	s.sex = 0;
	s.add();
}
       实际上我实现了Martin Fowler所谓的Active Record模式,而且看起来有点像Rails的ActiveRecord,而且我认为本质上是和EJB2差不多,只不过EJB2为了更加灵活的进行映射,又搞了个映射文件,这个在第3点中详述。不过我是在写本文时临时编了这么类的,因此它还不能解决关联关系,如果想在对象级别解决的话,只能做映射,就算是ActiveRecord也只不过是把这种映射内置为语法了。
       如果Active Record就够用了,那么就找一个实现该模式的框架,对于你而言刚好够简单。
      那么既然我认同ActiveRecord模式,而且我觉得自己写一个完善的实现挺费劲的,那我就选一个这样的框架好了,GRails?可能吧。但重点我想说的是,我们应该这样去选则框架。
    3 如果我认为OR不匹配确实是个问题,我需要足够的灵活性,我要实现灵活的继承映射,可以让类、属性和表、字段的名字不那么一样,在n+1和笛卡尔乘积中灵活的切换,灵活的缓存策略,不再编写SQL语句,等等,那么我想我需要一个强大的ORM,而且它实现的模式是Fowler所说的Data Mapper模式,因为其已经论述了它的好处,并且我很赞同,而我自己又不想实现这么一个ORM框架,那就用Hibernate吧。
      使用Hibernate这样的框架和ActiveRecord这样的框架应该取决于你对一些观点的态度,和你项目的实际需求,而不是别的什么:
      1 )Rod Johnson在J2EE Development without EJB中强烈反对POJO 强制继承一个抽象类,就连Struts中那种要继承Action类的做法也是勉强同意,而在Spring MVC中则只出现接口继承。同时他也觉得.Net中所有企业服务都要继承自ServicedComponent类也是一种不好的实践。对此我表示认同。
       2)我认为持久层和领域层干净的分离是必要的,因为在大型项目中,在DAO接口层后面将Hibernate DAO实现替换为Jdbc实现是会发生的,虽然Hibernate也封装了对于原生Sql的操作,不过我觉得还是也能够JdbcTemplate更简单些。甚至于我的持久化方式可能换为XML或者文件系统。
       3)你的项目规模,你做的到底是大多数简单的Web系统,还是真正复杂的企业应用。
       当然,你可能并不认同,因此会觉得Hibernate是复杂的。
       认为Hibernate是复杂的原因还在于Hibernate解决的很多问题,在很多人看来都不是问题,很多方便方法(在一个Session中重附对象自动刷新到数据库)是过度设计。另外一个方面就是“不看说明书就使用电器”(见我跟shinewang的讨论 http://www.blogjava.net/shinewang/archive/2009/01/21/245309.html#266593
      业务层框架

        1 如果你的操作只是CRUD,那么或许一个MVC框架(如Struts)+jdbcTemplate就可以搞定了(我始终觉得应该使用JdbcTemplate,因为它除了简化你的工作,似乎没有什么副作用,而且很简单。),那么就不要用一个业务层框架

        2 如果你不认同IoC,甚至于面向接口编程,或者没有学会如何进行面向接口编程(很多人都不会),那么可能使用Spring并不会让你受益。

        3 如果你不认为事务管理应该放到业务层(而应该放在DAO层,或者应该是面向DDL操作的),并且你不觉得在业务层import持久层的类(Hibernate Session或者JPA EntityManager)和异常是不优雅的(我不知道为什么有人会认为在JSP中出现sql语句是很好的做法),并且你没有试图自己实现在业务层管理事务,那么你不会理解声明式事务管理的价值。如果你不认为容器外测试、自动化单元测试是非常重要的,那么你或许也不会觉得Spring的声明式事务处理有多么珍贵。

        4 如果你的业务没有那么复杂,或者你不认为关注点分离很重要,那么也不会觉得分层架构的重要性。我认为怎么强调分层架构都不为过,以我编写Delphi程序和在微软平台下开发的经历(那工具可以完全不用分层,开发小规模应用非常爽),不分出业务层和持久层想到恐怖。

          但是,我看到很多抱怨分层太多并去赞美RoR在这方面的简洁时(或许我对RoR还不甚了解,我总怀疑鼓吹者是否用过Delphi和.Net),都只是机械的创建一个POJO,然后为其写一个DAO,然后在为这个DAO写一个Transaction类或者Manager类,然后再写一个Action或者是远程Service,每层的代码不过是委托而已,还有不少代码是在VO和POJO(甚至分出了专门的PO类或者Entity类)进行转换,DAO从来都不会得到复用,它始终是其上层Transaction类专属的,当然你不会感到分层的意义,只能怪分层太麻烦了。

 

      框架因何而生

      大多数的开源框架都是为了解决问题而出现的,而不是为了制造麻烦而出现的,当然还有那么一句经典的话,“你现在使用的方法,造成了你现在面临的问题”(大概意思,有人知道原文请告知,不胜感激)。

       优秀的程序员和专业的团队在开始项目之前一定会自己先写一个Framework的(就像Rails、Spring、Hibernate诞生的原因一样),它们为了简化项目中的代码,把一些公共的代码抽取出来,解决问题,使得日常的开发不必陷入技术的细节,或者是重复的体力活。将基础设施和业务代码分离。就是这么简单。

       当然了,这些框架的作者遇到的问题、经历的项目可能跟你并不相同,某些框架来自于复杂的企业项目,而某些则来自于简单的Web应用项目,因此他们的复杂度自然是不同的。

      如何选择框架

       因此,在你选择框架的时候,应该列出你项目技术需求清单,然后再去找一个跟你的需求清单最贴切的框架,而不是看别人用Hibernate就用Hibernate,别人说RoR简单,你就用RoR。我见过很多号称不要编程拖拖拽拽就可以开发的软件产品(比如IBM的WID,Oracle的OAF、Form,Justep的X平台),演示的时候让人万分惊喜,太快了,太快了,但是放到我们的项目里就觉得根本不是那么回事了。

       因为,框架只是帮助你做了一些你需要做的事情,但是如果有些事情你不需要框架做,框架也帮你做了,那就不好了(比如Hibernate那个自动刷新数据库的操作,比如Hibernate有时会自动关闭Jdbc批处理);而有时你想让框架做的事情,框架却没有做,那也用这个框架没啥意思了(比如你希望可以不编程就解决一个需要进行多次迭代和分支判断的问题)。

       如果你选则了一个框架,有100项功能,而你只需要其中的两项,而使用这两项功能时,你却需要花费时间去理解支持这两项功能的40多个功能,那就得不偿失了。你去选一个能够很好的解决你需要解决的那个两个功能的框架是最明智的选择。

       问题都没了,还用框架干嘛

       如果你的项目用OO数据库,那么就把Hibernate扔了吧。如果你的项目只是统计分析数据,那搞个Spring框架干嘛呢?这个问题在liujunsong的帖子http://www.javaeye.com/topic/371118中讨论过了,不细说了。

 

 

      总之,找到一个框架,替代你本来要做的事情,如果这件事情你本来都不在意或者没觉得那是一个问题,那么使用那个框架,你会觉得是冗繁的,复杂的,摸不着头脑,然后被框架折磨死,最后痛骂框架不好。

       你使用一个框架的感觉始终应该是:哇,我正想实现这个东西呢,这个框架正好帮我实现了!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值