分享
 
 
 

OO随笔(关于connection pool系列的补充,兼答bonmot)

王朝java/jsp·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

OO随笔(关于connection pool系列的补充,兼答bonmot)

说起OO, 每个人都有每个人自己的见解。粗浅者如“obj.method的语法就是OO”;高深的则必侃“design pattern”.

今天我也来说说我的一孔之见。

什么是OO?

就是面向接口编程。无论你是用vtable, 或gp的function object, 或就是C的函数指针,正交分解也好,各种pattern也罢,都是面向接口编程思想的一种实现。

为什么要面向接口编程?

为了解耦。

什么是解耦?

就是把程序中互相不相关或有限相关的模块分割开来。就象收拾屋子,你希望把不同的东西放到不同的地方。把酱油和醋倒进不同的瓶子里去。

这里,对完全不相关的功能,可以简单地分开实现。

但事实上,很多情况下,不同模块之间是有互相之间的关系的。这时,就需要接口。用接口准确定义模块之间的关系。解耦前,两个模块之间共享所有信息(这个信息包括数据,也包括各自的实现细节)。解耦后,需要共享的信息被准确地定义在接口中。同时,信息的流向也被确定。

解耦的好处是什么呢?

首先,程序变得清晰了。

其次,不该暴露的实现细节被隐藏了。代码的修改变容易了。

再次,结构灵活了,通过静态多态(function object)或动态多态(vtable), 一个模块可以和任意实现接口的模块协作。原来类A只能与类B协作,解耦后可以和所有实现接口IB的类如B1, B2, ... 协作了。扩展性大大增强。自然而然就代码重用了。

编译依赖也没有了。你可以专心写和编译一个模块,不用等待其它模块的完成。

调试容易了。只要模块对一个接口调试成功,其它的接口也没有问题。于是,甚至可以用一个simple naive的实现该接口的dummy类来调试。(这点,使用template的gp不适用)

那么解耦的坏处是什么呢?

接口的定义变得很关键。解耦就是隐藏一些信息,定义一些需要共享的信息。如果接口定义的不好,隐藏了不该隐藏的信息,那么对某些需要这些信息的复杂情况来说,这个解耦就失败了。

而如果没有隐藏一些应该隐藏的信息,那么不该有的耦合仍然存在。

那么怎样解耦,又怎样定义接口呢?

这是一个纯粹业务逻辑的思考过程。这里,对编程语言的知识变得无关紧要。事实上,只要精确掌握需求,严密地分析需求和模块内部子模块之间的需求,任何一个会逻辑思考的人都可胜任这个工作。就象歌星郑智化一样,虽然不识谱,但一样写歌,只不过最后要懂谱的人把歌纪录下来。

解耦的原则很简单:精确定义需求,仔细分析需求。不要隐藏任何“需求”也许会需要的信息。不要放过任何“需求”明显不需要的信息。

而对需求不清楚的情况,宁可错放一千,不能错杀一个。总而言之,决不能隐藏可能需要的信息。

不考虑重用,重用是解耦后的自然结果。不能倒因为果!

至于对这些原则的具体的运用在前面几篇的connection pool的文章里已经有所体现了。

下面,我先针对bonmot对我的connection pool的例子的疑问进行回答。最后,再对bonmot的一个问题给出我的解决的思路。

无关紧要的问题:

1.ConnectionFactoryImpl也可以继承方式实现ConnectionFactory

其实,我最初的实现,的确是ConnectoinFactoryImpl implements ConnectionFactory的,

但后来,当我overload了instance()函数之后,我发现,这两个函数返回的ConnectionFactory的实现类的代码是不同的。于是,匿名类就诞生了。

这里,有一点值得吹嘘的是,对构造函数的隐藏,使得使用ConnectionFactoryImpl的客户代码对我的改动完全不敏感。这也就是我为什么一直鼓吹要隐藏构造函数的原因。

以下是这个类的实现:

public class ConnectionFactoryImpl

{

private ConnectionFactoryImpl(){}

static public ConnectionFactory instance(final String driver, final String url,

final String user, final String pwd)

throws SQLException, ClassNotFoundException{

final Class driverClass = Class.forName(driver);

return new ConnectionFactory(){

private final Class keeper = driverClass;

public final Connection createConnection()

throws SQLException{

return DriverManager.getConnection(url,user,pwd);

}

};

}

static public ConnectionFactory instance(final String driver, final String url)

throws SQLException, ClassNotFoundException{

final Class driverClass = Class.forName(driver);

return new ConnectionFactory(){

private final Class keeper = driverClass;

public final Connection createConnection()

throws SQLException{

return DriverManager.getConnection(url);

}

};

}

}

2.ConnectionFactoryImpl中

private final Class keeper = driverClass;//似乎多余

是啊,很多代码里都是秃秃的Class.forName(classname)。也工作的很好。不过,记得在哪篇文章里看到过,在新的java language specification里,动态加载的类有可能被垃圾回收。如果是这样,那不麻烦啦?我好容易Class.forName()加载了driver类,好嘛!哪天jvm一高兴给我回收啦!所以咱还是以防万一的好!

功能的问题

1.ConnectionPooling是实现pooling的算法,其最基本的就是getConnection(),releaseConnection(Conn)

为什么不直接在ConnectionPool定义releaseConnection()方法,而要多一个interface ConnectionHome

首先,我的ConnectionPool接口是直接给用户使用的。我在该文的第一章就提出,向用户暴露releaseConnection(Connection)是不好的。你怎样保证用户没有向oracle连接池中返回sql server连接?怎样保证用户不会把同一个连接向连接池返回两次?已经有Connection.close(), 用户为什么要调用releaseConnection?

ConnectionHome接口是PooledConnection类定义的。PooledConnection作为一个封装在物理Connection外的与pool协同工作的类,它需要知道怎样返还一个物理Connection. ConnectionHome接口只定义了一个方法:void releaseConnection(Connection), 就是描述这一需求的产物。

2.事物总是对等的,Factory用于实现物理连接,同样应该负责关闭物理连接,而不应该让pooling算法关闭物理连接。另外,获取与关闭connection应该在一个接口中实现,如果分成2个接口,就不能保证连接的实现一定对应于关闭的实现。

即Factory是物理层,pool是cache层,client是应用层。

首先,ConnectionPooling作为一个描述pooling算法的接口,它需要代表所有可能的pooling算法,所以,我们不能排除在某种pooling算法中,它会以一定的逻辑关闭物理数据库连接。因此,pooling算法一定要可以在任何时候关闭这个连接。

至于是调用Connection.close(), 还是放一个closeConnection方法在ConnectionFactory中,让我们先看看一些其它的factory的实现。

在COM中,IFactory的接口负责生产对象。但释放对象是由IUnknown::Release()负责的。

在Java中,很多Factory接口负责生产对象,但垃圾收集负责回收对象。

为什么这些factory的机制不要求生产者来销毁对象呢?

其原因在于类型安全!

举个例子:

class Factory{

public Object getObject(){

if(...)return new ClassA();

else return new ClassB();

}

public void release(Object obj){

if(obj 是ClassA){

((ClassA)obj).closeA();

}

else{

((ClassB)obj).closeB();

}

/*丑啊!*/

}

}

在这样一个工厂里,getObject方法知道生产的对象的真正类型。但在返回之后,该对象的真正类型就被丢失了。

这样,如果你再把对象送还给工厂,说:“嘿!这是从你们厂出的,现在我不用了,还给你。”对工厂来说,它需要:

1,确认这个对象真是出自本厂。(这可不那么容易)

2,确认这个对象是怎么造出来的。以便找出相应的销毁机制(也不容易)

我们为什么不把releaseConnection对用户公开?也是因为考虑到用户可能会错误返还非本厂生产的东西。

其实,当对象出厂之后,只有对象自己才知道怎样销毁自己。其它任何对象,包括生产者,都无能为力。

4.可靠性不够。表现在:

a.pool的可靠性应该与server的可靠性无关,即database server或socket server可能由于某些原因重新启动,但pool不应该也要重新启动(比如一个pool存有不同server的connection),否则就跑出错误。所以,pool因该检查物理connection的连接状态

怎么说呢?这属于ConnectionPool这个接口的语义。我们是否想让我们的pool即使数据库server崩溃了也能工作呢?

首先,这样做是否有意义呢?如果数据库server崩溃了,我们的Connection pool怎么补救呢?

其次,就算这样是有意义的,它也是ConnectionPooling的逻辑。完全可以交给一个对此负责的ConnectionPooling处理。

b.pooled Connection可能由于一个client忘记关闭,而导致整个pool阻塞。所以,应该对pooled Connection进行监控,对于超时的或其他invaild状态的pooled connection强制回收。

这个问题提的好!起初,我觉得这也只是另一个ConnectionPooling的逻辑。可以交给一个监测已分配的连接使用情况的ConnectionPooling实现来处理。但仔细一想。这样做是不好的。

首先,监视连接的使用一定会需要在连接对象上记录一些状态,象连接分配的时间,最近一次客户使用该连接的时间等等。而ConnectionPooling的语义是返回pool里的物理连接,而由ConnectionPooling2Pool类来做封装。这样,ConnectionPooling的实现就很难纪录必要的信息。当然,ConnectionPooling也可以在返回物理连接前先做一个wrapper, 把信息纪录在这个wrapper里。可是,这样一来,类型安全就得不到保障。在使用该wrapper时,就要进行downcast.

其次,监视已分配连接和管理空闲连接之间到底有多大耦合呢?能否对它们解耦呢?经过分析,我感觉,答案是:不能。监视已分配连接的算法理论上有可能需要知道空闲连接的一些信息,而反之也是一样。而且,更讨厌的是,它们之间所需要的信息量无法估计,也就是说,对一些特定的算法,它们可能是完全的紧耦合。如果按这样分析,这种ConnectionPool可能还得要求实现者直接实现ConnectionPool, 就象我们第三章里使用的方法,只能偶尔使用一些utility类,象PooledConnection之类。

不过,虽然我们不能完全把监视算法和分配算法分开。但事实上很多监视算法,分配算法确实是互不相关的。我们也许可以写一个框架,简化对这些互不相关的算法的实现。虽然对完全紧耦合的情况我们无能为力,但对多数普通的情况,我们还是可以有些作为的。而且,这样一个框架并不影响对复杂的紧耦合情况的特殊实现。

这个框架,当然应该和我们现有的框架协同工作。具体的实现思路,我将在后面给出。

c.ConnectionPoolingImpl

public final synchronized void clear(){

closeAll();

freeConn.removeAllElements();

}//没有transaction保证,有可能引起数据不一致,资源(connection)泄漏(connection没关闭,pool却拿掉了)

可以关闭一个connection,去掉一个pool对象

这里不需要transaction保证的。我们先关闭所有连接,然后再清连接池,怎么可能“connection没关闭,pool却拿掉了”呢?

扩展的问题

1.ConnectionPool是否定义成一个结构interface更好,而让pooling实现pooling算法。

pool可定义成Vector,Tree,...,负责存储遍历,而pooling负责check in,check out.

数据结构和算法永远是紧耦合的。实际上,算法决定数据结构,不可能实现定义一个数据结构,然后强迫所有算法使用。即使是Collection, Iterator之类较抽象的结构也不行。

2.可能有大型的pool,比如字库,因此有检索问题

这就是ConnectionPooling的实现者要动的脑筋了。我们的框架只定义语义和责任分工,并不牵扯这样的实现细节。

2.更复杂的是可能每个connection上有多个引用,pooling要负责给client引用最少的那个connection.

这还是一个实现的细节。不过我想不出有什么理由我们会要不同客户共享同一个连接。这是不安全的,不是吗?

3.可能同一个pool存储不同类型的对象,对不同对象的处理是否可用visitor模式。

还是ConnectionPool的实现者的事。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有