前書き
ボブおじさんは、オブジェクト指向プログラミングをより適切に実装するために S.O.L.I.D の 5 つの原則を提案し、継承しました。 5 つの原則は次のとおりです。
単一責任原則 (単一責任 SRP)
オープン/クローズド。原則 (OCP)
リスコフ置換原則 (LSP)
インターフェース分離原則 (ISP)
依存性反転原則 (DIP)
5 つの原則はブログ コミュニティで議論されてきたと思います。特に C# の実装については、プロトタイプベースの動的型言語である JavaScript と比べるとまだ少ないため、このシリーズでは JavaScript プログラミング言語に基づいた 5 つの原則の適用について説明します。 OK、最初の記事「単一責任」を始めましょう。
英語原文: http://freshbrewedcode.com/derekgreer/2011/12/08/solid-javascript-single-responsibility-principle/
単一責任
単一の説明責任は次のとおりです:
クラスを変更する理由は 1 つだけである必要があります
クラスを変更する理由は 1 つだけである必要があります
コードをコピー
クラス (JavaScript ではオブジェクトである必要があります)関連する動作は何を意味しますか?の近いセットが必要です。単一の責任に従うことの利点は、オブジェクトに多くの責任がカプセル化されている場合、1 つの責任を変更する必要があると、必然的にオブジェクトの他の責任のコードに影響を与えることになります。切り離すことで、責任ある各従業員がより柔軟に変更できるようになります。
しかし、オブジェクトの複数の動作が複数の責任を構成するのか、それとも 1 つの責任を構成するのかをどのようにして知るのでしょうか?書籍『オブジェクト デザイン: 役割、責任、およびコラボレーション』で提案されている役割ステレオタイプの概念を参照して決定できます。この概念では、責任を区別するために次の役割ステレオタイプが提案されています。
情報保持者 – このオブジェクトは保存するように設計されています。オブジェクトを作成し、オブジェクト情報を他のオブジェクトに提供します。
Structurer - このオブジェクトは、オブジェクトと情報の間の関係を維持するように設計されています。
Service Provider - このオブジェクトは、作業を処理し、他のオブジェクトにサービスを提供するように設計されています。
Controller - このオブジェクトは、一連のオブジェクトを制御するように設計されています。責任ある決定 タスク処理
コーディネーター - このオブジェクトは意思決定の処理作業を行わず、単に他のオブジェクトに作業を委任するだけです
インターフェイス - このオブジェクトは、システムのさまざまな部分で情報 (またはリクエスト) を変換するように設計されています
理解すると、これらの概念を使用すると、コードに複数の責任があるのか、それとも単一の責任があるのかを簡単に知ることができます。
コード例
このコード例は、ショッピング カートに商品を追加する方法を示しています。コードは次のとおりです。
function Product(id, description) {
this.getId = function () {
return id;
};
this.getDescription = function () {
return description;
function Cart(eventAggregator) {
var items = [ ];
this.addItem = function (item) {
items.push(item)
}
(function () {
var products = [新製品 (1, "スター・ウォーズ レゴ船"),
新製品 (2, "バービー人形"),
新製品 (3, "リモコン飛行機")],
カート = new Cart() ;
function addToCart() {
var productId = $(this).attr('id');
var product = $.grep(products, function ( x) {
return x.getId() == productId;
cart.addItem(product) = $('
').html(product.getDescription()).attr('id-cart', product.getId()).appendTo("#cart"); >products.forEach(function (product) {
var newItem = $('').html(product.getDescription())
.attr('id', product .getId())
.dblclick(addToCart)
.appendTo("#products")
})();
それぞれ 2 つの関数を宣言します。製品とカートの説明に使用されます。匿名関数の役割は、画面を更新してユーザーと対話することです。これはそれほど複雑な例ではありませんが、匿名関数には無関係な多くの役割が含まれています。見てみましょう。責任の数:
まず、製品コレクションの宣言があります。
次に、製品コレクションを #product 要素にバインドするコードがあり、イベント ハンドラーも追加します。ショッピングカートに追加するための
3 番目に、Cart ショッピングカートを表示する機能があります
4 番目に、商品アイテムをショッピングカートに追加して表示する機能があります
コードのリファクタリング
コードを独自のオブジェクトに格納できるように分解してみましょう。このために、Martinfowler のイベント アグリゲーター (Event Aggregator) 理論を参照して、オブジェクト間で通信するコードを処理します。
まず、イベント集約関数を実装しましょう。この関数は 2 つの部分に分かれており、1 つはハンドラーのコールバック コードに使用され、もう 1 つはサブスクライブとパブリッシュに使用される EventAggregator です。イベント。コードは次のとおりです:
コードをコピーします
コードは次のとおりです:
function Event(name) {
var handlers = [];
this.getName = function () {
名前を返します。
};
this.addHandler = function (ハンドラー) {
handlers.push(ハンドラー);
};
this.removeHandler = function (ハンドラー) {
for (var i = 0; i < handlers.length; i ) {
if (handlers[i] == ハンドラー) {
handlers.splice(i, 1);
休憩;
}
}
};
this.fire = function (eventArgs) {
handlers.forEach(function (h) {
h(eventArgs);
});
};
}
function EventAggregator() {
var events = [];
function getEvent(eventName) {
return $.grep(events, function (event) {
return event.getName() === イベント名;
})[0];
}
this.publish = function (eventName,eventArgs) {
var events = getEvent(eventName);
if (!event) {
event = new Event(eventName);
events.push(イベント);
}
event.fire(eventArgs);
};
this.subscribe = function (eventName, handler) {
var events = getEvent(eventName);
if (!event) {
event = new Event(eventName);
events.push(イベント);
}
event.addHandler(ハンドラー);
};
}
その後、我们来註製品对象、代码如下:
function Product(id, description) {
this.getId = function () {
return id;
};
this.getDescription = function () {
説明を返します;
};
}
次に、Cart オブジェクト、このオブジェクトの addItem の関数を参照して、イベント itemAdded を配布し、その後 item をパラメータとして削除します。 >
复制代码
代码如下: function Cart(eventAggregator) {
var items = [];
this.addItem = function (item) {
items.push(item);
eventAggregator.publish("itemAdded", item);
};
}
CartController は主にカート オブジェクトとイベント コマータを受け取り、阅itemAdded によって要素ポイントを追加し、productSelected イベントによって製品を追加します。 >
复制代码
eventAggregator.subscribe("productSelected", function (eventArgs) {
cart.addItem(eventArgs.product);
});
}
リポジトリの目的は、データを取得し (ajax から取得できる)、その後 getdata を公開する方法です。复制代码
代码如下:
function ProductRepository() {
var products = [new Product(1, "Star Wars Lego Ship"),
ProductController では、onProductSelect メソッドの 1 つが定義されており、主に配布触発productSelected イベントです。forEach は主にデータを商品リストに固定するために使用されます。 >
复制代码
代码如下:
function ProductController(eventAggregator, productRepository) {
var products = productRepository.getProducts();
});
}
products.forEach(function (product) {
var newItem = $('
').html(product.getDescription())
.attr('id', product.getId())
.dblclick(onProductSelected)
.appendTo("#products")
});
}
最後の安全関数:
复制代码
代码如下:
(function () {
var eventsAggregator = new EventAggregator(),
cart = new Cart(eventAggregator),
cartController = new CartController(cart,eventAggregator),
productRepository = new ProductRepository(),
productController = new ProductController(eventAggregator, productRepository)
})();
匿名関数のコードが削減されていることがわかります。オブジェクトのインスタンス化コード。このコードでは、情報を受け取ってアクションに渡すコントローラーの概念を導入しました。また、主にオブジェクトの表示を処理するために使用されるリポジトリーの概念も導入しました。リファクタリングの結果、オブジェクト宣言をたくさん書くことになりますが、利点は、表示データを表示する必要があり、コレクションを処理するために処理コレクションを変更する必要があるため、それぞれのオブジェクトが明確な責任を持つことです。結合度は非常に低いです。
最終コード
関数イベント(名前) {
var handlers = [];
this.getName = function () {
return name;
this.addHandler = function (ハンドラー) {
handlers.push(handler);
};
this.removeHandler = function (ハンドラー) {
for (var i = 0; i < handlers.length ; i ) {
if (handlers[i] == ハンドラー) {
break;
}
}; >
this.fire = function (eventArgs) {
handlers.forEach(function (h) {
h(eventArgs);
}); >
function EventAggregator() {
var events = [];
function getEvent(eventName) {
return $.grep(events, function (event) {
return event .getName( ) === イベント名;
})[0];
this.publish = function (eventName,eventArgs) {
var イベント = getEvent(eventName);
if (!event) {
event = new Event(eventName);
}
event.fire(eventArgs); };
this.subscribe = function (eventName, handler) {
var イベント = getEvent(eventName);
if (!event) {
event = new Event(イベント名);
events.push(イベント);
event.addHandler(ハンドラー);
}
関数説明) {
this.getId = function () {
return id
};
return description; }
function Cart(eventAggregator) {
var items = [];
this.addItem = function (item) {
items.push(item); eventAggregator.publish ("itemAdded", item);
};
}
function CartController(cart,eventAggregator) {
eventAggregator.subscribe("itemAdded", function (eventArgs) {
var newItem = $('
').html(eventArgs.getDescription()).attr('id-cart',eventArgs.getId()).appendTo("#cart ");
});
eventAggregator.subscribe("productSelected", function (eventArgs) {
cart.addItem(eventArgs.product);
});
}
function ProductRepository() {
var products = [新製品(1, "スター・ウォーズ レゴ船"),
新製品(2, "バービー人形"),
新製品(3, "リモコン飛行機")];
this.getProducts = function () {
商品を返品する;
}
}
function ProductController(eventAggregator, productRepository) {
var products = productRepository.getProducts();
function onProductSelected() {
var productId = $(this).attr('id'); $.grep (products, function (x) {
return x.getId() == productId;
})[0];
eventAggregator.publish("productSelected", {
product: product
});
}
products.forEach(function (product) {
var newItem = $('').html(product .getDescription( ))
.attr('id', product.getId())
.dblclick(onProductSelected)
.appendTo("#products")
; }
(function () {
var eventsAggregator = new EventAggregator(),
cart = new Cart(eventAggregator),
cartController = new CartController(cart,eventAggregator),
productRepository = new ProductRepository(),
productController = new ProductController(eventAggregator, productRepository)
})();
概要
このリファクタリングの結果を見て、一部のブロガーは次のように思うかもしれません。本当にそんなに複雑にする必要があるのですか?と私は尋ねました。私が言えるのは、これを行うべきかどうかはプロジェクトの状況次第だということだけです。
プロジェクトが非常に小規模でコードの量がそれほど多くない場合、実際にはそれほど複雑にリファクタリングする必要はありませんが、プロジェクトが非常に複雑な大規模プロジェクトや小規模なプロジェクトの場合は、プロジェクトが将来的に急速に成長する可能性がある場合は、将来のメンテナンスを容易にするために、初期段階で責任を分離するための SRP 原則を考慮する必要があります。