我正在用 Node.js 编写一些代码,这些代码让我觉得使用策略模式结构更好。来自 .Net 我将创建其余部分所基于的接口并从那里移动,在 JavaScript 中这不是那么明确。
我理解作为一种原型语言 JavaScript 没有接口继承的概念,所以我不确定我所做的是否是一种气味,因为我似乎找不到参考,除了一篇试图推断接口的博客文章通过使用一个基抽象类来强制继承类实现该函数(当它抛出时)。
我的基类
QueryStrategy = function () {
};
QueryStrategy.prototype.create = function(){
throw new Error("Not Implemented");
}
module.exports = QueryStrategy;
实施1
var util = require('util');
var QueryStrategy = require('./queryStrategy');
SelectQueryStrategy = function (query) {
this.parameters = query.parameters || [];
this.entity = query.entity || '';
};
util.inherits(SelectQueryStrategy, QueryStrategy);
SelectQueryStrategy.prototype.create = function () {
var self = this,
params = self.parameters,
paramList = self.parameters.length >= 1 ? '' : '*';
for (var i = 0; i < params.length; i++) {
var suffix = i === (params.length - 1) ? '' : ', ';
paramList = paramList + params[i].key + suffix;
}
return util.format("SELECT %s FROM %s", paramList, self.entity);
};
module.exports = SelectQueryStrategy;
目前,该基础没有任何共享功能或属性。共享功能将会出现,属性,因为原型链搜索我没有看到添加“共享”属性的意义,因为它们被创建的实例覆盖(如果这是错误的,请告诉我)。
这是一种可接受的方法,还是在这种情况下我应该忽略继承。可能会有一些类型检查,如果您可以将类型推断为一个(即它们必须都是 Type QueryStrategy),那么它会更容易,但我不希望我的 .Net 偏见在这里接管。
替代方法
首先,我不是想在这里卖书,我只是用我拥有的书阅读了很多关于这个主题的书,但想在应得的地方给予赞扬。
根据这几条评论,可以正确地说类型推断在这里不会有任何区别,可能应该留给 Duck Typing 来检查,并且可能试图插入一些未在语言不是最好的方法。我已经阅读了关于接口的 Addy Osmani Javascript 模式,虽然我的实现不一样,虽然接口使用是可以接受的,但可能不需要。
保留策略方法可能更简单,但使用不同的实现,在 Stoyan Stefanov 的 Javascript 模式中概述的一种实现,您有一个实现,并且基于某种形式的配置,您知道要使用哪种策略。
伪样本
QueryBuilder = function () {
this.types = [];
}
QueryBuilder.prototype.create = function (type, query) {
var strategy = types[type];
strategy.create(query);
};
QueryBuilder.types.Select = {
create: function (query) {
params = query.parameters,
paramList = query.parameters.length >= 1 ? '' : '*';
for (var i = 0; i < params.length; i++) {
var suffix = i === (params.length - 1) ? '' : ', ';
paramList = paramList + params[i].key + suffix;
}
return util.format("SELECT %s FROM %s", paramList, query.entity);
}
};
这可能是一种更简洁的方法,我要确定的一件事是我是否应该向原型添加类型,但我想在这种简单的情况下不需要它,但在更复杂的情况下,你可以有一个许多策略和一个文件可能需要抽象出来。
这是一种更容易接受的方法吗?
我最终得到了什么
我环顾四周,试图了解更多关于 Duck 打字、优点和缺点等的信息,并尝试根据收到的一些评论和回答来简化我的设计。我坚持采用类似策略的方法,因为每个策略都定义了相同的功能,本质上封装了实现中的不同之处,并为它们提供了一个通用的合同,以便获得更好的条款。
改变的一件事是基类/接口,正如正确指出的那样,它没有做任何建设性的事情被删除了,每个策略都是独立的(因此在模式的经典表示中不是 100%)它们,如前所述,它们只是定义相同的创建函数.
从嵌套的 if 和 switch 曾经所属的地方开始,已被单个 switch 替换,该 switch 根据请求的查询类型决定使用哪种查询策略。稍后可以将其重新分解为工厂,但现在它达到了它的目的:
Query.prototype.generate = function () {
var self = this,
strategy = undefined;
switch (this.queryType) {
case "SELECT":
strategy = new Select(self);
break;
case "INSERT":
strategy = new Insert(self);
break;
case "UPDATE":
strategy = new Update(self);
break;
case "DELETE":
strategy = new Delete(self);
break;
}
return strategy.create();
};
每个策略都经过独立测试,我觉得这更容易维护,就好像某事失败了,它会在一个地方失败,并且基于单元测试的调查会更容易。显然有一个权衡,更多的文件,和一个稍微复杂的结构......我们将看看会发生什么。