论坛首页 Java企业应用论坛

我的酒窝

浏览 43498 次
锁定老帖子 主题:我的酒窝
该帖已经被评为精华帖
作者 正文
   发表时间:2007-01-05  
firebody 写道
ajoo 写道
没有看到我说的“不是测试的问题”么?

这里面dimple是用在production代码里面的。测试代码不用dimple。



哦,不好意思,我满脑子都是测试,哈哈。 打错靶子了。

不过,我仍然想多啰嗦几句。


问题在于某个和第三方库或者legacy system集成的部分碰巧没有Map,而只有一个PropertyMap,这时就可以用dimple。

这个例子用Dimple确实可以,但是我看不出我直接用一个Utils.convertToMap的逻辑有何优势。

那个,啥意思?你是说看不出相比于直接Utils.convertToMap有何优势?还是看不出用一个adapter有何优势?

firebody 写道

另外,对于你提到的需要拦截Connection.close ,而需要一个ConnectionAdapter 来统一拦截,以此提出了Dimple的设计。

我对这个出发点也提出疑义。  虽然这是一个很好的办法,但是我更倾向于用一个统一的接口来open,close Connection来达到统一拦截的目的,或许这个接口语义很弱,但是在一个项目/框架开发里面,把这个需求提到每个开发者的需求也不是很难的事情。

虽然,这里有点类似AOP的概念,但是对于AOP,正如ROD所说的,AOP不应该和OOP成为对立的关系,两者应该是互补的关系。 AOP应该在OOP设计良好的基础上成为良好的补充。

统一的接口来open, close又是啥吗意思?俺咋糊涂了尼。
0 请登录后投票
   发表时间:2007-01-05  
ajoo 写道
firebody 写道
ajoo 写道
没有看到我说的“不是测试的问题”么?

这里面dimple是用在production代码里面的。测试代码不用dimple。



哦,不好意思,我满脑子都是测试,哈哈。 打错靶子了。

不过,我仍然想多啰嗦几句。


问题在于某个和第三方库或者legacy system集成的部分碰巧没有Map,而只有一个PropertyMap,这时就可以用dimple。

这个例子用Dimple确实可以,但是我看不出我直接用一个Utils.convertToMap的逻辑有何优势。

那个,啥意思?你是说看不出相比于直接Utils.convertToMap有何优势?还是看不出用一个adapter有何优势?

firebody 写道

另外,对于你提到的需要拦截Connection.close ,而需要一个ConnectionAdapter 来统一拦截,以此提出了Dimple的设计。

我对这个出发点也提出疑义。  虽然这是一个很好的办法,但是我更倾向于用一个统一的接口来open,close Connection来达到统一拦截的目的,或许这个接口语义很弱,但是在一个项目/框架开发里面,把这个需求提到每个开发者的需求也不是很难的事情。

虽然,这里有点类似AOP的概念,但是对于AOP,正如ROD所说的,AOP不应该和OOP成为对立的关系,两者应该是互补的关系。 AOP应该在OOP设计良好的基础上成为良好的补充。


统一的接口来open, close又是啥吗意思?俺咋糊涂了尼。


更正一下:
是说 Dimple相比于直接Utils.convertToMap有何优势 。


第二个疑问:

意思是说原来conn.close()---> resourceManager.closeConn(conn);

0 请登录后投票
   发表时间:2007-01-05  
引用
更正一下:
是说 Dimple相比于直接Utils.convertToMap有何优势 。

第二个疑问:

意思是说原来conn.close()---> resourceManager.closeConn(conn);

dimple就是用来实现convertToMap的呀。要不你怎么实现Utils.convertToMap?


你是说ResourceManager.closeConnection(conn)么?嗯,老实说我一点也看不出这种方法为什么可以不被禁止。

其实,这个问题之所以被提出,一个原因就是因为一个遗留系统使用了ConnectionManager.checkIn(Connection)的方法。而我要调用Ibatis,(Ibatis对ConnectionManager当然是一无所知),于是为了保证安全我才把一个禁止了close()的Connection对象传递给IBatis,以避免IBatis,或者IBatis的用户代码调用Connection.close()。其实所有的麻烦罪魁祸首就是这个遗留系统非要标新立异弄什么ConnectionManager.checkIn,而不是遵循标准,允许Connection.close()
0 请登录后投票
   发表时间:2007-01-05  
ajoo 写道
引用
更正一下:
是说 Dimple相比于直接Utils.convertToMap有何优势 。

第二个疑问:

意思是说原来conn.close()---> resourceManager.closeConn(conn);

dimple就是用来实现convertToMap的呀。要不你怎么实现Utils.convertToMap?


你是说ResourceManager.closeConnection(conn)么?嗯,老实说我一点也看不出这种方法为什么可以不被禁止。

其实,这个问题之所以被提出,一个原因就是因为一个遗留系统使用了ConnectionManager.checkIn(Connection)的方法。而我要调用Ibatis,(Ibatis对ConnectionManager当然是一无所知),于是为了保证安全我才把一个禁止了close()的Connection对象传递给IBatis,以避免IBatis,或者IBatis的用户代码调用Connection.close()。其实所有的麻烦罪魁祸首就是这个遗留系统非要标新立异弄什么ConnectionManager.checkIn,而不是遵循标准,允许Connection.close()


dimple就是用来实现convertToMap的呀。要不你怎么实现Utils.convertToMap?

Utils.convertoMapFromPropertyMap(PropertyMap pm){ Map result = new HashMap();  ...
return result; }


对于你的第一个帖子,确实会碰到这样的问题,除了用一个Adapter, 没有更好的办法解决了。 不过对于后面提到的要替代EasyMock,我不觉得Dimple有充分的优势。
0 请登录后投票
   发表时间:2007-01-05  
Utils.convertoMapFromPropertyMap(PropertyMap pm){ Map result = new HashMap(); ...
return result; }

为什么不这么做呢?因为这样做的语义是不同的。

这个PropertyMap可能被注射进组件后才被populate。
一个简单化的例子:
PropertyMapImpl pmap = new PropertyMapImpl();
MyClass mc = new MyClass(convertToMap(pmap));
pmap.addProperty(...);
mc.doSomething();

所以不能copy,只能adapter

另外,dimple不是要替代easymock。

只是说,当前提是你并不想做mock testing,而仅仅是为了方便用easymock来创建stub的时候,用dimple直接创建stub可能更合适。
0 请登录后投票
   发表时间:2007-01-05  
ajoo 写道
Utils.convertoMapFromPropertyMap(PropertyMap pm){ Map result = new HashMap(); ...
return result; }

为什么不这么做呢?因为这样做的语义是不同的。

这个PropertyMap可能被注射进组件后才被populate。
一个简单化的例子:
PropertyMapImpl pmap = new PropertyMapImpl();
MyClass mc = new MyClass(convertToMap(pmap));
pmap.addProperty(...);
mc.doSomething();

所以不能copy,只能adapter

另外,dimple不是要替代easymock。

只是说,当前提是你并不想做mock testing,而仅仅是为了方便用easymock来创建stub的时候,用dimple直接创建stub可能更合适。


恩,更具体的情况或许我没有考虑到,但是从你提供的runApi(Map map) 这个接口来看,runApi它还需要知道这个map是PropertyMap?  还需要依赖PropertyMap更深的语义?

0 请登录后投票
   发表时间:2007-01-05  
引用
恩,更具体的情况或许我没有考虑到,但是从你提供的runApi(Map map) 这个接口来看,runApi它还需要知道这个map是PropertyMap? 还需要依赖PropertyMap更深的语义?

不需要了解。但是,语义上一个clone或者是那个对象自己是有区别的。就说不是PropertyMap而是直接的Map,下面两个代码也是有区别的:
Map someMap = new HashMap();
MyClass mc = new MyClass(someMap);
someMap.put(...);
mc.doSomething();

Map someMap = new HashMap();
MyClass mc = new MyClass(cloneMap(someMap));
someMap.put(...);
mc.doSomething();

对么?

当我需要第一段代码的语义的时候,你的Utils.convertToMap就不成了。
0 请登录后投票
   发表时间:2007-01-05  
ajoo 写道
引用
恩,更具体的情况或许我没有考虑到,但是从你提供的runApi(Map map) 这个接口来看,runApi它还需要知道这个map是PropertyMap? 还需要依赖PropertyMap更深的语义?

不需要了解。但是,语义上一个clone或者是那个对象自己是有区别的。就说不是PropertyMap而是直接的Map,下面两个代码也是有区别的:
Map someMap = new HashMap();
MyClass mc = new MyClass(someMap);
someMap.put(...);
mc.doSomething();

Map someMap = new HashMap();
MyClass mc = new MyClass(cloneMap(someMap));
someMap.put(...);
mc.doSomething();

对么?

当我需要第一段代码的语义的时候,你的Utils.convertToMap就不成了。


哦,看了你的代码,想了一下,估计你的意思是说,runApi的逻辑是需要改变的参数的state的,而且外面的逻辑需要依赖于这个改变的state.

如果这样的话,确实只能这样了。

不过还想啰嗦多一些题外话, 另外针对这样的设计,我觉得是一种很奇怪或者危险的设计:
a.getMap().put(..) ,a.getList().add   , 或者更甚的,就像你举的例子,直接把Map,List当作参数传来传去,在每个逻辑里面修改map/list的状态。 


换成这样:
class a{
 Map getMap() {return collections.unmodifialbale(map) } ; void addMapMember(key,value) ,..... } 



这样的话,通过明确语义的接口,逻辑至少要清晰很多 。 然后,把所有的map.put的逻辑都封装到一个方法里面,针对你提到的语义的危险,那么也可以在这class:A 这个 地方加以规范了 。


至少,不再为此想那么多tricky甚至古怪到精妙的方式了。
0 请登录后投票
   发表时间:2007-01-05  
firebody 写道
ajoo 写道
引用
恩,更具体的情况或许我没有考虑到,但是从你提供的runApi(Map map) 这个接口来看,runApi它还需要知道这个map是PropertyMap? 还需要依赖PropertyMap更深的语义?

不需要了解。但是,语义上一个clone或者是那个对象自己是有区别的。就说不是PropertyMap而是直接的Map,下面两个代码也是有区别的:
Map someMap = new HashMap();
MyClass mc = new MyClass(someMap);
someMap.put(...);
mc.doSomething();

Map someMap = new HashMap();
MyClass mc = new MyClass(cloneMap(someMap));
someMap.put(...);
mc.doSomething();

对么?

当我需要第一段代码的语义的时候,你的Utils.convertToMap就不成了。


哦,看了你的代码,想了一下,估计你的意思是说,runApi的逻辑是需要改变的参数的state的,而且外面的逻辑需要依赖于这个改变的state.

如果这样的话,确实只能这样了。

不过还想啰嗦多一些题外话, 另外针对这样的设计,我觉得是一种很奇怪或者危险的设计:
a.getMap().put(..) ,a.getList().add   , 或者更甚的,就像你举的例子,直接把Map,List当作参数传来传去,在每个逻辑里面修改map/list的状态。 


换成这样:
class a{
 Map getMap() {return collections.unmodifialbale(map) } ; void addMapMember(key,value) ,..... } 



这样的话,通过明确语义的接口,逻辑至少要清晰很多 。 然后,把所有的map.put的逻辑都封装到一个方法里面,针对你提到的语义的危险,那么也可以在这class:A 这个 地方加以规范了 。


至少,不再为此想那么多tricky甚至古怪到精妙的方式了。

runApi需要的就是一个Map,一点trick也没有。它才不管这个state是在什么时候变化的呢。这也是dependency injection的好处,组件可以简单到极点。

tricky的是这个runApi的客户代码。这本来是个遗留系统,现在要使用runApi来重构一下。原来是代码到处都是直接调用PropFactory.getInstance().getProperty(),现在要改为依赖注射的方式,把Map注射进runApi。

因为无法确定这个遗留系统本来是否依赖于状态变化,或者什么时候状态会变化,(其实,我也不想关心太多这个系统的实现细节)最好的办法就是不做任何假设,原封不动地照搬原来的语义(也就是直接使用一个PropertyMap,而不是一个PropertyMap的clone)。

这既没有什么古怪,也没有什么精妙之处。只是重构的一个原则罢了。
0 请登录后投票
   发表时间:2007-01-07  
关于那个Connection的例子,实际的情况(简化了的)是这样:
1。遗留系统用ConnectionManager.checkOut()和ConnectionManager.checkIn(Connection)这两个静态方法来创建和释放连接。(看到了吧?什么ConnectionManager, ResourceManager之类的方法都是害群之马!)
2。现在需要使用iBatis来实现一部分持久层。但是需要仍然遵循遗留系统的ConnectionManager方式。很明显,ibatis对我们自制的ConnectionManager一无所知,它只知道调用标准的Connection.close()来关闭连接。

解决方案是给ibatis配置一个DataSourceFactory类。要配置DataSourceFactory,需要提供一个特制的DataSource实现和Connection实现,让DataSource.getConnection()调用ConnectionManager.checkOut(),把ConnectionManager返回的realConnection封装成另外一个Connection实例,这个实例在被调用Connection.close()的时候,实际调用ConnectionManager.checkIn(realConnection)。

伪代码实现如下:
class LegacyConnection implements Connection {
  private final Connection realConnection;
  private boolean closed;
  public void close(){
    if(closed) return;
    ConnectionManager.checkIn(realConnection);
    closed = true;
  }
  public boolean isClosed() {
    return closed;
  }
  //所有其它的方法都委托给realConnection。
}

class LegacyDataSource implements DataSource {
  public Connection getConnection() {
    return new LegacyConnection(ConnectionManager.checkOut());
  }
  //所有其它的方法都不需要实现。
}
public class LegacyDataSourceFactory implements DataSourceFactory {
  public DataSource getDataSource() {
    return new LegacyDataSource();
  }
}


明显,上面的代码编译不过,因为我们没有实现所有的Connection和DataSource接口定义的方法。

传统的解决方法是,根据我们上面的注释,用Eclipse的自动代码生成来把所有其它的方法搞定。这样做的问题除了繁琐之外,最大的麻烦是versioning。也许你生成的代码是针对jdbc 3.0的,那么它放在jdbc 4.0下,将不能编译。

使用dimple,就不会有这个问题:

class LegacyConnection /*implements Connection*/ {
  private final Connection realConnection;
  private boolean closed;
  public void close(){
    if(closed) return;
    ConnectionManager.checkIn(realConnection);
    closed = true;
  }
  public boolean isClosed() {
    return closed;
  }
}

class LegacyDataSource /*implements DataSource*/ {
  public Connection getConnection() {
    final Connection realConnection = ConnectionManager.checkOut();
    return Implementor.proxy(Connection.class, new LegacyConnection(realConnection), realConnection);
  }
}
public class LegacyDataSourceFactory implements DataSourceFactory {
  public DataSource getDataSource() {
    return Implementor.proxy(DataSource.class, new LegacyDataSource());
  }
}


上面的代码里面,我们只实现了关心的方法,其它的都用dimple给自动搞定了。这个代码不管放到哪个jdbc版本下都工作(呵呵,当然得支持DataSource才行了)
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics