一个糟糕的设计

      3 月份开始,就开始做一个 WinForm 的项目,现在快要收工了。这是偶参与的第一个 Windows 应用。这也是一个典型的三层应用,现在也到了开始总结经验教训的时候了,温故而知新嘛。

今天要总结的是其中一个糟糕的设计,这个设计存在于一个数据访问类中,至今没有被解决掉因为修改设计需要修改大量的代码,而且不修改设计的话,我们的软件确实可以工作至少目前是这样的。

<?xml:namespace prefix = o />

 

这个类的类图大概是这样的

<?xml:namespace prefix = v />

r_DataAccess.gif 

为了简单,我们省略了其中存在的大量的public方法(包括执行存储过程、获取DataSetDataReader的大量方法和重载方法)和一些异常处理代码。图中的Instance属性实际上实现了Singleton模式。

其中,我们已ExecuteNoneQuery的执行过程为例

 o_CommonDataAccess.ExecuteNoneQuery.gif
 

从图中我们可以看出,方法首先调用this.Open()来打开数据库连接,然后执行任务,最后调用this.Close()来关闭连接。

这看起来没有什么问题,调用起来也很方便,直接CommonDataAccess.Instance.ExecuteNoneQuery就可以了,而且,由于采用了单体模式,也带来了一些其他一些好处,比如内存使用上的。事实证明,很久以来,我们都对这种模式相当满意,但是随着项目的推进,问题慢慢浮现了,由于我们的系统经常要同远端的WebServive服务连接,为了解决用户响应上的问题,我们开始使用多线程我们把长时间之行的任务放到新的线程里面执行,这些任务里面当然也包括数据访问。那么问题就是,我们的数据访问类是为单线程应用设计的,在引入其他线程以后,它开始出现不同线程访问同一数据连接在多数情况下这将导致并发错误!也就是说,当我的连接在执行一项查询的时候,如果另外一个线程使用它执行另外一个查询或者是更新,将出现错误。

我们试着修改这个类,让它可以工作在多线程环境中。但是我们发现要修改的代码非常惊人,几乎每一个使用this.Open()和使用数据连接的地方都需要更改。我们比较懒,所以我们决定只修改了一处那就是Instance属性的get访问器。

 

原先的代码是这样的

       

None.gif         CommonDataAccess instance ; 
None.gif
None.gif        
public  CommonDataAccess Instance
ExpandedBlockStart.gifContractedBlock.gif        
dot.gif
InBlock.gif              
get 
ExpandedSubBlockStart.gifContractedSubBlock.gif              
dot.gif
InBlock.gif                   
if( instance == null ) 
InBlock.gif                       instance 
= new CommonDataAccess();   
InBlock.gif
InBlock.gif                   
return instance ; 
ExpandedSubBlockEnd.gif              }
 
ExpandedBlockEnd.gif     }
 
None.gif
None.gif

 

修改以后成了这样的

      

None.gif          CommonDataAccess instance ; 
None.gif
None.gif         
public  CommonDataAccess Instance
ExpandedBlockStart.gifContractedBlock.gif         
dot.gif
InBlock.gif              
get 
ExpandedSubBlockStart.gifContractedSubBlock.gif              
dot.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif                   
/**//* 
InBlock.gif
InBlock.gif                       if( instance == null ) 
InBlock.gif
InBlock.gif                       instance = new CommonDataAccess(); 
InBlock.gif
ExpandedSubBlockEnd.gif                   
*/
 
InBlock.gif  
InBlock.gif
InBlock.gif                   
return new CommonDataAccess(); 
ExpandedSubBlockEnd.gif              }
 
InBlock.gif
ExpandedBlockEnd.gif     }
 
None.gif
None.gif

不许笑,这确实解决了多线程的问题,但再也不是Singleton了,倒是成了不断生产新产品的Factory了,而且即使在一个线程内部,我们使用Instance属性的时候,都创建了新的CommonDataAccess实例,而在我们写那些代码的时候,完全没有预料到。

这真是一个糟糕的设计,那么应该有改良的办法吧。

类图:

o_DataAccess2.gif


ExecuteNoneQuery的序列图

 

o_CommonDataAccess2.ExecuteNoneQuery().gif 

如果你还没有明白,那还是看代码吧:

 

None.gif   public   void  ExecuteNoneQuery( string  cmdText)
ExpandedBlockStart.gifContractedBlock.gif         
dot.gif
InBlock.gif              
using ( IDbConnection conn = this.Open() ) 
ExpandedSubBlockStart.gifContractedSubBlock.gif              
dot.gif
InBlock.gif                   IDbCommand cmd 
= new SqlCommand(cmdText); 
InBlock.gif                   cmd.Connection  
= conn;  
InBlock.gif
InBlock.gif                   cmd.ExecuteNonQuery(); 
ExpandedSubBlockEnd.gif              }
 
ExpandedBlockEnd.gif     }
   
None.gif
None.gif         IDbConnection Open() 
ExpandedBlockStart.gifContractedBlock.gif         
dot.gif
InBlock.gif              IDbConnection conn 
= new SqlConnection(connStr); 
InBlock.gif              conn.Open(); 
InBlock.gif              
return conn ; 
ExpandedBlockEnd.gif         }
 
None.gif
None.gif  
None.gif
None.gif

        也就是说,每次Open的时候,产生新的连接,我们不必担心new SqlConnection的效率,事实上,使用Ado.net,连接池是DotNetFrameWork自动维护的,从连接池中获取连接的速度也非常快。如果你愿意的话,你甚至可以在Open()方法中使用自己的连接池逻辑。

转载于:https://www.cnblogs.com/QuitGame/archive/2005/10/13/254256.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值