[英]Better way than mediator pattern for decoupling widgets in this situation?
我正在嘗試找出在特定情況下應遵循的模式。 我有一個網絡應用程序,該應用程序由幾個互相影響的主要小部件組成。 小部件遵循模塊模式。
讓代碼說話:
MyApp.Widgets.MainLeftBox = (function(){
var self = {};
self.doSomething = function(){
var certainState = MyApp.Widgets.MainRightBox.getCertainState();
if (certainState === 1){
console.log(‘this action happens now’);
}
else {
console.log(‘this action can’t happen because of a certain state in My.App.Widgets.MainRightBox’);
}
}
return self;
})();
如您所見,我在這里緊密耦合。 眾所周知,緊密耦合是邪惡的。 (除非找到了唯一的一個!;-)
我知道可以通過遵循pub-sub / custom事件模式來實現很多解耦。 但這更適合於A開始某些事情而B可以做出反應的情況。 但是我遇到這樣的情況,A會獨立啟動某件事,但是需要檢查B的某個狀態才能繼續。
在追求可維護性的同時,我正在尋找擺脫困境的出路。
我首先想到的是調解人模式。
但仍然,我的代碼如下所示:
MyApp.Widgets.MainLeftBox = (function(mediator){
var self = {};
self.doSomething = function(){
var certainState = mediator.getCertainState();
if (certainState === 1){
console.log(‘this action happens now’);
}
else {
console.log(‘this action can’t happen because of a certain state in mediator’);
}
}
return self;
})(MyApp.Mediator);
這樣會好一點,因為窗口小部件不直接進行通信,而是通過中介間接進行通信。
但是,我仍然覺得我做錯了,必須有一種更好的方法來實現小部件之間的解耦。
編輯
到目前為止,讓我總結一下!
總的來說,我喜歡MVC分離視圖的方法! 但是,請將該示例視為更復雜的模塊。 從視覺上講,這些並不一定就是“盒子”。 用這種方式描述會更容易。
另一個給定的事實應該是,A 獨立啟動一個動作,然后需要檢查某種狀態。 它不能訂閱B的狀態更改並提供操作或不提供操作。 它必須像A一樣獨立啟動,然后需要檢查某個狀態。 將此視為B需要進行的一些復雜操作。
因此,我想到了自定義事件/回調/調解器方法的混合體,對此我確實很喜歡。
1.)一個模塊不知道任何其他模塊
2.)模塊也不知道調解員
3.)依賴於某個外部狀態的模塊只知道它依賴於某個外部狀態-而不是更多
4.)模塊真的不在乎誰會提供此特定狀態
5.)模塊可以確定是否已提供某些狀態
6.)請求管道是直的。 換句話說,模塊是此操作的啟動器。 它不僅訂閱狀態更改事件(記住A開始操作,然后需要B(或某個地方)的狀態
我在這里發布了一些示例代碼,並提供了一個jsFiddle: http : //jsfiddle.net/YnFqm/
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
</head>
<body>
<div id="widgetA"></div>
<div id="widgetB"></div>
<script type="text/javascript">
var MyApp = {};
(function (MyApp){
MyApp.WidgetA = function WidgetA(){
var self = {}, inner = {}, $self = $(self);
//init stuff
inner.$widget = $('#widgetA');
inner.$button = $('<button>Click Me!</button>')
.appendTo(inner.$widget)
.click(function(){self.doAction();});
self.doAction = function(){
//Needs state from WidgetB to proceed
/* Tight coupling
if (MyApp.WidgetB.getState() == 'State A'){
alert('all fine!');
}
else{
alert("can't proceed because of State in Widget B");
}
*/
var state;
$self.trigger('StateNeeded',function(s){state = s});
if (state == 'State A'){
alert('all fine!');
}
else{
alert("can't proceed because of State in Widget B");
}
};
return self;
};
MyApp.WidgetB = function WidgetB(){
var self = {}, inner = {};
//init stuff
inner.$widget = $('#widgetB');
inner.$button = $('<button>State A</button>')
.appendTo(inner.$widget)
.click(function(){
var buttonState = inner.$button.text();
if (buttonState == 'State A'){
inner.$button.text('State B');
}
else{
inner.$button.text('State A');
}
});
self.getState= function(){
return inner.$button.text();
};
return self;
};
MyApp.Mediator = (function(){
var self = {}, widgetA, widgetB;
widgetA = new MyApp.WidgetA();
widgetB = new MyApp.WidgetB();
$(widgetA).bind('StateNeeded', function(event, callback){
//Mediator askes Widget for state
var state = widgetB.getState();
callback(state);
});
return self;
})();
})(MyApp);
</script>
</body>
</html>
您應該查看由Addy Osmani Patterns撰寫的有關大型JS應用程序的精彩文章,有關大型JavaScript應用程序體系結構 ,這是代碼示例Essential js設計模式
您仍然可以使用調解器,但可以在其中實現業務邏輯。 所以,與其mediator.getCertainState()
有一個方法mediator.canTakeAction()
該知道的小窗口進行查詢,並確定是否允許該動作。
當然,這仍將由一個調解員結束,該調解員知道要查詢的小部件。 但是,由於我們已經分擔了調解器內部的業務邏輯,因此我認為了解此類事情是可以的。 甚至可能是創建這些小部件的實體。 或者,您可以使用某種注冊機制,在其中告訴中介者創建它們時哪個小部件用作哪個角色。
編輯:根據給定的代碼示例的精神提供一個示例。
MyApp.DeleteCommand=(function(itemsListBox, readOnlyCheckBox) {
var self = {};
self.canExecute = function() {
return (not readOnlyCheckBox.checked()) && (itemsListBox.itemCount() > 0);
}
return self;
})(MyApp.Widgets.ItemsList, MyApp.Widgets.ReadOnly);
您可以進一步執行以下兩個步驟:
假設我了解“框”的本質是在頁面上可見的框,則框應呈現一個表示您的應用程序狀態或其一部分的視圖-底層狀態本身應由與表示UI中該狀態的視圖分開的對象。
因此,例如,方框圖可能會呈現一個人的視圖,而當人在睡覺時,該框將是黑色的,而當人醒着時,該框將變為白色。 如果您的另一個方框負責顯示此人在吃什么,那么您可能希望該方框僅在該人醒着時才起作用。 (好的例子很難,我剛剛醒來。很抱歉。)
這里的要點是,您不希望視圖互相詢問-您希望它們關心基礎對象(在本例中為Person)的狀態。 如果兩個視圖關心同一個Person,則只需將Person作為參數傳遞給兩個視圖。
很有可能您的需求會稍微復雜一些:)但是,如果您可以根據“有狀態對象”的獨立視圖來考慮問題,而不是需要彼此直接關心的兩個視圖,那么我認為您是會更好。
我仍然建議使用中保-但是,如果你更繼承風扇,你可能要玩的模板方法,國家或戰略,以及裝飾圖案-因為JavaScript 沒有接口,這些可能有用。
后一種方法可能使您可以將過程分類為更易於管理的策略 ,但是,我將繼續介紹Mediator,因為在這種情況下,它對我來說最有意義。
您可以將其實現為EDM(事件驅動的中介)或經典的中介器:
var iEventHub = function iEventHub() {
this.on;
this.fire;
return this;
};
var iMediator = function iMediator() {
this.widgetChanged;
return this;
};
我唯一能真正建議的就是打破您的程序,讓Mediator在此過程中有發言權。 中介看起來可能像這樣:
var Mediator = function Mediator() {
var widgetA = new WidgetA(this)
, widgetB = new WidgetB(this);
function widgetChanged(widget) {
identifyWidget(widget); // magical widget-identifier
if (widgetA.hasStarted) widgetB.isReady();
if (widgetB.isReady) widgetA.proceed("You're proceeding!");
}
return this;
};
var WidgetA = function WidgetA(director) {
function start() {
director.widgetChanged(this);
}
function proceed(message) {
alert(message);
}
this.start = start;
this.proceed = proceed;
return this;
};
var WidgetB = function WidgetB(director) {
function start() {
this.iDidMyThing = true;
director.widgetChanged(this);
}
function isReady() {
return iDidMyThing;
}
this.iDidMyThing = false;
this.start = start;
this.isReady = isReady;
return this;
};
基本上,WidgetA必須獲得Mediator的許可才能繼續進行,因為Mediator將具有狀態的高級視圖。
使用經典介體 ,您可能仍需要調用director.widgetChanged(this)
。 但是,使用EDM的iEventHub
在於您不必與Mediator本身耦合,而是所有模塊都實現iEventHub
接口或與公共hub
耦合。 另外,您可以通過重構widgetChanged
方法來修改經典的Mediator
以幫助進行模塊授權 :
// Mediator
function widgetChanged(ACTION, state) {
var action = actionMap[ACTION || 'NO_ACTION_SPECIFIED'];
action && action.call && action.call(this, state);
}
// WidgetX
const changes = this.toJSON();
director.widgetChanged('SOMETHING_SPECIFIC_HAPPENED', changes);
我認為您非常親密-希望能幫上忙。
為什么不能通過以下方式使用發布-訂閱模型
LeftBox
發出getStateFromRightBox
事件。
RightBox
具有getStateFromRightBox
訂閱者,該訂閱者使用stateData
發出sendStateToLeftBoxAndExecute
事件
LeftBox具有sendStateToLeftBoxAndExecute
訂閱服務器,該訂閱服務器提取stateData
並有條件地執行操作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.