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

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

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

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

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

我们需要实现一个类似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="site from scratch" name="keywords" />    
    	<input type="submit" value="search!" />
    </form>

    <table border="1" bordercolor="grey" >
    	<tr>
    		<td>result 1</td><td>something..................</td>
    	</tr>
    	<tr>
    		<td>result 1</td><td>something..................</td>
    	</tr>
    	<tr>
    		<td>result 1</td><td>something..................</td>
    	</tr>
    	<tr>
    		<td>result 1</td><td>something..................</td>
    	</tr>
    </table>

  </body>
</html>

通过浏览器访问 /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="<%=keywords %>" name="keywords" />    
    	<input type="submit" value="search!" />
    </form>

    <%
    if(!results.isEmpty()) {
    %>
    <table border="1" bordercolor="grey" >
    	<%
    	for(int i = 0; i < results.size(); i++) {
    		Result result = (Result)results.get(i);
    	%>
    	<tr>
    		<td><%=result.title %></td><td><%=result.content %></td>
    	</tr>
    	<%
    	}
    	%>
    </table>
    <%
    }
    %>

  </body>
</html>

这里,我们构造了一批伪数据(同时创建了一个类),并通过对应的执行逻辑,得到了完全一致的展示效果。

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="<%=keywords %>" name="keywords" />    
    	<input type="submit" value="search!" />
    </form>

    <%
    if(!results.isEmpty()) {
    %>
    <table border="1" bordercolor="grey" >
    	<%
    	for(int i = 0; i < results.size(); i++) {
    		Result result = (Result)results.get(i);
    	%>
    	<tr>
    		<td><%=result.title %></td><td><%=result.content %></td>
    	</tr>
    	<%
    	}
    	%>
    </table>
    <%
    }
    %>

  </body>
</html>

这里,我们构造了一个 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="<%=keywords %>" name="keywords" />    
    	<input type="submit" value="search!" />
    </form>

    <%
    if(!results.isEmpty()) {
    %>
    <table border="1" bordercolor="grey" >
    	<%
    	for(int i = 0; i < results.size(); i++) {
    		Result result = (Result)results.get(i);
    	%>
    	<tr>
    		<td><%=result.title %></td><td><%=result.content %></td>
    	</tr>
    	<%
    	}
    	%>
    </table>
    <%
    }
    %>

  </body>
</html>

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="<%=keywords %>" name="keywords" />    
    	<input type="submit" value="search!" />
    </form>

    <%
    if(!results.isEmpty()) {
    %>
    <table border="1" bordercolor="grey" >
    	<%
    	for(int i = 0; i < results.size(); i++) {
    		Result result = (Result)results.get(i);
    	%>
    	<tr>
    		<td><%=result.title %></td><td><%=result.content %></td>
    	</tr>
    	<%
    	}
    	%>
    </table>
    <%
    }
    %>

  </body>
</html>

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

时间: 2024-09-03 04:30:27

开发,从需求出发 · 之一 从视觉开始的相关文章

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

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

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

<西游降魔>里面的<儿歌三百首>里面有首儿歌叫做<春天在哪里> 歌词是这样的: 春天在哪里 春天在哪里 春天就在小朋友的眼睛里 通过俺的渣英语翻译之后是这样的: where spring is where spring is the fucking spring is in javatar's eyes yo yo check it out 我相信,java程序员已经意识到我说的春天是什么了:) 不过spring跟我们现在说的东东有关系么?暂时还没有-_-b 言归正传,

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

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

开发,从需求出发 &#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.可