首页 > 代码库 > 开发,从需求出发 · 之一 从视觉开始

开发,从需求出发 · 之一 从视觉开始

从需求出发并不是一句空话,在开发过程中也是如此。

从需求出发,实质上是暗合了极限编程和测试驱动开发的一些思想。

鉴于网站开发是一个比较流行的方向,我打算从一个网站开始,阐述一下自己对“需求驱动开发“的理解,并将其引申到一个更广泛的领域。


首先,我们假设一个需求:

我们需要实现一个类似google的网站,用户通过web浏览器访问,在首页输入框中查询,返回搜索的结果。

效果如下图所示:


STEP 0,通过eclipse创建一个web项目:sitefromscratch,文件结构如图所示:

在WebRoot下新增一个jsp文件:

<%@ page pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  </head>
  
  <body>
    <h1>search it!</h1>
    
    <form action="" method="get">
    	<input type="text" value=http://www.mamicode.com/"site from scratch" name="keywords" />    >


通过浏览器访问 /sitefromscratch/search1.jsp 可以得到和目标一致的效果。


STEP 2,通过界面所展示的内容,我们可以大致估计出所需要的数据以及其格式,让我们加入少量的代码实现同样的效果:


<%@ page pageEncoding="UTF-8"%>
<%@page import="java.util.List"%>
<%@page import="java.util.ArrayList"%>
<%!
class Result {
	String title;
	String content;
	public Result(String title, String content) {
		this.title = title;
		this.content = content; 
	}
} 
%>
<%
	String keywords = "site from scratch";
	
	List results = new ArrayList();	
	results.add(new Result("result 1", "something.................."));
	results.add(new Result("result 2", "something.................."));
	results.add(new Result("result 3", "something.................."));
	results.add(new Result("result 4", "something.................."));
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  </head>
  
  <body>
    <h1>search it!</h1>
    
    <form action="" method="get">
    	<input type="text" value=http://www.mamicode.com/"" name="keywords" />    >
这里,我们构造了一批伪数据(同时创建了一个类),并通过对应的执行逻辑,得到了完全一致的展示效果。


STEP 3,现在,该把提交、查询、结果展示的流程走通了:

<%@ page pageEncoding="UTF-8"%>
<%@page import="java.util.List"%>
<%@page import="java.util.ArrayList"%>
<%!
class Result {
	public String title;
	public String content;
	public Result(String title, String content) {
		this.title = title;
		this.content = content; 
	}
} 

public List search(String keywords) {
	List results = new ArrayList();	
	results.add(new Result("result 1", "something.................."));
	results.add(new Result("result 2", "something.................."));
	results.add(new Result("result 3", "something.................."));
	results.add(new Result("result 4", "something.................."));
	
	return results;
}
%>
<%
	String keywords = request.getParameter("keywords");
	if(keywords == null) keywords = "";
	
	List results = search(keywords);	
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  </head>
  
  <body>
    <h1>search it!</h1>
    
    <form action="" method="get">
    	<input type="text" value=http://www.mamicode.com/"" name="keywords" />    >
这里,我们构造了一个 List search(String keywords)方法,将业务逻辑和页面展示分离开来,分别置于在jsp文件中分离的区块。

STEP 4,接着,为了保持页面的简洁,我们把定义的类和方法提取出来,用包组织起来:


package cn.com.sitefromscrath.entity;

public class Result {
	
	public String title;
	public String content;

	public Result(String title, String content) {
		this.title = title;
		this.content = content;
	}
}

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 keywords) {
		
		List results = new ArrayList();
		results.add(new Result("result 1", "something.................."));
		results.add(new Result("result 2", "something.................."));
		results.add(new Result("result 3", "something.................."));
		results.add(new Result("result 4", "something.................."));

		return results;
	}

}

不出意料,SearchService.java 和 Result.java的代码就是直接从search.jsp中copy过去的。

值得一提的是,我们新增了一个BeanFactory类,作为工厂模式的一个实现,它简单的通过指定的ID所对应的类返回产生的实例。

如下所示:


package cn.com.sitefromscrath;

import cn.com.sitefromscrath.service.SearchService;

public class BeanFactory {
	
	public static Object getBean(String id) {
		if("searchService".equals(id)) {
			return new SearchService();
		}
		
		throw new RuntimeException("cannot find the bean with id :" + id);
	}

}

虽然它现在看来显得画蛇添足了一些,但是在我之后的展开论述中,它将占有很重要的位置。


现在,jsp文件的内容看起来简洁多了:

<%@ page pageEncoding="UTF-8"%>
<%@page import="java.util.List"%>
<%@page import="cn.com.sitefromscrath.service.SearchService"%>
<%@page import="cn.com.sitefromscrath.BeanFactory"%>
<%@page import="cn.com.sitefromscrath.entity.Result"%>
<%
	String keywords = request.getParameter("keywords");
	if(keywords == null) keywords = "";
	
	SearchService searchService = (SearchService)BeanFactory.getBean("searchService");	
	List results = searchService.search(keywords);	
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  </head>
  
  <body>
    <h1>search it!</h1>
    
    <form action="" method="get">
    	<input type="text" value=http://www.mamicode.com/"" name="keywords" />    >

STEP 5,听说MVC是个很高科技的东西,我们也来实现一下:


新增一个servlet,作为 Model 层。

package cn.com.sitefromscrath.web;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.com.sitefromscrath.BeanFactory;
import cn.com.sitefromscrath.service.SearchService;

public class SearchServlet extends HttpServlet {

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doPost(request, response);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		String keywords = request.getParameter("keywords");
		if(keywords == null) keywords = "";
		
		SearchService searchService = (SearchService)BeanFactory.getBean("searchService");	
		List results = searchService.search(keywords);	
		
		request.setAttribute("keywords", keywords);
		request.setAttribute("results", results);

		request.getRequestDispatcher("/search5.jsp").forward(request, response);
	}

}


新增一个jsp文件,search5.jsp,作为视图层。

<%@ page pageEncoding="UTF-8"%>
<%@page import="java.util.List"%>
<%@page import="cn.com.sitefromscrath.entity.Result"%>
<%
	String keywords = (String)request.getAttribute("keywords");	
	List results = (List)request.getAttribute("results");	
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  </head>
  
  <body>
    <h1>search it!</h1>
    
    <form action="" method="get">
    	<input type="text" value=http://www.mamicode.com/"" name="keywords" />    >


web.xml中配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
	http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	<servlet>
		<servlet-name>SearchServlet</servlet-name>
		<servlet-class>cn.com.sitefromscrath.web.SearchServlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>SearchServlet</servlet-name>
		<url-pattern>/search</url-pattern>
	</servlet-mapping>
	
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
	
</web-app>


现在,让我们再来run一次,http://localhost:8080/sitefromscratch/search 

binggo,效果跟目标无差!


——————————————————————————————————————————————————————————

嗯,到这里,我们到底达到了什么目的?数据还是假的啊,有这个必要吗?有这个必要吗?

看看,葛大爷已经被拍了一脸血了,我希望您还没到这地步。


到了这一步,我们其实完成了跟前端页面制作人员的握手协议:

您写js的也好,html5的也好,随便整,我返回的数据格式和内容您也看见了,就这样,格式不会变,内容也不会出错。数据怎么嵌,那是您的事儿,俺就不伺候了。


关于”内容不会出错“这句,补充一点儿,这是指的前端页面人员(也许是你自己兼任)无需启动一大堆复杂的应用程序,比如mysql、memcache,就能调试自己的html或者js代码,同时,也避免了其他异常(数据库down了,数据表毁坏了,网络断了,memcache连接不上了等等等等)对前端开发的干扰。


特别是debug阶段,如果你不能确保哪些是正确的,你就无法找到错误的

很多程序员在debug排查某个问题的时候,最常犯的错误就是迷失在一大堆的模块中间,找不到出路,造成这种情况的根本原因就在于:在当事人看来,每个模块都是可疑的,不确定的。要么猜要么一个个查,精力和时间就此白白浪费。


而对于后端开发人员来说,现在面临的就只剩一个任务:让下面的类方法返回真实的业务结果吧。

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 keywords) {
		
		List results = new ArrayList();
		results.add(new Result("result 1", "something.................."));
		results.add(new Result("result 2", "something.................."));
		results.add(new Result("result 3", "something.................."));
		results.add(new Result("result 4", "something.................."));

		return results;
	}

}

我们将在下一章讨论这个问题。

to be continued...

开发,从需求出发 · 之一 从视觉开始