开发,从需求出发 · 之三 春天在哪里

《西游降魔》里面的《儿歌三百首》里面有首儿歌叫做《春天在哪里》

歌词是这样的:

春天在哪里

春天在哪里

春天就在小朋友的眼睛里

通过俺的渣英语翻译之后是这样的:

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....

开发,从需求出发 · 之三 春天在哪里

时间: 2024-10-25 12:26:43

开发,从需求出发 · 之三 春天在哪里的相关文章

开发,从需求出发 &#183; 之四 春天在这里

首先,我要大字标语表达立场: 你所使用的framework & non-core features,就跟女人穿在身上的衣服一样,越少越好! 扯淡完毕,说正经的. 让我们继续盯着花姐--啊,不--是 BeanFactory看. public static SearchService getSearchService() { if(MOCK) { return new SearchServiceMock(); } else { LuceneDAO luceneDAO = getLuceneDAO()

开发,从需求出发 &amp;#183; 之四 春天在这里

首先,我要大字标语表达立场: 你所使用的framework & non-core features,就跟女人穿在身上的衣服一样,越少越好! 扯淡完成,说正经的. 让我们继续盯着花姐--啊,不--是 BeanFactory看. public static SearchService getSearchService() { if(MOCK) { return new SearchServiceMock(); } else { LuceneDAO luceneDAO = getLuceneDAO()

开发,从需求出发 &#183; 之一 从视觉开始

从需求出发并不是一句空话,在开发过程中也是如此. 从需求出发,实质上是暗合了极限编程和测试驱动开发的一些思想. 鉴于网站开发是一个比较流行的方向,我打算从一个网站开始,阐述一下自己对"需求驱动开发"的理解,并将其引申到一个更广泛的领域. 首先,我们假设一个需求: 我们需要实现一个类似google的网站,用户通过web浏览器访问,在首页输入框中查询,返回搜索的结果. 效果如下图所示: STEP 0,通过eclipse创建一个web项目:sitefromscratch,文件结构如图所示:

开发,从需求出发 &#183; 之二 造飞机的工厂

CD镇楼~~! 现在,让我们切换到后端开发人员的角度看问题.我们需要做的是实现一下这个类,让它返回真实的业务数据. package cn.com.sitefromscrath.service; import java.util.ArrayList; import java.util.List; import cn.com.sitefromscrath.entity.Result; public class SearchService { public List search(String key

BEGINNING SHAREPOINT&#174; 2013 DEVELOPMENT 第1章节--SharePoint 2013 介绍 处理开发人员需求

BEGINNING SHAREPOINT? 2013 DEVELOPMENT 第1章节--SharePoint 2013 介绍 处理开发人员需求 SharePoint本质上是一个平台.你必须理解哪些平台功能,才知道SharePoint如何能帮助你(开发人员).当你探索并了解组成此平台的功能范围时,你会看到一些有趣而引人的机会出现在开发人员面前. 我们看一个实际例子.如你所知,一个业务生产平台意味着终端用户可以是他们更加协同,并在日常工作生活中更多产--SharePoint当然能够做到.它可以立刻

办公管理支撑流程能力PaaS平台运维开发软件需求设计方案

1.概述 办公管理支撑流程能力PaaS平台(以下文中简称"能力平台")运维开发是指基于Cordys BOP 4 PaaS平台,通过二次开发,为运维开发人员提供快速支撑办公管理流程开发.实施.再造的能力,也为管理流程全生命周期管理提供数据支撑能力.管理目标及软件需求如下: (1)开发/运维人员 开发/运维人员是指系统平台管理人员.运维人员,例如:省公司与地市公司信息化系统管理员.开发/运维人员为专业信息化管理员,都是受过专业计算机教育,懂计算机软件维护.开发,但是,不是专业程序员,也就是

从实际需求出发----笑谈一个大数据应用

原始文章,如转载,请注明出处! 这是我在北航大数据同学群里转的一个笑话: 从这里看到,从实际的需求出发,才能让产品功能完善.如果天天将产品功能定位在天上,或觉得自己牛到我做出来的产品就是让别人适应的(比如IPHONE的最初设计),那可能最终的结果就是你没那实力,别人不能适应你.

图书馆图书管理系统开发前期需求探索

鉴于近期给一个客户开发图书馆图书管理系统,在与客户的合作过程中,遇到了很多问题,总结起来主要是前期需求不明确!因此,本文的目的主要是探讨关于图书管理系统开发前的客户需求调研,只有充分了解了客户的需求,才能做出满意的系统. 一.功能模块需求1.图书管理:图书管理.新增图书.作者.图书分类.批量导入.条码管理 2.借阅管理:图书借阅.图书归还.图书丢失.借出图书.已还图书.逾期管理等 3.用户管理:借阅人管理.新增.删除,用户类型.班级/职务.借阅卡等 4.申请购书:用户在线提交购书申请.后台审核并

分布式监控开发 01 需求

为什么要做监控? zabbix已经这么强大了,为什么要写一个监控 –熟悉IT监控系统的设计原理. 自己写的时候肯定有很多事更zabbix相匹配的. –开发一个简版的类Zabbix监控系统.为以后团队写监控做准备 zabbix在2K以上数量机器的时候,明显会吃力.小米也正是由于这个自己写了open-falcon.那么如果以后我们遇到大数量的服务器的时候,完全也会基于公司的业务去写一个监控. 那么现在练练手也是完全OK的. –掌握自动化开发项目的程序设计思路及架构解藕原则. 监控系统需求讨论 1.可