我理解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;
目前基础部分没有任何共享功能或属性。共享函数将会出现,因为在原型链搜索中,由于被实例创建覆盖了“共享”属性,我看不到添加“共享”属性的意义(如果这是错误的,请让我知道)。
这种方法可行吗?还是应该忽略继承?可能会有一些类型检查,如果您可以推断类型为一种类型(即它们必须全部属于类型QueryStrategy),那么会更容易,但我不希望我的.Net偏见在这里占上风。
另一种方法
首先,我不是在这里销售书籍,只是在阅读我拥有的书籍时学到很多东西,但也想给予他人应有的赞誉。
根据几条评论,可以说类型推断在这里并不会产生任何区别,可能应该把它留给鸭子类型进行检查,而试图插入未在语言中定义的内容可能不是最好的方法。我在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);
}
};
这可能是一个更清晰的方法,有一件事我不确定是否应该为原型添加类型,但我想在这种简单情况下不需要,但在更复杂的情况下,您可能需要抽象出许多策略,并且在一个文件中需要进行抽象。
这是一个更可接受的方法吗?
我最终得到的结果
我四处查找,尝试更多地了解鸭子类型、优缺点等,并根据一些评论和答案简化了我的设计。我坚持使用策略式方法,每个策略定义相同的函数,基本上封装了不同实现之间变化的内容,并为它们提供了一个共同的合同(缺乏更好的术语)。
发生改变的一件事是基类/接口被删除了,因为它不起任何积极作用,每个策略都是独立的(所以不完全符合模式的经典表示),它们只是定义了相同的create函数。
从嵌套的if和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();
};
每个策略都独立测试,我觉得这样更容易维护,因为如果出现问题,它只会在一个地方失败,并且根据单元测试进行调查将更加容易。显然存在一些权衡,有更多的文件和稍微复杂的结构...我们会看到会发生什么。