《西游降魔》里面的《儿歌三百首》里面有首儿歌叫做《春天在哪里》
歌词是这样的:
春天在哪里
春天在哪里
春天就在小朋友的眼睛里
通过俺的渣英语翻译之后是这样的:
where spring is
where spring is
the fucking spring is
in javatar's eyes
yo
yo
check it out
我相信,java程序员已经意识到我说的春天是什么了:)
不过spring跟我们现在说的东东有关系么?暂时还没有-_-b
言归正传,开始我们上一章节所说的,把问题搞复杂点儿。
我们假设这个搜索业务需要lucene和mysql的支持。
通过lucene的检索获得文档ID,然后根据ID去mysql数据库查询,获得文档的标题和文本内容。
让我们从需求开始,首先操作 SearchServiceInRealBiz 类:
package cn.com.sitefromscrath.service;
import java.util.ArrayList;
import java.util.List;
import cn.com.sitefromscrath.entity.Result;
public class SearchServiceInRealBiz implements SearchService {
public List search(String keywords) {
int[] idlist = findDocIDs(keywords);
List<Result> results = getResultsByDocIDs(idlist);
return results;
}
}
当然,你会看到你的eclipse会有错误提示信息:
因为这两个方法没有定义。但是没有关系,eclipse本身自带工具,
自动生成的代码是这样子的:
实现他们就好啦:)
我们按照先前的开发流程,给出模拟实现先:
package cn.com.sitefromscrath.service;
import java.util.ArrayList;
import java.util.List;
import cn.com.sitefromscrath.entity.Result;
public class SearchServiceInRealBiz implements SearchService {
public List search(String keywords) {
int[] idlist = findDocIDs(keywords);
List<Result> results = getResultsByDocIDs(idlist);
return results;
}
private List<Result> getResultsByDocIDs(int[] idlist) {
List<Result> results = new ArrayList<Result>(idlist.length);
for(int i = 0; i < idlist.length; i++) {
int id = idlist[i];
String title = "result " + id;
String content = "something..................";
results.add(new Result(title, content));
}
return results;
}
private int[] findDocIDs(String keywords) {
return new int[]{1, 2, 3, 4};
}
}
请记住,每一步我们都应该去做测试,这里就不一一赘述。
比如,我们可以把BeanFactory run一次,看看main方法的输出会不会发生非预期的变化。
现在,虽然我没有运行tomcat查看网页,但是我能肯定,网页所展示的内容一定是正确的。
由于java程序员的本性发作,我觉得写一个DAO层,然后。。。当然是接口和实现分离虾米的。。。
lucene的实现(模拟阶段):
接口:
package cn.com.sitefromscrath.dao;
public interface LuceneDAO {
public int[] findDocIDs(String keywords);
}
实现:
package cn.com.sitefromscrath.dao;
public class LuceneDAOMock implements LuceneDAO {
@Override
public int[] findDocIDs(String keywords) {
return new int[]{1, 2, 3, 4};
}
}
Mysql的实现(模拟阶段):
接口:
package cn.com.sitefromscrath.dao;
import java.util.List;
import cn.com.sitefromscrath.entity.Result;
public interface MysqlDAO {
public List<Result> getResultsByDocIDs(int[] idlist);
}
实现:
package cn.com.sitefromscrath.dao;
import java.util.ArrayList;
import java.util.List;
import cn.com.sitefromscrath.entity.Result;
public class MysqlDAOMock implements MysqlDAO {
@Override
public List<Result> getResultsByDocIDs(int[] idlist) {
List<Result> results = new ArrayList<Result>(idlist.length);
for(int i = 0; i < idlist.length; i++) {
int id = idlist[i];
String title = "result " + id;
String content = "something..................";
results.add(new Result(title, content));
}
return results;
}
}
然后,我们把 SearchServiceInRealBiz 的代码从新组织一次,将:
package cn.com.sitefromscrath.service;
import java.util.ArrayList;
import java.util.List;
import cn.com.sitefromscrath.entity.Result;
public class SearchServiceInRealBiz implements SearchService {
public List search(String keywords) {
int[] idlist = findDocIDs(keywords);
List<Result> results = getResultsByDocIDs(idlist);
return results;
}
private List<Result> getResultsByDocIDs(int[] idlist) {
}
private int[] findDocIDs(String keywords) {
}
}
替换为:
public class SearchServiceInRealBiz implements SearchService {
public List search(String keywords) {
// int[] idlist = findDocIDs(keywords);
// List<Result> results = getResultsByDocIDs(idlist);
LuceneDAO luceneDAO = new LuceneDAOMock();
int[] idlist = luceneDAO.findDocIDs(keywords);
MysqlDAO mysqlDAO = new MysqlDAOMock();
List<Result> results = mysqlDAO.getResultsByDocIDs(idlist);
return results;
}
}
测试,stdout / eclipse console 输出无误。
[result 1]something..................
[result 2]something..................
[result 3]something..................
[result 4]something..................
当然,我们同样会发现一个问题,我们现在的类是模拟的数据啊,以后怎么切换呢?
还好,有工厂。既然我们在工厂里切换了
SearchService
那么,
LuceneDAO
MysqlDAO
同样可以在那里处理,于是,同样执行我上面提到的流程,
从需求开始,我需要一个什么样的方法,那就先定义什么方法,然后利用eclipse工具生成方法的骨架 method skeleton,然后实现它。
首先改写 SearchServiceInRealBiz :
然后,实现方法骨架:
package cn.com.sitefromscrath;
import java.util.List;
import javax.xml.rpc.ServiceFactory;
import cn.com.sitefromscrath.dao.LuceneDAO;
import cn.com.sitefromscrath.dao.LuceneDAOMock;
import cn.com.sitefromscrath.dao.MysqlDAO;
import cn.com.sitefromscrath.dao.MysqlDAOMock;
import cn.com.sitefromscrath.entity.Result;
import cn.com.sitefromscrath.service.SearchService;
import cn.com.sitefromscrath.service.SearchServiceMock;
import cn.com.sitefromscrath.service.SearchServiceInRealBiz;
public class BeanFactory {
public static boolean MOCK = true;
public static Object getBean(String id) {
if("searchService".equals(id)) {
if(MOCK) {
return new SearchServiceMock();
} else {
return getSearchService();
}
}
throw new RuntimeException("cannot find the bean with id :" + id);
}
public static LuceneDAO getLuceneDAO() {
if(MOCK) {
return new LuceneDAOMock();
} else {
throw new RuntimeException("cannot find the LuceneDAO bean");
}
}
public static MysqlDAO getMysqlDAO() {
if(MOCK) {
return new MysqlDAOMock();
} else {
throw new RuntimeException("cannot find the MysqlDAO bean");
}
}
public static SearchService getSearchService() {
if(MOCK) {
return new SearchServiceMock();
} else {
return new SearchServiceInRealBiz();
}
}
public static void main(String ... arg) {
String keywords = "test";
SearchService searchService = (SearchService)BeanFactory.getBean("searchService");
List results = searchService.search(keywords);
for(int i = 0; i < results.size(); i++) {
Result result = (Result) results.get(i);
System.out.print("[" + result.title + "]");
System.out.println(result.content);
}
}
}
测试无误。bingo!
I gotta get the GREEN BAR!!!(JUnit专用~~~)
现在,我开始发现
BeanFactory
有点儿无处不在了,一旦需要修改,哪怕是改个 BeanFactory 的名字,如果没有refactor工具,工作也是相当麻烦的。
这就是所谓的 “上帝类 God Class” “上帝对象 God Object”
为了减少影响,至少,我应该尽可能的把 BeanFactory 的字样从其他类里面移出去。
让我们高唱国际歌,“从来就没有什么救世主,也没有神仙皇帝”,对BeanFactory进行大刀阔斧的革命~~!
在我们现在的代码里,仅有 SearchServiceInRealBiz 拥抱了 “上帝”。 因此,想想办法:
public class SearchServiceInRealBiz implements SearchService {
private LuceneDAO luceneDAO;
private MysqlDAO mysqlDAO;
private SearchServiceInRealBiz(LuceneDAO luceneDAO, MysqlDAO mysqlDAO) {
super();
this.luceneDAO = luceneDAO;
this.mysqlDAO = mysqlDAO;
}
public List search(String keywords) {
int[] idlist = luceneDAO.findDocIDs(keywords);
List<Result> results = mysqlDAO.getResultsByDocIDs(idlist);
return results;
}
}
当然 BeanFactory 肯定会报错的,
必须说明:我期待这样的报错(编译时报错),这样我们才能发现对代码进行调整之后会影响那些地方。然后随之做出对应调整。
参考另外一种方式:
private LuceneDAO luceneDAO;
private MysqlDAO mysqlDAO;
public void setLuceneDAO(LuceneDAO luceneDAO) {
this.luceneDAO = luceneDAO;
}
public void setMysqlDAO(MysqlDAO mysqlDAO) {
this.mysqlDAO = mysqlDAO;
}
public List search(String keywords) {
int[] idlist = luceneDAO.findDocIDs(keywords);
List<Result> results = mysqlDAO.getResultsByDocIDs(idlist);
return results;
}
这样eclipse不会报错,但是。。。。。。。你失去了改正的机会!
混过了编译时检测,跑不掉运行时报错D,亲!而且,这个问题会隐蔽的让你吐血~~~~
(其实,我在暗示你应该在spring的xml中选择哪一种装配方式……)
现在,让我们修复BeanFactory 的报错:
public static SearchService getSearchService() {
if(MOCK) {
return new SearchServiceMock();
} else {
LuceneDAO luceneDAO = getLuceneDAO();
MysqlDAO mysqlDAO = getMysqlDAO();
return new SearchServiceInRealBiz(luceneDAO, mysqlDAO);
}
}
RUN一次,ok,没有问题。 I LOVE GREE BAR!
《儿歌三百首》绝非浪得虚名~~
to be continued....