65.9K
CodeProject 正在变化。 阅读更多。
Home

JavaScript 中的反向装饰器,或责任链

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2018 年 12 月 1 日

CPOL

3分钟阅读

viewsIcon

7490

在 JavaScript 中实现责任链模式

引言

这是我对先前在其他两篇文章中涵盖主题的延续

  1. VB.NET WinForms 中的装饰器模式
  2. VB.NET WinForms 中的反向装饰器

本文中的代码可在 GitHub 上找到 这里

实现了我称之为“反向装饰器”的东西。它实际上只是对责任链模式的一种变体。这是一种模块化编程风格,我相信它遵循 SOLID 原则。我已经在几个项目中使用了它,所有项目都在我的组织内部。我想听听其他人的看法。我将坚持使用一个简单的待办事项应用程序,以便将重点放在模式上。我将假设你已经阅读并理解了我的其他两篇文章,然后这只是 JavaScript 中的一个实现。

背景

我一直在研究装饰器模式一段时间了。我用 VB.NET 实现了它,因为那是我在工作中使用的语言。我写了我的第一篇 CodeProject 文章,其中包含装饰器模式和解释。接下来我写了反向装饰器文章,也包含解释。我想用 JavaScript 实现这个模式,因为该语言提供了一些优势。函数不必写在一个类中,dataObj 具有更大的灵活性,并且我们不需要实现接口。我不会重复我在其他两篇文章中涵盖的内容,主要是因为我只想发布这个示例。

起点

我们从一个 todo.html 文件开始。在 HTML 中,我创建了我们的待办事项应用程序的基本 UI。它引用了 Bootstrap CSS。没什么太令人兴奋的。我们将从两个 JavaScript 文件开始,以帮助我们组织项目。“todo.js” 文件是我们将在其中组合操作并运行它的地方。在“todoAdd.js” 中,我们将定义所有函数。我发现将函数与组合和运行分开进行维护更容易。

函数优先

我们将从函数文件“todoAdd.js” 开始。基本函数将具有此格式

function functionName(anyPramsHere, next){
    return function(dataObj){
        
        //function logic here
        
        if(next){
            next(dataObj);
        }
    };
}

这种格式将允许我们将函数组合在一起。使用 JavaScript 而不是 VB.NET 的一个好处是,我们不必声明一个类。我们可以只使用一个可以返回另一个函数的函数。“next” 是下一个函数,dataObj 是将状态从一个函数传递到下一个函数的类。

对于这个简单的应用程序,我将使用五个函数。它们是

  1. 添加错误处理程序
  2. 从表单获取数据
  3. 将数据添加到表格
  4. 按到期日对表格进行排序
  5. 向用户显示任何错误

这些函数都不是特别突破性的,所以我们将完成它们,如下所示

//get the data from form
function getDataFromForm(next){
    return function(dataObj){
        if(!dataObj.hasError){
            dataObj.dtAddDueDate = document.getElementById("dtAddDueDate").value;
            dataObj.txtAddTask = document.getElementById("txtAddTask").value;
        }
        
        if(next){
            next(dataObj);
        }
    };
}

//write it to the table
function addToTable(next){
    return function(dataObj){
        if(!dataObj.hasError){
            var newRow = document.createElement("tr");
            var ckCol = document.createElement("td");
            var ck = document.createElement("input");
            ck.type = "checkbox";
            ckCol.appendChild(ck)
            newRow.appendChild(ckCol);

            var dtCol = document.createElement("td");
            dtCol.innerText = dataObj.dtAddDueDate;
            newRow.appendChild(dtCol);

            var taskCol = document.createElement("td");
            taskCol.innerText = dataObj.txtAddTask;
            newRow.appendChild(taskCol);

            var tblBody = document.getElementById("tblBody");
            tblBody.appendChild(newRow);
        }
        
        if(next){
            next(dataObj);
        }
    };
}

//sort the table on Due Date
function sortTableOnDueDate(next){
    return function(dataObj){

        var rows = document.getElementById("tblBody").rows;
        var numSorted;
        do {
            numSorted = 0;
            for (i = 0; i < rows.length; i++) {

                var currEl = rows[i];
                var nextEl = currEl.nextElementSibling;
                if ((nextEl) && 
                   (new Date(currEl.cells[1].innerText) > new Date(nextEl.cells[1].innerText))) {

                    document.getElementById("tblBody").insertBefore(nextEl, currEl);
                    numSorted++;
                }
            }

        } while (numSorted > 0);

        if(next){
            next(dataObj);
        }
    };
}

//add error handler
function addErrHandler(next) {
    return function (dataObj) {
        dataObj.err = {
            hasError: false,
            message: ""
        }
        try {
            if (next) {
                next(dataObj);
            }
        } catch (err) {
            dataObj.err.hasError = true;
            dataObj.err.message = err.message;
            next(dataObj);
        }
    };
}

//show error to user
function showErrorToUser(next){
   
    return function(dataObj){
        if(dataObj.err.hasError){
            alert("ERROR: " + dataObj.err.message);
        }
        if(next){
            next(dataObj);
        }
    };
}

组合函数

todo.js 文件中,我们将把函数放在一起。我们反向组合它们,以便它们运行时会向前运行。我们这样做

var addToDo = function(){

    // we compose here:
    var runMe = null;
    runMe = showErrorToUser(runMe);
    runMe = sortTableOnDueDate(runMe);
    runMe = addToTable(runMe);
    runMe = getDataFromForm(runMe);
    runMe = addErrHandler(runMe);

    dataObj = {};
    // run here:
    runMe(dataObj);
};

反向组合并向前运行允许我们使用 async/await 模式。如果你想知道为什么,请参阅我的 VB.NET 文章关于 反向装饰器

在组合的末尾,我们创建 dataObj。它将是我们从一个函数到另一个函数传递状态时持久化状态的地方。

运行它,它就可以工作了!

历史

  • 2018 年 12 月 1 日:初始版本
  • 2018 年 12 月 3 日:文章已更新
© . All rights reserved.