在本文中,将探讨一种模块化编程风格,即反向装饰器模式,它遵循SOLID原则。已经在组织内部的几个项目中使用了这种模式,并希望得到他人的反馈。为了专注于模式本身,将以一个简单的待办事项应用为例。假设已经阅读并理解了其他两篇文章,本文将仅展示JavaScript中的实现。
一直在探索装饰器模式,并在VB.NET中实现了它,因为这是在工作中使用最多的语言。写了第一篇文章,介绍了装饰器模式,并进行了解释。接下来,写了反向装饰器的文章,同样进行了解释。想在JavaScript中实现这种模式,因为这种语言提供了一些优势。函数不需要在类中编写,对数据对象有更大的灵活性,不需要实现接口。主要想发布这个示例,所以不会重复在其他两篇文章中覆盖的内容。
从todo.html
文件开始。在HTML中,为待办事项应用创建了基本的用户界面。它引用了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"是将状态从一个函数传递到下一个函数的类。
对于这个简单的应用,将使用五个函数。它们是:
这些函数都不是特别惊人,将像这样完成它们:
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);
}
};
}
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);
}
};
}
function sortTableOnDueDate(next) {
return function (dataObj) {
var rows = document.getElementById("tblBody").rows;
var numSorted;
do {
numSorted = 0;
for (var 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);
}
};
}
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);
}
};
}
function showErrorToUser(next) {
return function (dataObj) {
if (dataObj.err.hasError) {
alert("ERROR: " + dataObj.err.message);
}
if (next) {
next(dataObj);
}
};
}
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);
};