一、單一職責原則
不要存在多于一個導致類變更的原因,簡單來說,就是一個Class/Interface/Method只負責一項職責,
這句話最為重要的就是這一段:一個Class/Interface/Method只負責一項職責,
我們先來舉一個例子,我們在日常生活中都或多或少的聽過LOL(英雄聯盟)這個游戲,而這個游戲在各個直播平臺都很火爆,那我們就以此為例:
某個游戲直播平臺會將主播直播時的視頻錄制下來,等到主播下播后再上傳到平臺,這樣就形成了錄播,對于這兩種視頻觀看模式,平臺有著這樣的規定:觀看直播不允許快進、回放,而錄播則可以,那我們首先想到的應該是這樣方式:
/**
* 平臺
*/
public class UuSee {
private final String LiveName = "直播";
private final String ReplayName = "錄播";
// 觀看
public void watch(String name){
if (LiveName.equals(name)){
System.out.println("觀看LOL "+name+",不能快進、回放!");
} else if(ReplayName.equals(name)) {
System.out.println("觀看LOL "+name+",可以快進、回放!");
}
}
}
我們來寫一個測驗的代碼看看:
public static void main(String[] args) {
UuSee uuSee = new UuSee();
uuSee.watch("直播");
uuSee.watch("錄播");
}
從測驗的代碼來看的話,
UuSee類承擔了兩種不同的處理邏輯,那么現在來增加一個需求:對直播和錄播的視頻進行加密,而直播和錄播視頻的加密方式不同,那么我們必須要修改原始碼,而這可能影響其他地方的呼叫,所以現在我們來將兩種觀看方式分開,讓它們互不干擾:
直播(LiveVideo類):
/**
* 直播
*/
public class LiveVideo {
public void watch(String name){
System.out.println("直播視頻加密......");
System.out.println("觀看LOL "+name+",不能快進....");
}
}
錄播(RePlayVideo類):
/**
* 錄播
*/
public class RePlayVideo {
public void watch(String name){
System.out.println("錄播視頻加密......");
System.out.println("觀看LOL "+name+",可以快進.....");
}
}
呼叫代碼:
public static void main(String[] args) {
RePlayVideo rePlayVideo = new RePlayVideo();
rePlayVideo.watch("錄播");
LiveVideo liveVideo = new LiveVideo();
liveVideo.watch("直播");
}
這樣看的話,直播類
LiveVideo和錄播類RePlayVideo都呼叫自己的處理邏輯,兩者互不干擾,那么如果業務繼續發展:增加VIP用戶,并且只有VIP用戶才能觀看錄播(獲得視頻流);而普通用戶雖然不能觀看錄播,但可以獲取錄播視頻的基本資訊,這樣就會增加兩個方法:getReplayVideoInfo()和getReplayVideo(),這時候發現錄播類RePlayVideo和直播類LiveVideo都會擁有其中的兩個方法,那不如設計一個頂級介面,將他們一起納入管理:UuSeeInterface
public interface UuSeeInterface {
// 獲得 錄播視頻資訊
public String getReplayVideoInfo();
// 獲得 錄播視頻 視頻流
public byte[] getReplayVideo();
// 觀看視頻
public void watch(String name);
}
寫完這個介面,我們發現從控制視頻播放權限的層面來看的話,可以分為兩個職責:管理職責和展示職責;
getReplayVideoInfo()和getReplayVideo()方法都屬于展示職責,watch()方法屬于管理職責,這樣的話我們就可以將上面的介面拆分一下:
管理職責介面(UuSeeManagement)
/**
* 管理職責
*/
public interface UuSeeManagement {
// 觀看視頻
public void watch(String name);
}
展示職責介面(UuSeeInfo)
/**
* 展示職責
*/
public interface UuSeeInfo {
// 獲得 錄播視頻資訊
public String getReplayVideoInfo();
// 獲得 錄播視頻 視頻流
public byte[] getReplayVideo();
}
說完了一個Class/Interface只負責一項職責的事情后,我們再來看一看一個Method只負責一項職責的問題,
假如有這么一個方法:更改一個用戶的用戶名和地址,我們可能會偷懶寫成這樣,
// 修改用戶名稱和地址
public void modifyUserInfo(String userName,String address){
System.out.println("用戶名改為:"+userName);
System.out.println("用戶地址改為:"+address);
}
那么當我們又增加一個需求:只更改用戶名稱,不更改用戶地址,那么我們在呼叫
modifyUserInfo()方法時,還要去千方百計的獲得用戶的地址,非常的麻煩,倒不如將上面的方法拆分為兩個方法:
// 修改用戶名稱
public void modifyUserName(String userName){
System.out.println("用戶名改為:"+userName);
}
// 修改用戶地址
public void modifyUserAddress(String address){
System.out.println("用戶地址改為:"+address);
}
這樣來看,我們需要修改用戶名稱的時候就呼叫
modifyUserName()方法,需要修改用戶地址的時候就呼叫modifyUserAddress()方法,兩個方法獨立分開,不相互干擾,后期也好維護,當然,在日常作業中,我們不可能面面俱到,也不要為了某種原則而將自己陷入某個陷阱中,還是要根據實際情況做出不同的選擇,但是,撰寫代碼時,要盡量的滿足單一原則,這樣也方便我們后期的代碼維護,
二、依賴倒置原則
這個原則是開閉原則的基礎,是指設計結構代碼時,高層模塊不應該依賴于底層模塊,二者應該依賴于抽象,抽象不應該依賴于細節,細節應該依賴于抽象,即:針對介面編程,依賴于抽象而不依賴于具體,
我們來先看一段代碼:
/**
* 動物園
*/
public class Zoom {
// 老虎
public void tiger(){
System.out.println("老虎在吃雞肉.....");
}
// 猴子
public void monkey(){
System.out.println("猴子在吃香蕉.....");
}
}
// 測驗
public static void main(String[] args) {
Zoom zoom = new Zoom();
zoom.tiger();
zoom.monkey();
}
小明周末去動物園游玩,正值晌午,動物飼養員在給動物喂食,覺得有趣就想記錄一下,在記錄完tiger和monkey之后,小明又發現了熊貓(panda)在吃竹子,于是興沖沖的準備記錄下來,可動筆時卻發現一個問題:
tiger()、monkey()方法已經寫好了,呼叫沒有任何問題,但在增加一個panda()方法的話,不就修改了Zoom類的源代碼,這樣會不會有額外的風險呢?似乎也不符合設計模式中的開閉原則,思考良久,小明將上述代碼改成了下面這樣:
/**
* 動物園
*/
public interface Zoom {
// 吃午飯
public void eat();
}
// 老虎
public class Tiger implements Zoom {
@Override
public void eat() {
System.out.println("老虎在吃雞肉.....");
}
}
// 猴子
public class Monkey implements Zoom{
@Override
public void eat() {
System.out.println("猴子在吃香蕉......");
}
}
// 呼叫類
public class Xming {
public void eat(Zoom zoom){
zoom.eat();
}
}
// 測驗
public static void main(String[] args) {
Xming xming = new Xming();
xming.eat(new Tiger()); // 老虎
xming.eat(new Monkey()); // 猴子
}
這樣一看,
Tiger和Monkey互不干擾,當需要記錄熊貓吃竹子時,只需要在創建一個Panda類就可以了,不用去修改現有的源代碼:
// 熊貓
public class Panda implements Zoom {
@Override
public void eat() {
System.out.println("熊貓正在吃竹子......");
}
}
// 測驗
public static void main(String[] args) {
Xming xming = new Xming();
xming.eat(new Tiger()); // 老虎
xming.eat(new Monkey()); // 猴子
xming.eat(new Panda());// 熊貓
}
這個時候我們發現
Xming類呼叫eat()方法時注入方式很熟悉,想了想是依賴注入,而依賴注入的方式還有構造器注入和setter注入,我們先來看看構造器注入的方式:
// 呼叫類
public class Xming {
private Zoom zoom;
public Xming(Zoom zoom) {
this.zoom = zoom;
}
public void eat(){
zoom.eat();
}
}
呼叫代碼:
public static void main(String[] args) {
Xming tiger = new Xming(new Tiger());
tiger.eat();
Xming panda = new Xming(new Panda());
panda.eat();
}
構造器注入的方式在每次呼叫時都要創建實體,那么,如果
Xming是全域單例的話,我們就只能選擇setter注入的方式了:
// 呼叫類
public class Xming {
private Zoom zoom;
public void setZoom(Zoom zoom) {
this.zoom = zoom;
}
public void eat(){
zoom.eat();
}
}
// 呼叫
public static void main(String[] args) {
Xming tiger = new Xming();
tiger.setZoom(new Tiger());
tiger.eat();
Xming panda = new Xming();
panda.setZoom(new Panda());
panda.eat();
}
結語:依賴倒置原則的本質還是面向介面編程,而事實就是以抽象為基準比以細節為基準搭建起來的框架要穩定的多,后期維護與查看都相對的容易與清晰一些,所以,我們在日常的開發中,要根據實際的業務需求來分析,盡量的使用面向介面編程,先頂層再細節的步驟來設計代碼架構,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/285546.html
標籤:Java
上一篇:Golang開源定時任務調度框架robfig/cron優化
下一篇:mybatis筆記(記錄)
