Javen-Studio 咖啡小屋

http://javenstudio.org - C++ Java 分布式 搜索引擎
Naven's Research Laboratory - Thinking of Life, Imagination of Future

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  24 随笔 :: 57 文章 :: 170 评论 :: 4 Trackbacks

学习笔记之ORM设计中用到的模式
by Naven at 2005-09-19


DAO 模式

DAO 模式即 Data Access Object 模式,实际是两个模式的组合,即 Data Accessor 模式和 Active Domain Object 模式,其中 Data Accessor 模式实现了数据访问和业务逻辑的分离,而 Active Domain Object 模式实现了业务数据的对象化封装,一般都是将这两个模式组合使用。

DAO 模式通过对业务层提供数据抽象层接口,实现了以下目标:
1、数据存储逻辑的分离:通过对数据访问逻辑进行抽象,为上层结构提供抽象化的数据访问接口。
2、数据访问底层实现的分离:通过将数据访问划分为抽象层和实现层,从而分离了数据使用和数据访问的底层实现细节。
3、资源和调度的分离:将数据访问逻辑从业务逻辑中脱离开来,使得在数据访问层实现统一的资源调度,通过数据库连接池以及各种缓存机制(Statement Cache,Data Cache等)的配合使用,大幅度提升系统性能。
4、数据抽象:通过对底层数据的封装,为业务层提供一个面向对象的接口,使得业务逻辑开发人员可以面向业务中的实体进行编码。即对业务层屏蔽了数据库访问的底层实现,业务层仅包含于本领域相关的逻辑对象和算法。

总结来说,DAO 模式可以这么理解:
Data Accessor Object (DAO)= Data + Accessor + Domain Object

举例来说,如下(引自《深入浅出Hibernate》):
public Double calcAmount(String customerid, double amount)
{
    // 根据客户ID获得客户记录
    Customer customer = CustomerDAO.getCustomer(custmerid);
    // 根据客户等级获得打折规则
    Promotion promotion = PromotionDAO.getPromotion(customer.getLevel());
    // 累积客户总消费额,并保存累计结果
    customer.setSumAmount(customer.getSumAmount().add(amount);
    CustomerManager.save(customer);
    // 返回打折后的金额
    return amount.multiply(protomtion.getRatio());
}
从这段代码看出,通过 DAO 模式对各个数据对象进行封装,对业务层屏蔽了数据库访问的底层实现,业务层仅包含于本领域相关的逻辑对象和算法。


Abstract Factory 模式

Abstract Factory 模式即 抽象工厂创建型模式,它提供一个创建一系列相关或相互依赖对象的接口,而无需制定它们具体的类。换句话说,就是向调用者提供一个接口,使得在不必指定产品的具体类型情况下,创建多个产品族中的产品对象,这就是抽象工厂模式的用意。
作为最常用的创建模式,抽象工厂模式在这里起到连接接口和实现的桥梁作用。通过该模式,可以根据具体需要加载相应的实现,并将此实现作为所对应接口的一个实例提供给业务层使用。

如抽象如下接口(引自《深入浅出Hibernate》):
public interface CustomerDAO {
    public Customer getCustomer(String custid);
    public void save(Customer customer);
}
业务层这样使用:
CustomerDAO custDAO = (CustomerDAO) DAOFactory.getDAO(CustomerDAO.class);
Customer customer = custDAO.getCustomer(customerID);

也就是说,业务层通过接口调用底层实现,具体的DAO实现类不会出现在业务代码中。
不过却带来了另一个缺憾,即混杂了一些数据访问层的内容,如 DAOFactory.getDAO() 方法的调用,所以还需引入 Proxy 模式加以改良。


Proxy 模式

Proxy 模式即 代理结构型模式,它为其他对象提供一种代理以控制对这个对象的访问。比如说 Windows 的快捷方式和 Unix 的文件/目录的Link文件就是一种代理的例子。
引入 Proxy 模式的作用是通过提供一个中间层(Proxy),将上层调用接口与下层的实现相衔接,以做到保持业务代码的简洁,消除 Factory 模式带来的缺憾。

改进后的业务代码如下(引自《深入浅出Hibernate》):
public Double calcAmount(String customerid, double amount)
{
    // 根据客户ID获得客户记录
    Customer customer = CustomerProxy.getCustomer(custmerid);
    // 根据客户等级获得打折规则
    Promotion promotion = PromotionProxy.getPromotion(customer.getLevel());
    // 累积客户总消费额,并保存累计结果
    customer.setSumAmount(customer.getSumAmount().add(amount);
    CustomerManager.save(customer);
    // 返回打折后的金额
    return amount.multiply(protomtion.getRatio());
}
而在 CustomerProxy类和 PromotionProxy类中实现调用 CustomerDAO类和 PromotionDAO类的方法。


Decorator 模式

Decorator 模式即 装饰结构型模式,又名包装(Wrapper)模式。目的是利用一个对象,透明地为另一个对象添加新的功能,是继承关系的一个替代方案。就增加功能来说,Decorator 模式相比生成子类更为灵活。简单来说,就是通过一个 Decorator 对原有对象进行封装,同时实现与原有对象相同的接口,从而得到一个基于原有对象的,对即有接口的增强性的实现。

在以下情况下应当使用 Decorator 模式:
1、需要扩展一个类的功能,或给一个类增加附加责任。
2、需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
3、需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实。

Decorator 模式有如下缺点:
使用装饰模式会产生比使用继承关系更多的对象,更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。

举例来说(引自《深入浅出Hibernate》):
如果要实现连接池,并且在 Connection 对象使用后调用 close() 时不关闭连接,而是释放到连接池中,则可以考虑实现一个 ConnectionDecorator 类来做,它实现 Connection 类所有方法,并且所有方法都转调 Connection 类的相同方法。

public class ConnectionDecorator
    implements Connection {

    private Connection dbconn;
   
    public ConnectionDecorator(Connection conn) {
        this.dbconn = conn; // 实际从数据库获得的 Connection 引用
    }

    /**
     * 次方法将被子类覆盖,以实现数据库连接池的连接返回操作
     */
    public void close() throws SQLException {
        this.dbconn.close();
    }

    // 其他方法类似,均调用 dbconn.xxx方法

}

然后就可以实现一个类继承 ConnectionDecorator,而覆盖其 close() 方法即可,如下:

public class PooledConnection
    extends ConnectionDecorator
    implements Connection {
   
    private ConnectionPool connPool;
   
    public PooledConnection(ConnectionPool pool, Connection conn) {
        super(conn);
        connPool = pool;
    }
   
    /**
     * 覆盖close方法,将数据库连接返回连接池,而不是直接关闭连接
     */
    public void close() throws SQLException {
        connPool.releaseConnection(this.dbconn);
    }
   
}

同样的道理,还可以利用 Decorator 模式对 DriverManager类进行同样的改造,从而最小化数据库连接池对传统 JDBC 编码方式的影响.
但是由于 Decorator 模式的缺点,会导致大量类似的小类,并且由于此模式要求实现与目标对象一致的接口,Connection 接口中定义的方法众多,也必须在 ConnectionDecorator 类中逐一实现,虽然只是简单的委托实现。随意接下来引入 Dynamic Proxy 模式很好地解决了这一问题。


Dynamic Proxy 模式

Dynamic Proxy 模式是 JDK1.3 版本中引入的一种新的动态代理机制(Dynamic Proxy),它为 Java 带来了极佳的运行期扩展能力。严格来说,Dynamic Proxy 本身并非一种模式,只能算是 Proxy 模式的一种动态实现方式。感觉有点像 Win32 里 hook 机制。

下面通过上面连接池的例子来解释 Dynamic Proxy 机制(引自《深入浅出Hibernate》):
Dynamic Proxy 模式可以解决 Decorator 模式带来的弊端。通过实现一个绑定到 Connection 对象的 InvocationHandler 接口来实现,使得可以在 Connection.close() 方法被调用时将其截获,并以新的 close() 方法替代,实现连接调用关闭方法时返回到数据库连接池中的目的。
下面是 ConnectionHandler 类,它实现了 InvocationHandler 接口,按照 Dynamic Proxy 机制的定义,invoke() 方法将截获所有代理对象的方法调用操作,这里通过 invoke() 方法截获 close() 方法来处理。

public class ConnectionHandler
    implements Invocationhandler {
   
    Connection dbconn;
    ConnectionPool pool;
   
    public ConnectionHandler(ConnectionPool connPool) {
        this.pool = connPool;
    }
   
    /**
     * 将动态代理绑定到指定 Connection 对象
     */
    public Connection bind(Connection conn) {
        this.dbconn = conn;
       
        Connection proxyConn =
            (Connection) Proxy.newProxyInstance(
                conn.getClass().getClassLoader(),
                conn.getClass().getInterfaces(),
                this);
               
        return proxyConn;
    }
   
    /**
     * 方法调用拦截器
     * 判断当前调用的方法是否 "close" 方法
     * 如是,调用 pool.releaseConnection() 方法替代
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
       
        Object obj = null;
       
        // 如果调用的方法是 "close" 方法
        if( "close".equals(method.getName()) ) {
            pool.releaseConnection(dbconn);
        }else {
            obj = method.invoke(dbconn, args);
        }
       
        return obj;
    }
}

另外 DbConnectionPool.getConnection() 方法也需要做修改以使用 ConnectionHandler 类:

pubic synchronised Connection getConnection()
    throws DBException {
   
    Connection conn;
   
    // 这里代码从连接池中获取一个空闲的连接,如无空闲连接,则创建一个新的连接
    ...
   
    // 这里调用 ConnectionHandler 类来绑定获得的连接,使之变为一个连接代理返回给用户
    ConnectionHanler connHanler = new ConnectionHanler(this);
    return connHanler.bind(conn);
}

到这里可以看到,基于 Dynamic Proxy 模式的实现相对 Decorator 更加简洁明了。

参考文献:1、《深入浅出Hibernate》

posted on 2005-10-03 12:24 Javen-Studio 阅读(1359) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理