`
txf2004
  • 浏览: 6873227 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

一次愉快的“DAO模式之旅”(三)

阅读更多

六.DAO工厂类的实现

在刚开始的时候设计DAO模式的结构时,我们按 一个典型的 DAO 实现有以下组件:

一个 DAO 工厂类
一个 DAO 接口
一个实现了 DAO 接口的具体类
数据传输对象(有时称为值对象)
如何才能使系统通过一种更加灵活地方式来使用不同的实现了 DAO 接口的具体类呢?以下将仿照Spring的bean工厂,实现一个简单的IOC容器,这种做法的目的,其实就是帮助我们更好地理解DIP,更好地掌握面向对象编程地一个原则:按接口编程,从而也对Spring的bean工厂原理有更好地理解。

通过工厂模式,我们可以依据配置文件或者其它途径得到这些DAO对象的实例,从而使业务处理程序可以方便地调用DAO接口所定义的方法。通过修改DAO工厂的代码,或者修改DAO工厂所使用的配置文件就可以产生DAO对象的不同实例,我们在这里选择后者,因为通过硬编码实现的方式对于相对简单的应用来说可以接受,但是应用一旦庞大了就不好管理了,通过配置文件来达到具体DAO实现方法的可配置性,大大地增加了系统的灵活性。

为了方便地进行配置文件的读取,书中采用了Apache软件组织的一个专门用于将XML格式转换为Java对象的公共组件-Commons Digester,有关这个组件地详细介绍我们可以从书中或者网上都可以找到相关的资料。现在我们是抛开Spring等框架地前提下用一些专门地API来实现在工厂类里读取配置文件对各种元素进行解析,获得Java bean,由于这个系统比较小,所以这两个过程在同一个Java对象中完成了。下面我们一步步地来完成这个简单地IOC容器(说它简单是因为只能通过配置文件中的名字然后把它交给工厂的一个Get方法来访问一个bean组件,没有实现复杂地组装一个对象等功能)

第一步,配置文件的规划和定义(这一步有助于了解什么是控制反转):

在这里是按照Spring中bean工厂的配置文件的格式来的,它是一个具有良了结构和可扩展性的配置文件。在这个系统中使用XML格式的文件做配置文件,Digester组件进行解析的方式有很好的扩展性,可以随时增加任意配置项。

在当前的实例中,所使用的配置项只有DAO的配置,也就是配置每个DAO接口的具体实现对象是哪一个。所以,在这里所规划的配置文件就使用config元素作为配置文件的根元素(其实我们也可以仿照spring以beans作为根元素),在config元素内包含了若干个bean元素,其中每个bean元素代表一个DAO接口的实现信息。

经过这样的规划,将这个配置文件命名为MessgeConfig.xnl,具体的实现方法如清单11

清单11

<?xml version="1.0"?>
<config>
<bean id="userDao" type="cn.hxex.message.dao.hibernate.UserDAO" />
<bean id="messageDao" type="cn.hxex.message.dao.hibernate.MessageDAO" />
</config>

这里的bean元素只有id和Type两个属性,其中的Type关键字跟Spring的class有异曲同工之处,即表示DAO接口的具体实现对象是哪个,其实这里就很好地体现了像Spring应用框架的控制反转(IOC)容器,特点很好地在这里表现出来了,真正地达到了针对接口编程,不是抽象依赖于实现而是反过来实现依赖于抽象,即我们如果想要在业务层实现什么样的功能就直接在接口里定义一个方法(留个参数,用来跟实体对象关联),然后我们就不用管底层是怎么技术来处理持久化细节了,使组件达到了可复用地目的。在这里,如果将来不使用Hibernate作为持久层组件了,或者通过其他方法实现了持久层的操作,那么只需要在这里进行相应的配置就可以使这个系统轻松地改变持久层的实现方法,而业务处理层和显示层则不需要进行任何改动了。我想这些动机就是所有的Ioc容器的出发点吧。

第二步,定义配置对象(做完这一步可以更好地了解Spring的bean工厂的内部工作情况)

在完成了配置文件的定义后还需要定义一系列的对象来表示这个配置文件,这里使用的定义原则是每个用于表示配置文件的对象与一个配置文件中的元素相对应,但这也不是绝对的,对于非常简单的元素则不需要定义单独的对象来表示。由于这个系统的配置文件比较简单,所以用于表示配置文件的对象就比较简单。在这里,用MessageConfig对象与配置文件中的config元素相对应,而BeanConfig则与bean元素相对应。下面的两个清单是这两个对象的具体实现方法,具体地实现过程与细节我们就不必深究了,也不是我们关心地重点,因为我们以后可以用Spring等其它的框架来直接用它们的配置文件就可以了~~~

清单12

/**
* 系统配置信息类
*/
public class MessageConfig
{
public static Hashtable beans;

/**
* 构造函数
*/
public MessageConfig()
{
beans = new Hashtable();
}

/**
* 增加一个BeanConfig对象
* @param bean
*/
public void addBean(BeanConfig bean)
{
beans.put(bean.getId(), bean);
}

/**
* 得到一个DAO接口对象的实例
* @param name DAO接口对象的名称
* @return 指定DAO接口的实现类的实例
*/
public Object getBean(String name)
{
BeanConfig config = (BeanConfig) beans.get(name);

if (config == null)
{
throw new MessageException("Couldn't find the bean: " + name);
}

return config.getInstance();
}

public String toString()
{
return ReflectionToStringBuilder.toString(this);
}
}

清单13

public class BeanConfig
{
private String id;
private String type;

public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}

public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}

private Object instance;
public Object getInstance()
{
if( null!=instance ) return instance;

try
{
Class cls = Class.forName( getType() );
instance = cls.newInstance();
return instance;
}
catch( Exception ex )
{
throw new MessageException( "Couldn't find class:" + getType() );
}
}

public String toString()
{
return ReflectionToStringBuilder.toString( this );
}
}
最后一步,生成bean工厂类(做完这一步就知道Spring的bean工厂功能是如此强大)

在这里主要完成的任务是进行配置文件的处理,并实现相应的bean工厂类的编写,前面已经提到过了,由于这个系统比较小,所以读取配置文件,生成配置对象的实例都在工厂类里完成了。最好地做法就是抽取出那些读取配置文件,还有生成对象实例都放在另外一个独立的类里。而bean工厂实现一个生成bean的方法,可以根据bean的id值得到相应bean实例。同样地,在这里如何解析XML配置文件并转化成Java对象的实例的功能是也不是我们关注的重点,了解下Digester就行了:

清单13

public class DaoFactory
{
private static Log log = LogFactory.getLog(DaoFactory.class);

public static final MessageConfig messageConfig;

static
{
Digester digester = new Digester();
digester.setValidating( false );

digester.addObjectCreate( "config", "cn.hxex.message.config.MessageConfig" );
digester.addSetProperties( "config" );

digester.addObjectCreate( "config/bean", "cn.hxex.message.config.BeanConfig" );
digester.addSetProperties( "config/bean" );
digester.addSetNext( "config/bean", "addBean", "cn.hxex.message.config.BeanConfig" );

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream in = classLoader.getResourceAsStream( "MessageConfig.xml" );
MessageConfig config = null;
try {
if( in!=null )
config = (MessageConfig)digester.parse( in );
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
}
messageConfig = config;
}

public static Object getDao( String name )
{
if( null==messageConfig ) return null;

return messageConfig.getBean( name );
}
}

七.总结

通过以上内容,我们至少可以学到以下两点:

一.学会怎样编写更好的 DAO 的技巧

1 注意事务界定的处理方法,不要把这些处理细节放在DAO方法里面,使代码得到高度地重用,而不是重复写同样的代码,记得是重用而不是重复!

2 DAO异常的处理,达到DAO 更容易使用、更健壮及更易于维护的效果。

二.学会了什么是针对接口编程,什么是依赖反转原则(DIP)

"针对接口编程"是面向对象的一条重要的原则,敏捷软件开发的接口隔离原则(ISP),讲的就是使用多个专门的接口比使用单一的接口好,换一种角度,从客户类(调用者)的角度讲,一个类对另外一个类的依赖性应当是建立在最小的接口之上的,从前面DAO模式的实现过程中我们也可以看到这一点。引用敏捷软件开发那本书里面的两句话:

1 高层模块不应该依赖于低层模式。两者都应该依赖于抽像。

2 抽象不应该依赖于细节,细节应该依赖于抽象

其实上面的两句话都是在强调控制反转,即依赖倒置,更通俗地理解就是上面的两句话中的动词“依赖”~~~我想这也是Spring等框架的核心原则吧。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics