單一職責原則

2022-08-05 10:30:18 字數 4291 閱讀 6201

單一職責原則(srp)的職責被定義為「引起變化的原因」。如果我們有兩個動機去改寫一

個方法,那麼這個方法就具有兩個職責。每個職責都是變化的乙個軸線,如果乙個方法承擔了過

多的職責,那麼在需求的變遷過程中,需要改寫這個方法的可能性就越大。

此時,這個方法通常是乙個不穩定的方法,修改**總是一件危險的事情,特別是當兩個職

責耦合在一起的時候,乙個職責發生變化可能會影響到其他職責的實現,造成意想不到的破壞,

這種耦合性得到的是低內聚和脆弱的設計。

因此,srp原則體現為:乙個物件(方法)只做一件事情。

設計模式中的 srp 原則,srp原則在很多設計模式中都有著廣泛的運用,例如**模式、迭代器模式、單例模式和裝飾者模式。

1. **模式

比如現在實現乙個預載入的例子:大家知道當網速很慢的情況下,把網速調至 5kb/s,可以看到,在被載入好之前,頁面中有一段長長的空白時間。所以在沒有載入到客戶端之前用一張進行站位。 到達預載入的效果。

var myimage=(function () 

return

}})()

myimage.setsrc('');};

這段**預載入函式,有兩個職責。乙個是預載入職責,乙個是載入職責。 myimage 物件除了負責給 img 節點設定 src外,還要負責預載入。我們在處理其中乙個職責時,有可能因為其強耦合性影響另外乙個職責的實現。

且這是違反單一職責原則的。如果乙個物件承擔了多項職責,就意味著這個物件將變得巨大,引起它變化的原因可

能會有多個。物件導向設計鼓勵將行為分布到細粒度的物件之中,如果乙個物件承擔的職責過多,

等於把這些職責耦合到了一起,這種耦合會導致脆弱和低內聚的設計。當變化發生時,設計可能

會遭到意外的破壞。

如果我們只是從網路上獲取一些體積很小的,或者 10年後的網速快到根本不再需

要預載入,我們可能希望把預載入的這段**從 myimage 物件裡刪掉。這時候就不得不改動myimage 物件了。

這時就體現了單一職責好處。通過引入虛擬**的方式分離其職責,myimage 物件只負責設定img節點的src屬性,虛擬**物件負責的預載入職責。給 img 節點設定 src 和預載入這兩個功能,被隔離在兩個物件裡,它們可以各自變化而不影響對方。何況就算有一天我們不再需要預載入,那麼只需要改成請求本體而不是請求**物件即可。

這個預載入的例子。通過增加虛擬**的方式,把預載入的職責放到**物件中,而本體僅僅負責往頁面中新增 img 標籤,這也是它最原始的職責。myimage 負責往頁面

中新增 img 標籤:

var myimage = (function()

}})();

var proxyimage = (function()

return

}})();

proxyimage.setsrc( 'http:// imgcache.qq.com/music/photo/000ggdys0ya0nk.jpg' );~

把新增 img 標籤的功能和預載入的職責分開放到兩個物件中,這兩個物件各自都只有乙個被修改的動機。在它們各自發生改變的時候,也不會影響另外的物件。

2. 迭代器模式

我們有這樣一段**,先遍歷乙個集合,然後往頁面中新增一些 div,這些 div的 innerhtml分別對應集合裡的元素:

for ( var i = 0, l = data.length; i < l; i++ )

};

我們有必要把遍歷 data 的職責提取出來,這正是迭代器模式的意義,迭代器模式提供了一

種方法來訪問聚合物件,而不用暴露這個物件的內部表示。

var each = function( obj, callback ) 

} else

}return obj;

};

在es5中的陣列的迭代方法直接提供的迭代器模式的功能函式foreach;

單例模式:

當在頁面中建立乙個唯一的登陸浮框時,使用單利模式是比較好的方式。一下採用惰性單例實現乙個頁面的登陸浮框。

var createloginlayer =(function() 

return loginlyer;

}})()

上面的單例模式有兩個職責,乙個是管理的單例的職責,乙個是建立登陸浮框的職責。

現在我們把管理單例的職責和建立登入浮窗的職責分別封裝在兩個方法裡,使其迎合單一職責原則。這兩個方法可以

獨立變化而互不影響,當它們連線在一起的時候,就完成了建立唯一登入浮窗的功能,下面的代

碼顯然是更好的做法:

var getsingle = function(fn)  

}var createloginlayer=function()

var createsingleloginlayer = getsingle( createloginlayer );

var loginlayer1 = createsingleloginlayer();

var loginlayer2 = createsingleloginlayer();

alert ( loginlayer1 === loginlayer2 ); // 輸出: true

這是單一職責原則在單例模式中體現。

4. 裝飾者模式

使用裝飾者模式的時候,我們通常讓類或者物件一開始只具有一些基礎的職責,更多的職責

在**執行時被動態裝飾到物件上面。裝飾者模式可以為物件動態增加職責,從另乙個角度來看,

這也是分離職責的一種方式。

當用分離與核心業務邏輯模組**無關的模組時,將其分離。(體現為面向切面程式設計,aop)

當實現乙個資料上報的功能時,資料上報一般乙個登陸浮層為載體。

點選開啟登入浮層

function.prototype.after=function(fn)

}var showlogin = function();

var log = function();

document.getelementbyid( 'button' ).onclick = showlogin.after( log );

// 開啟登入浮層之後上報資料

srp原則的應用難點就是如何去分離職責。

srp原則是所有原則中最簡單也是最難正確運用的原則之一。
要明確的是,並不是所有的職責都應該一一分離。

一方面,如果隨著需求的變化,有兩個職責總是同時變化,那就不必分離他們。比如在 ajax

請求的時候,建立 xhr 物件和傳送 xhr 請求幾乎總是在一起的,那麼建立 xhr 物件的職責和傳送

xhr 請求的職責就沒有必要分開。

另一方面,職責的變化軸線僅當它們確定會發生變化時才具有意義,即使兩個職責已經被耦

合在一起,但它們還沒有發生改變的徵兆,那麼也許沒有必要主動分離它們,在**需要重構的

時候再進行分離也不遲。

在人的常規思維中,總是習慣性地把一組相關的行為放到一起,如何正確地分離職責不是一

件容易的事情。

我們也許從來沒有考慮過如何分離職責,但這並不妨礙我們編寫**完成需求。對於 srp

原則,許多專家委婉地表示「this is sometimes hard to see.」。

一方面,我們受設計原則的指導,另一方面,我們未必要在任何時候都一成不變地遵守原則。

在實際開發中,因為種種原因違反 srp的情況並不少見。比如 jquery的 attr 等方法,就是明顯

違反 srp 原則的做法。jquery 的 attr 是個非常龐大的方法,既負責賦值,又負責取值,這對於

jquery的維護者來說,會帶來一些困難,但對於 jquery的使用者來說,卻簡化了使用者的使用。

在方便性與穩定性之間要有一些取捨。具體是選擇方便性還是穩定性,並沒有標準答案,而

是要取決於具體的應用環境。比如如果乙個電視機內建了 ***機,當電視機壞了的時候,***

機也沒法正常使用,那麼乙個 *** 發燒友通常不會選擇這樣的電視機。但如果我們的客廳本來

就小得誇張,或者更在意 *** 在使用上的方便,那讓電視機和 *** 機耦合在一起就是更好的

選擇。srp 原則的優點是降低了單個類或者物件的複雜度,按照職責把物件分解成更小的粒度,

這有助於**的復用,也有利於進行單元測試。當乙個職責需要變更的時候,不會影響到其他

的職責。

但 srp 原則也有一些缺點,最明顯的是會增加編寫**的複雜度。當我們按照職責把物件

分解成更小的粒度之後,實際上也增大了這些物件之間相互聯絡的難度。

單一職責原則

定義 不要存在多於乙個導致類變更的原因。通俗的說,即乙個類只負責一項職責。問題由來 類t負責兩個不同的職責 職責p1,職責p2。當由於職責p1需求發生改變而需要修改類t時,有可能會導致原本執行正常的職責p2功能發生故障。解決方案 遵循單一職責原則。分別建立兩個類t1 t2,使t1完成職責p1功能,t...

單一職責原則

單一職責原則 乙個類,只有乙個引起它變化的原因。應該只有乙個職責。每乙個職責都是變化的乙個軸線,如果乙個類有乙個以上的職責,這些職責就耦合在了一起。這會導致脆弱的設計。當乙個職責發生變化時,可能會影響其它的職責。另外,多個職責耦合在一起,會影響復用性。例如 要實現邏輯和介面的分離。對於user類,裡...

單一職責原則

問題由來 一心二用,效率降低 類t負責兩個不同的職責 職責p1,職責p2。當由於職責p1需求發生改變而需要修改類t時,有可能會導致原本執行正常的職責p2功能發生故障。解決方案 專注做某件事情 遵循單一職責原則。分別建立兩個類t1 t2,使t1完成職責p1功能,t2完成職責p2功能。這樣,當修改類t1...