在实际项目开发中的六层划分
????? 在实际项目的开发过程中,常常为了减弱类之间的耦合性,同时便于项目的维护和复用,往往要分层进行编写代码。自己在实际的项目中发现,一般的项目分的很细的话,会分这样的五层:
????? VIEW层:表面展现层,该层是直接面向用户的,即展现层。
????? Control层:控制层,控制跳转的
????? VO层:值对象,有人也叫模型层,主要是和数据库中表相对应的,字段可少可多。
????? QO层:查询对象,当涉及到页面的查询比较多的时候可以将每个查询条件封装起来,作为一层。
????? BO层:业务层,处理具体的业务。
????? DAO层:数据访问层,该层直接和数据库打交道,用于数据库的增、删、改、查。
????? 有句话描述的这个项目层次的关系很生动、也很形象。当然表面层我用struts来代替的,语言描述如下:
????? 数据流向描述:
????? FORM,QO和VO只是用来运输数据,FORM就像是大卡车,QO就是放查询条件的包裹,VO是放业务数据的包裹,FORM拉着这两个包裹在VIEW和ACTION中间跑。在VIEW中把查询条件包到QO中,FORM拉上这个QO,把它给了ACTIION,ACTION将QO给BO,BO用这个QO通过DAO向数据库取数,取到的数包到VO中,由BO再给ACTION,FROM再从ACTION中把这个VO包拉给跳转过去的VIEW,VIEW把VO包中数据显示出来。
???? 下面关于一个简单六层的测试例子来分析详细分析:
????? 该例子是从页面输入查询条件,点击提交,最后在另一页面显示查询结果的例子:
将一个小小的工程分成六层来完成这个查询:
1、view的查询页面:
?? 查询页面
<%@ page contentType="text/html;charset=GBK"%><%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%><%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html"%><%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%><html> <head> <title>WELCOME</title> </head> <body> <html:form action="/testAction.do"> <table> <tr> <td>First Name:</td> <td><html:text name="testForm" property="firstName" /></td> </tr> <tr> <td>Last Name:</td> <td><html:text name="testForm" property="lastName" /></td> </tr> <tr> <td><html:submit value="submit" /></td> </tr> </table> </html:form> </body></html>
结果显示页面:
<%@ page contentType="text/html;charset=GBK"%><%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%><%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html"%><%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%><html><head><title>TEST</title></head><body><html:form action="/testAction.do"><table border="1"><tr><td>First Name:</td><td><bean:write name="testForm" property="firstName" /></td></tr><tr><td>Last Name:</td><td><bean:write name="testForm" property="lastName" /></td></tr><!-- 用标签显示那两个VO --><!-- 先判断一下form里的VO List 是否是空,要不是空再显示 --><logic:notEmpty name="testForm" property="testVOList"><tr><td>company</td><td>department</td></tr><!-- 遍历VO List --><logic:iterate name="testForm" property="testVOList" id="testVO"><tr><td><bean:write name="testVO" property="company" /></td><td><bean:write name="testVO" property="department" /></td></tr></logic:iterate></logic:notEmpty><tr></table></html:form></body></html>
?
?2、action中的两个类(本例子用了struts)
public class TestAction extends Action {public ActionForward execute(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) {// 取出页面传回来的formTestForm testForm = (TestForm) form;// 从form中取查询对象QOTestQO testQO = testForm.getTestQO();// 用BO处理业务TestBO testBO = new TestBO();List testVOList = testBO.doBusiness(testQO);// 将处理后的VO List放回form中,用于页面显示testForm.setTestVOList(testVOList);// 跳转页面ActionForward forward = mapping.findForward("success");return forward;}}
?
public class TestForm extends ActionForm {private String firstName;private String lastName;private TestQO testQO; // 这个是你的QO,要有get和set方法private TestVO testVO; // 这个是你的VO,要有get和set方法// 一般来说,返回到页面的值对象VO不是一个,因为从数据库查出的是多条记录// 这就要把它分放到一个List中private List testVOList;public void reset(ActionMapping mapping ,HttpServletRequest request){super.reset(mapping, request);testQO = new TestQO();testVO = new TestVO();}public TestVO getTestVO() {return testVO;}public void setTestVO(TestVO testVO) {this.testVO = testVO;}public TestQO getTestQO() {return testQO;}public void setTestQO(TestQO testQO) {this.testQO = testQO;}public String getFirstName() {return firstName;}public void setFirstName(String firstName) {this.firstName = firstName;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName = lastName;}public List getTestVOList() {return testVOList;}public void setTestVOList(List testVOList) {this.testVOList = testVOList;}}
?3、QO层
/** * @author:gaojie * @作用:查询对象,该对象封装了页面的查询条件,可以封装好多的条件在里面, * @ 本例中只封了两个查询字段firstName和lastName。 */public class TestQO {// 每个查询条件对应这里的一个字段,记得写相应的get和set方法private String firstName;// 第一个查询条件private String lastName;// 第二个查询条件/** * 获取第一查询条件 * @return */public String getFirstName() {return firstName;}/** * 设置第一查询条件 * @param firstName */public void setFirstName(String firstName) {this.firstName = firstName;}/** * 获取第二查询条件 * @return */public String getLastName() {return lastName;}/** * 设置第二查询条件 * @param lastName */public void setLastName(String lastName) {this.lastName = lastName;}}
?4、vo层
/** * @author gaojie * @作用:值对象,与数据库中的表是基本对应的,其包含的结构字段应小于等于数据库中的表。 * */public class TestVO {private String firstName; //第一个名字private String lastName; //最后的名字private String department; //部门字段private String company; //公司名称/** * 获取第一个名字 * @return */public String getFirstName() {return firstName;}/** * 设置第一个名字 * @param firstName */public void setFirstName(String firstName) {this.firstName = firstName;}/** * 获取最后的名字 * @return */public String getLastName() {return lastName;}/** * 设置最后的名字 * @param lastName */public void setLastName(String lastName) {this.lastName = lastName;}/** * 返回部门名称 * @return */public String getDepartment() {return department;}/** * 设置部门名称 * @param department */public void setDepartment(String department) {this.department = department;}/** * 返回公司名称 * @return */public String getCompany() {return company;}/** * 设置公司名称 * @param company */public void setCompany(String company) {this.company = company;}}
?5、BO层
/** * * @功能:该方法调用DAO的方法,,处理相关的业务。 * @创建人 gao_jie * @创建日期 Jun 14, 2009 * @版本 1.0 * */public class TestBO {public List doBusiness(TestQO testQO) {// 好的方法应该用工厂模式和单例模式来生成DAO,这里采用的是单利模式ItestDAO Itest = null;try {Itest = TestDAO.getInstance();} catch (Exception e) {e.printStackTrace();}// 用DAO从数据库中取到数据或写入数据// 这个例子只是取数据了,返回那个放了两个VO的ListList conditonglist = Itest.getConditionList(testQO);// 按相应的业务逻辑处理这些数据,也可以把处理后的数据放在另一个容器中返回// 返回处理后的数据,用来在页面中显示等。// 这里没作处理,只是简单返回了查询得到的数据return conditonglist;}}
?6DAO层
接口
/** * * @功能 数据库操作的接口 * @创建人 gao_jie * @创建日期 Jun 14, 2009 * @版本 1.0 * */public interface ItestDAO {/** * 获取查询的列表 * @param testQO * @return */public List getConditionList(TestQO testQO); }
?实现层
/** * @作者:gaojie * @作用:该类是DAO层,用来连接数据库,并执行查询、更新、插入、删除等操作数据库的操作, 如数据库中的SQL等。该类一般来说只是和数据库进行数据交互,不处理具体的业务,处理业务 * 部分放到BO层中。该类在设计过程中常用的模式是工厂模式或单例模式,这里为了方便不做设计 * 模式的定义。在该类的实际项目中会根据查询对象TestQO获取的值,拼装SQL语言,然后从数据 * 库中获取数据存放到值对象中。 */public class TestDAO implements ItestDAO{private static ItestDAO dataSourceImpl = null;/** * 单利模式 * @return * @throws Exception */public static ItestDAO getInstance() throws Exception {if (dataSourceImpl == null)dataSourceImpl = new TestDAO();return (TestDAO) dataSourceImpl;}/** * *该方法的通过QO查询条件动态生成SQL语句,连接数据库进行查询数据 * *本例子为了方便采用了XML文件做为数据库,所以在内容上简化了组装SQL的步骤 * * @param testQO * @return */public List getConditionList(TestQO testQO) {// 声明一个List来存放你从数据库里查出来的数据List<TestVO> conditionList = Collections.synchronizedList(new ArrayList<TestVO>());// 这里连接数据库,执行这条SQL,然后将返回值放到上面声明的xxxList中// 数据库中的一条记录就对应一个VO,xxxList中放的就是多个VO(就是多条数据库中的记录)try {Document document = loadXML(getDatasource());Element root = document.getRootElement();TestVO testVo = null;for (Object element : root.elements("persons")) {testVo = getTestVOFromElement((Element) element);if (testVo.getFirstName().equals(testQO.getFirstName())&& testVo.getLastName().equals(testQO.getLastName())) {conditionList.add(testVo);}}} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (DocumentException e) {// TODO Auto-generated catch blocke.printStackTrace();}return conditionList;}/** * 获取文件路径 * * @return */private String getWebPath() {String path = "";URL url = TestDAO.class.getClassLoader().getResource("");path = url.toString().substring(5, (url.toString().indexOf("WEB-INF")));return path;}/** * 加载XML文件 * * @param xmlfile * @return * @throws FileNotFoundException * @throws DocumentException * @throws org.dom4j.DocumentException */@SuppressWarnings("unused")private Document loadXML(String xmlfile) throws FileNotFoundException,DocumentException, org.dom4j.DocumentException {SAXReader reader = new SAXReader();Document doc = reader.read(new FileInputStream(xmlfile));return doc;}/** * 向XML中写入 * * @param doc * @param filePath * @param encoding */private static void writeToFile(Document doc, String xmlfile) {XMLWriter writer = null;try {OutputFormat format = OutputFormat.createPrettyPrint();format.setEncoding("UTF-8");writer = new XMLWriter(new FileOutputStream(xmlfile), format);writer.write(doc);} catch (Exception e) {e.printStackTrace();} finally {if (writer != null) {try {writer.flush();writer.close();} catch (IOException e) {e.printStackTrace();}}}}/** * * 获取TestVo对象 * * @param element * @return */@SuppressWarnings("unused")private TestVO getTestVOFromElement(Element element) {TestVO testVo = new TestVO();testVo.setFirstName(getAttributeValue(element, "firstName"));testVo.setLastName(getAttributeValue(element, "lastName"));testVo.setDepartment(getAttributeValue(element, "department"));testVo.setCompany(getAttributeValue(element, "company"));return testVo;}/** * 获取属性元素的值 * * @param element * @param attribute * @return */private String getAttributeValue(Element element, String attribute) {return element.attributeValue(attribute);}/** * 获取数据源的操作 * @return */@SuppressWarnings("unused")private String getDatasource() {return getWebPath() + "testdatasource.xml";}}
struts的配置文件如下:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://struts.apache.org/dtds/struts-config_1_2.dtd"><struts-config><data-sources /><form-beans><form-bean name="testForm" type="test.action.TestForm" /></form-beans><global-exceptions /><global-forwards /><action-mappings><action name="testForm" path="/testAction" scope="request"type="test.action.TestAction"><forward name="success" path="/test.jsp" /></action></action-mappings><message-resources parameter="test.ApplicationResources" /></struts-config>
?
总结:这个简单的例子通过一个查询页面提交,到后台经过层层的处理,最终把数据查询结果展现在用户面前,该例子的结构体现了在项目开发中的详细分层。本例子的数据库采用的是xml文件。
下面一些词语解释:
一、PO:persistant object 持久对象,可以看成是与数据库中的表相映射的java对象。最简单的PO就是对应数据库中某个表中的一条记录,多个记录可以用PO的集合。PO中应该不包含任何对数据库的操作。
二、VO:value object值对象。通常用于业务层之间的数据传递,和PO一样也是仅仅包含数据而已。但应是抽象出的业务对象,可以和表对应,也可以不,这根据业务的需要.个人觉得同DTO(数据传输对象),在web上传递。
三、DAO:data access object 数据访问对象,此对象用于访问数据库。通常和PO结合使用,DAO中包含了各种数据库的操作方法。通过它的方法,结合PO对数据库进行相关的操作。
四、BO:business object 业务对象,封装业务逻辑的java对象,通过调用DAO方法,结合PO,VO进行业务操作。
五、POJO:plain ordinary java object 简单无规则java对象,我个人觉得它和其他不是一个层面上的东西,VO和PO应该都属于它。