第10章 轻量级三层软件架构实例

10.1 设计需求

给定如下XML文档:

<?xml version="1.0" encoding="utf-8"?>
<books>
    <book>
        <ISBN>8475484848</ISBN>
        <title>XML技术</title>
        <price>80</price>
    </book>
    <book>
        <ISBN>35235348</ISBN>
        <title>Python programming</title>
        <price>65</price>
    </book>
    <book>
        <ISBN>67676348</ISBN>
        <title>Java development</title>
        <price>76</price>
    </book>
    <book>
        <ISBN>27974348</ISBN>
        <title>NodeJS development</title>
        <price>90</price>
    </book>
</books>

提出如下设计需求:

  1. 设计合适的NodeJS后台服务程序,该程序可读取后台XML文档数据并返回前端。
  2. 设计合适的NodeJS后台服务程序,该程序可读取后台XML文档数据且格式化为Table形式字符串并返回前端。
  3. 以上应用均基于三层软件架构。

10.2 设计分析

10.2.1 功能分析

测试用户期望功能如下:

  1. 后台读取XML文档文本并返回前端可视。
  2. 后台格式化(表格化)XML文档文本并返回前端可视。
用户测试功能用例建模
图10.1 用户测试功能用例建模

10.2.2 核心业务数据流分析

测试核心业务描述如下:

  1. 后台服务程序读取XML文档文本数据并发送到前端。
  2. 或后台服务程序读取XML文档文本,经格式化处理(标记替换)后再返回到前端并格式化输出。
核心业务数据流
图10.2 核心业务数据流

10.2.3 文件包及文件设计分析

基于三层架构视角的文件包及文件设计如表10.1所述。

表10.1 文件包及文件设计
序号 包设计 文件设计
1 设计表示层包(包名:UI),抽象表示层文件容器。 (1) 设计界面主页文件main.js。
(2) 主页引入jquery框架(jquery.js),辅助表示层处理。
2 设计业务逻辑层包(包名:server),抽象业务层文件容器。 (1) 设计通用业务逻辑服务文件server.js。
(2) 设计当前模块专用业务逻辑服务文件server_10.js。
3 设计数据层包(包名:data),抽象数据层文件容器。 (1) 设计XML数据存储文件books.xml。
文件包设计及文件关系
图10.3 文件包设计及文件关系

10.3 文件架构

基于以上分析,本案例文件架构逻辑如图10.4所示。

考虑到要让server.js通用逻辑服务多个项目,所以将server.js放置在UI、data、server文件夹外围。

文件架构
图10.4 文件架构

文件功能说明见表10.2。

表10.2 文件功能说明
序号 文件(夹)名 作用
1 books.xml 存放在后台用于测试的XML基础数据文件
2 main.html 实现人机交互的用户界面主页
3 Server.js NodeJS后台通用逻辑服务程序,动态引入Server_10.js模块。
4 Server_10.js 当前模块服务逻辑
5 UI 存放表示层文件的文件夹
6 server 存放业务逻辑层文件的文件夹
7 data 存放XML数据文件
8 10 存放文件夹:UI、data、server。

10.4 代码实现

10.4.1 主页实现

对应文件:main.html

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <h3>NodeJS读取XML测试</h3>
    <hr />
    <input type="button" value="查看原始XML文档" onclick="view_XML()" />
    <input type="button" value="查看格式化XML文档" onclick="view_formatXML()" />
    <hr />
    <div id="result"></div>
</body>
</html>
<script type="text/javascript" src="jquery-1.11.1.js"></script>
<script type="text/javascript" >
    //查看原始
    function view_XML() {
        var xhttp = new XMLHttpRequest();
        xhttp.open("post","http://localhost:1007/readXML",true);
        xhttp.onreadystatechange = function () {
            if (xhttp.status == 200 && xhttp.readyState == 4) {
                alert(xhttp.responseText);
            }
        }
        xhttp.send();
    }
    
    //查看格式化XML文档
    function view_formatXML() {
        var xhttp = new XMLHttpRequest();
        xhttp.open("post", "http://localhost:1007/readXMLToTable", true);
        xhttp.onreadystatechange = function () {
            if (xhttp.status == 200 && xhttp.readyState == 4) {
                $("#result").html(xhttp.responseText);
            }
        }
        xhttp.send();
    }
</script>

10.4.2 后台主服务程序实现

对应文件:server.js

// JavaScript source code
//---------------请将这个文件保存为Unicode格式,否则麻烦大大的--------------
const http_obj = require("http");//用于产生服务对象
const fs_obj = require("fs");//读写后台文件
const url_tran_obj = require("url");//解释URL

const server = http_obj.createServer(function (request, response) {
    //请求路径处理
    var req_str = decodeURI(request.url);
    var req_head;
    var POS = req_str.indexOf("?");
    if (POS == -1) { req_head = req_str; }
    else { req_head = req_str.substring(0, POS); }
    
    //设置查询对象
    var url_obj = url_tran_obj.parse(req_str, true);
    var Q_obj = url_obj.query;
    
    //响应头设置
    response.setHeader("Content-Type", "text/plain;charset=utf-8");
    response.setHeader("Access-Control-Allow-Origin", "*");//实现跨域访问
    response.writeHead(200);
    
    //%%%%%%%%%%%%%-----------begin your code
    require("./10/server/server_10.js")(request, response, req_head, Q_obj, fs_obj);
    //%%%%%%%%%%%%%------------end your code
});

server.listen(1007);
console.log("Server is running at port 1007...");

10.4.3 后台子服务程序实现

服务端子程序server_10.js代码如下:

function server_10(request, response, req_head, Q_obj, fs_obj) {
    if (req_head == "/readXML_10") {
        //代码1
        fs_obj.readFile("./10/data/books.xml", "utf-8", function (err, data) {
            if (err) { response.end(err.toString()); }
            else { response.end(data); }
        });
    }
    
    if (req_head == "/readXMLToTable_10") {
        //代码2
        fs_obj.readFile("./10/data/books.xml", "utf-8", function (err, data) {
            if (err) { response.end(err.toString()); }
            else {
                var temp = data.replace("<books>", "<table border='1'><tr><th>书号</th><th>书名</th><th>价格</th></tr>");
                temp = temp.replace("</books>", "</table>");
                temp = temp.replace(/<book>/g, "<tr>");
                temp = temp.replace(/<\ /book>/g, "</tr>");
                temp = temp.replace(/(<ISBN>|<title>|<price>)/g, "<td>");
                temp = temp.replace(/(<\ /ISBN>|<\ /title>|<\ /price>)/g, "</td>");
                response.end(temp);
            }
        });
    }
}

module.exports = server_10

10.4.4 实现效果

查看原始XML文档的实现效果如图10.5所示。

查看原始XML文档
图10.5 查看原始XML文档

查看格式化XML文档(表格化)的实现效果如图10.6所示。

查看格式化XML文档
图10.6 查看格式化XML文档

10.5 问题思考

问题提出:

基于nodeJS服务的轻量级三层软件架构开发的技术要点是什么?

问题思考:

  1. 建立3个相对独立文件夹(如:UI、server、data)。
  2. 将只关注数据可视化的文件放在UI文件夹;将只关注后台数据二次加工的业务逻辑文件放在server文件夹;将只关注原始存储的文件放在data文件夹。
  3. 基于三层架构下的独立功能模块的设计、实现、调试(UI所呈现的功能模块与server业务逻辑功能模块形成对应关系)。
  4. 基于三层架构下的集成功能调试及发布。

10.6 仿真实训

给定如下XML文档:

<?xml version="1.0" encoding="utf-8"?>
<students>
    <student category="new_student">
        <s_id>8475484848</s_id>
        <s_name>Max</s_name>
        <age>18</age>
    </student>
    <student>
        <s_id>35235348</s_id>
        <s_name>Jack</s_name>
        <age>20</age>
    </student>
    <student>
        <s_id>67676348</s_id>
        <s_name>Json</s_name>
        <age>21</age>
    </student>
    <student>
        <s_id>27974348</s_id>
        <s_name>Rose</s_name>
        <age>19</age>
    </student>
</students>

提出如下设计需求:

  1. 设计合适的NodeJS后台服务程序,该程序可读取后台XML文档数据并返回前端。
  2. 设计合适的NodeJS后台服务程序,该程序可读取后台XML文档数据且格式化为Table形式字符串并返回前端。
  3. 以上应用均基于三层软件架构。
  4. 仿真本章实例功能完成以上设计。

10.7 关于三层软件架构

三层软件架构因其低耦合性、可重用、标准化定义及良好的可伸缩性及可维护性而广泛应用于软件开发行业。在当今软件开发领域,面向对象分析方法是最典型、最常用的方法,面向对象分析方法认为系统是自包含的对象集合[1]。因此,在面向对象软件开发中,需要解决的核心问题是定义系统对象;然而,类是产生对象的通用模版。很显然,在面向对象软件开发中,需要解决的首要核心问题是定义系统中的类。在系统分析阶段,类可划分为实体类、控制类(用于描述一个或多个用例行为建模方式)、接口类[2]。用例是一系列行为活动的描述[3],因此,控制类是直接关系到软件功能实现的类,在软件分析和设计过程中,控制类的设计是一个非常重要的阶段。

三层架构是一种客户端-服务端架构,在三层架构中,用户界面、功能处理逻辑、计算机数据存储和存取是作为相对独立的模块进行开发和维护的[4]。在这一架构中,表示层、应用处理层和数据管理层在逻辑上是相对独立的并运行在不同的处理器上[5]。用户接口又叫作表示层[6];业务逻辑层是实现特定功能的核心层;数据访问层是从数据库获取数据并向业务逻辑层提供数据的类模块[7]。在三层架构中,客户端一般不直接访问数据库,但借助于位于中间层的COM/DCOM可建立与后台的连接;也就是说,客户端与数据库服务器的交互是通过叫业务逻辑层的中间层来实现的[8]。

三层架构中数据存取之所以有良好的安全保障,很大程度上是由于实现了层的分离[9]。图10.7描述了三层架构的逻辑结构。

三层架构逻辑结构
图10.7 三层架构逻辑结构

图10.4表明:表示层(Presentation layer)可直接与业务逻辑层(Business layer)通信;业务逻辑层可以直接与数据访问层(Data access layer)通信。事实上,层与层之间的通信是通过对象交互实现的。