私はNode.jsでいくつかのコードを書いていますが、それは戦略パターンを使用してより適切に構造化されていると私に飛びつきました。.Netから来て、残りの部分が基づいているインターフェースを作成し、そこから移動します。JavaScriptでは、これはそれほど明確ではありません。
プロトタイプ言語としてJavaScriptにはインターフェースの継承の概念がないことを理解しているので、インターフェースを推測しようとする1つのブログ投稿を除いて、参照を見つけることができないように見えるので、私が行ったことが匂いかどうかはわかりません。継承クラスに関数を(スローするときに)実装するように強制する基本抽象クラスを使用する。
私の基本クラス
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;
現在、ベースには共有機能やプロパティはありません。共有関数、プロパティが表示されます。典型的なチェーン検索のため、作成されたインスタンスによって上書きされるため、「共有」プロパティを追加する意味がわかりませんでした(これが間違っている場合はお知らせください)。
これは許容できるアプローチですか、それともこの場合の継承を無視する必要がありますか。いくつかの型チェックがあるかもしれません、そしてあなたが型を1つに推測することができればそれはより簡単になるでしょう(すなわちそれらはすべて型QueryStrategyのものでなければなりません)が、私は私の.Net偏見がここに引き継がれることを望んでいません。
代替アプローチ
第一に、私はここで本を売ろうとはしていません。私は自分が持っている本で主題の周りをたくさん読んでいますが、正当な理由がある場合は信用を与えたいと思っています。
いくつかのコメントに基づいて、型推論はここでは実際には何の違いももたらさず、おそらくダックタイピングに任せてチェックする必要があり、おそらくで定義されていないものをスロットに入れようとしていると言うのは正しいです言語は最善のアプローチではありません。私は、インターフェースに関するAddy Osmani Javascriptパターンを読みましたが、私の実装は同じではなく、インターフェースの使用法は許容できますが、おそらく必要ありません。
戦略アプローチを維持する方が簡単かもしれませんが、実装が異なります。StoyanStefanovによるJavascriptパターンで概説されています。ここでは、1つの実装があり、何らかの形式の構成に基づいて、使用する戦略がわかります。
疑似サンプル
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);
}
};
これはよりクリーンな方法かもしれません。プロトタイプに型を追加する必要があるかどうかは確かですが、この単純なケースでは必要ないと思いますが、より複雑なシナリオでは、多くの戦略と1つのファイルを抽象化する必要があるかもしれません。
これはより受け入れられるアプローチですか?
私が最終的に得たもの
私は周りを見回して、ダックタイピング、長所と短所などについてもっと理解しようとし、受け取ったコメントと回答のいくつかに基づいてデザインを単純化しようとしました。私は、すべての戦略が同じ機能を定義し、実装全体で異なるものを本質的にカプセル化し、より良い用語を求めて共通の契約を与えるという戦略的なアプローチに固執しました。
変更されたものの1つは、基本クラス/インターフェイスでした。これは、建設的なことを何もしていないため、削除されました。各戦略は独立しています(したがって、パターンの従来の表現では100%ではありません)。前述のように、同じ作成関数を定義するだけです。 。
ネストされたifとスイッチがかつて属していた場所から、要求されたクエリタイプのタイプに基づいて使用するクエリ戦略を決定する単一のスイッチに置き換えられました。これは後でファクトリにリファクタリングすることができますが、今のところ、その目的を果たします。
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();
};
それぞれの戦略は独立してテストされており、何かが失敗したかのように維持しやすく、1つの場所で失敗し、単体テストに基づいて調査が容易になると思います。明らかに、トレードオフ、より多くのファイル、および少し複雑な構造があります...何が起こるかを見ていきます。