spring揭秘—-(1)
2.1-2.笔记
/*
在我经历的FX项目中,经常需要近乎实时地为客户提供外汇新闻。
通常情况:
都是先从不同 的新闻社订阅新闻来源,
然后通过批处理程序定时地到指定的新闻服务器抓取最新的外汇新闻,
接着将这些新闻存入本地数据库,最后在FX系统的前台界面显示。
*/
// 为客户提供外汇新闻
public class FXNewsProvider {
// 依赖IFXNewsListener来帮助 抓取 新闻内容 == 假设默认使用道琼斯(Dow Jones)新闻社的新闻,见构造函数
private IFXNewsListener newsListener;
// 并依赖IFXNewsPersister 存储 抓取的新闻
private IFXNewsPersister newPersistener;
/*
假设默认使用道琼斯(Dow Jones)新闻社的新闻,
那么我们相应地提供了DowJonesNewsListener
DowJonesNewsPersister两个实现。
需要在 构造函数中 构造IFXNewsProvider依赖的这两个类,(以下将这种被其他类依赖的类或对象,简称为“依赖类”、“依赖对象”),
如代码清单2-2所示。
*/
// 传统方式:依赖
public FXNewsProvider()
{ //简称为“依赖类”、“依赖对象”
newsListener = new DowJonesNewsListener();
newPersistener = new DowJonesNewsPersister();
}
/*
对于FXNewsProvider来说:
那就是在getAndPersistNews()方法调用newsListener的相应方法时,newsListener能够准备就绪就可以了。
如果有人能够在我们需要时将某个依赖对象送过来,为什么还要大费周折地自己去折腾(意思是自己构造函数中创建newsListener对象呢)?
*/
// IoC方式之一:构造
public FXNewsProvider(IFXNewsListener newsListner,IFXNewsPersister newsPersister)
{
this.newsListener = newsListner;
this.newPersistener = newsPersister;
}
// IoC方式之二:setter()
public void setNewsListener(IFXNewsListener newsListener) {
this.newsListener = newsListener;
}
public IFXNewsListener getNewsListener() {
return newsListener;
}
//--------------------------------------------------------------
public void setNewPersistener(IFXNewsPersister newPersistener) {
this.newPersistener = newPersistener;
}
public IFXNewsPersister getNewPersistener() {
return newPersistener;
}
// // IoC方式之三:接口
// 获取所抓取的新闻进行存取操作
public void getAndPersistNews()
{ //可获取的每一条新闻都进行编号id 集合
String[] newsIds = newsListener.getAvailableNewsIds();
// 集合不为空不结束
if(ArrayUtils.isEmpty(newsIds))
{
return;
}
//循环取出
for(String newsId : newsIds)
{ //每一条新闻
FXNewsBean newsBean = newsListener.getNewsByPK(newsId);
//存入 数据库
newPersistener.persistNews(newsBean);
newsListener.postProcessIfNecessary(newsId);
}
}
}
## 2.3 IoC的附加值 ##
// 为客户提供外汇新闻
public class FXNewsProvider {
// 依赖IFXNewsListener来帮助 抓取 新闻内容 == 假设默认使用道琼斯(Dow Jones)新闻社的新闻,见构造函数
private IFXNewsListener newsListener;
// 并依赖IFXNewsPersister 存储 抓取的新闻
private IFXNewsPersister newPersistener;
/*
假设默认使用道琼斯(Dow Jones)新闻社的新闻,
那么我们相应地提供了DowJonesNewsListener
DowJonesNewsPersister两个实现。
需要在 构造函数中 构造IFXNewsProvider依赖的这两个类,(以下将这种被其他类依赖的类或对象,简称为“依赖类”、“依赖对象”),
--------------------------------------------------------------------------------------------------------------------
对于前面例子中的FXNewsProvider来说,在使用IoC重构之前,如果没有其他需求或变动,不光看起来,用起来也是没有问题的。
但是,当系统中需要追加逻辑以处理另一家新闻社的新闻来源时,------>问题就来了.
突然有一天,客户告诉你,我们又搞定一家新闻社,现在可以使用他们的新闻服务了,这家新闻社叫MarketWin24。
这个时候,你该如何处理呢?
*/
***// 传统方式:依赖=====只要new FXNewsProvider对象,它的依赖就定了下来,无法改变。***
public FXNewsProvider()
{ //简称为“依赖类”、“依赖对象”
newsListener = new DowJonesNewsListener();
/**
* 首先,毫无疑问地,应该先根据MarketWin24的服务接口提供一个MarketWin24NewsListener实现
* 为了解决问题,我们可能要重新实现一个继承自FXNewsProvider的MarketWin24NewsProvider
* (修改里面的构造参数,newsListener = new MarketWin24NewsListener(););或者干脆重新写一个类似的功能。
*/
// newsListener = new MarketWin24NewsListener();
//其次,因为都是相同的数据访问逻辑,所以原来的DowJonesNewsPersister可以重用,我们先放在一边不管。
newPersistener = new DowJonesNewsPersister();
}
// 获取所抓取的新闻进行存取操作
public void getAndPersistNews()
{ //可获取的每一条新闻都进行编号id 集合
String[] newsIds = newsListener.getAvailableNewsIds();
// 集合不为空不结束
if(ArrayUtils.isEmpty(newsIds))
{
return;
}
//循环取出
for(String newsId : newsIds)
{ //每一条新闻
FXNewsBean newsBean = newsListener.getNewsByPK(newsId);
//存入 数据库
newPersistener.persistNews(newsBean);
newsListener.postProcessIfNecessary(newsId);
}
}
}
添加IoC之后的优点体验:
// 为客户提供外汇新闻
public class FXNewsProvider {
// 依赖IFXNewsListener来帮助 抓取 新闻内容 == 假设默认使用道琼斯(Dow Jones)新闻社的新闻,见构造函数
private IFXNewsListener newsListener;
// 并依赖IFXNewsPersister 存储 抓取的新闻
private IFXNewsPersister newPersistener;
// IoC方式之一:构造
public FXNewsProvider(IFXNewsListener newsListner,IFXNewsPersister newsPersister)
{
this.newsListener = newsListner;
this.newPersistener = newsPersister;
}
// 获取所抓取的新闻进行存取操作
public void getAndPersistNews()
{ //可获取的每一条新闻都进行编号id 集合
String[] newsIds = newsListener.getAvailableNewsIds();
// 集合不为空不结束
if(ArrayUtils.isEmpty(newsIds))
{
return;
}
//循环取出
for(String newsId : newsIds)
{ //每一条新闻
FXNewsBean newsBean = newsListener.getNewsByPK(newsId);
//存入 数据库
newPersistener.persistNews(newsBean);
newsListener.postProcessIfNecessary(newsId);
}
}
}
// --------------------------------------------------------------------------------------------------------------
//在构造FXNewsProvider对象时,很方面的进行扩展
FXNewsProvider dowJonesNewsProvider = new FXNewsProvider(new DowJonesNewsListener(),new DowJonesNewsPersister());
// ...使用了DowJonesNews家的报社
FXNewsPrivider marketWin24NewsProvider = new FXNewsProvider(new MarketWin24NewsListener(),new DowJonesNewsPersister());
// ...使用了MarketWin24News家的报社
/*
随着开源项目的成功,TDD(Test Driven Developement ,测试驱动开发)已经成为越来越受重视的一种开发方式。
因为保证业务对象拥有良好的可测试性,可以为最终交付高质量的软件奠定良好的基础,同时也拉起了产品质量的第一道安全网。
所以对于软件开发来说,设计开发可测试性良好的业务对象是至关重要的----->而IoC模式可以让我们更容易达到这个目的。
比如,
使用IoC模式后,为了测试FXNewsProvider,
我们可以根据测试的需求,提供一个MockNewsListener给FXNewsProvider。
在此之前,我们无法将对DowJonesNewsListener的依赖排除在外,从而导致难以开展单元测试。
而现在,单元测试则可以毫无牵绊地进行,代码清单2-6演示了测试取得新闻失败的情形。
*/
// 测试新闻取得失败的MockNewsListner定义
public class MockNewsListener implements IFXNewsListener{
public String[] getAvailableNewsIds() {
throw new FXNewsRetrieveFailureException();
}
public FXNewsBean getNewsByPK(String newsId) {
// TODO
return null;
}
public void postProcessIfNecessary(String newsId) {
// TODO
}
}
// 相应的FXNewsProvider的单元测试类
public class FXNewsProviderTest extends TestCase {
private FXNewsProvider newsProvider;
@Override
protected void setUp() throws Exception {
super.setUp();
newsProvider = new FXNewsProvider(new MockNewsListener(),new MockNewsPersister());
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
newsProvider = null;
}
public void testGetAndPersistNewsWithoutResourceAvailable() {
try{
newsProvider.getAndPersistNews();
fail("因为MockNewsListener没有新闻支持,我们应该不能得到上面。");
}
catch(FXNewsRetrieveFailureException e){
//……
}
}
}