📢?聲明:
🍄 大家好,我是風箏
🌍 作者主頁:【古時的風箏CSDN主頁】。
?? 本文目的為個人學習記錄及知識分享。如果有什么不正確、不嚴謹的地方請及時指正,不勝感激。
直達博主:「古時的風箏」 。(搜索或點擊掃碼)
————————————————
假設系統中有一個接口,這個接口已經被10個實現類實現了,突然有一天,新的需求來了,其中5個實現類需要實現同一個方法。然后你就在接口中添加了這個方法的定義,想著一切都很完美。
當你在接口和其中5個實現類中加完這個方法后,一編譯。不妙啊,另外那 5 個實現類報錯了,沒實現新加的這個方法。要知道,接口中的方法定義必須要在實現類中實現才行,缺一個都編譯不過。
這時候你耳邊突然響起了開發之初的老前輩跟你說的話:“這幾個實現以后可能差距越來越大,接口中可能會加方法,注意留口子”。
現在咋整
假設之前的接口是這樣的,只有吃飯和喝水兩個方法。
public interface IUser {/*** 吃飯啊*/void eat();/*** 喝水啊*/void drink();
}
現在有 5 個實現類厲害了,要加一個 play() 方法。
既然情況已經這樣了,現在應該怎么處理。
破罐子破摔吧,走你
不管什么接口不接口的了,哪個實現類要加,就直接在那個實現類里加吧,接口還保持之前的樣子不動,仍然只有吃飯和喝水兩個方法,play 方法就直接加到 5 個實現類中。
public class UserOne implements IUser{@Overridepublic void eat() {System.out.println("吃飯");}@Overridepublic void drink() {System.out.println("喝水");}public void play() {System.out.println("玩兒");}
}
雖然可以實現,但是完全背離了當初設計接口的初衷,本來是照著五星級酒店蓋的,結果蓋了一層之后,上面的變茅草屋了。
從此以后,接口是接口,實現類是實現類,基本上也就沒什么關系了。靈活性倒是出來了,以后想在哪個實現類加方法就直接加了。
再加一個接口行不
還是有點兒追求吧,我新加一個接口行不行。之前的接口不動,新建一個接口,這個接口除了包含之前的兩個方法外,再把 play 方法加進去。
這樣一來,把需要實現 play 方法的單獨在弄一個接口出來。就像下面這樣 IUser
是之前的接口。IUserExtend
接口是新加的,加入了 play() 方法,需要實現 play() 方法的實現類改成實現新的IUserExtend
接口,只改幾個實現關系,改動不是很大嘛,心滿意足了。
但是好景不長啊,過了幾天,又要加新方法了,假設是上圖的 UserOne
和 UserNine
要增加方法,怎么辦呢?
假如上天再給我一次機會
假如上天再給我一次重來的機會,我會對自己說:“別瞎搞,看看設計模式吧”。
適配器模式
適配器模式可以通過創建一個適配器類,該適配器類實現接口并提供默認實現,然后已有的實現類可以繼承適配器類而不是直接實現接口。這樣,已有的實現類不需要修改,而只需要在需要覆蓋新方法的實現類中實現新方法。
不是要加個 play() 方法嗎,沒問題,直接在接口里加上。
public interface IUser {void eat();void drink();void play();
}
適配器類很重要,它是一個中間適配層,是一個抽象類。之前不是實現類直接 implements 接口類嗎,而現在適配器類 implements 接口類,而實現類 extends 適配器類。
在適配器類可以給每個方法一個默認實現,當然也可以什么都不干。
public abstract class UserAdapter implements IUser {@Overridepublic void eat() {// 默認實現}@Overridepublic void drink() {// 默認實現}@Overridepublic void play() {// 默認實現}
}
public class UserNine extends UserAdapter {@Overridepublic void eat() {System.out.println("吃飯");}@Overridepublic void drink() {System.out.println("喝水");}@Overridepublic void play() {System.out.println("玩兒");}
}public class UserTen extends UserAdapter {@Overridepublic void eat() {System.out.println("吃飯");}@Overridepublic void drink() {System.out.println("喝水");}
}
調用方式:
IUser userNine = new UserNine();
userNine.eat();
userNine.drink();
userNine.play();IUser userTen = new UserTen();
userTen.eat();
userTen.drink();
這樣一來,接口中隨意加方法,然后在在適配器類中添加對應方法的默認實現,最后在需要實現新方法的實現類中加入對應的個性化實現就好了。
策略模式
策略模式允許根據不同的策略來執行不同的行為。在這種情況下,可以將新方法定義為策略接口,然后為每個需要實現新方法的實現類提供不同的策略。
把接口改成抽象類,這里面 eat() 和 drink() 方法不變,可以什么都不做,實現類里想怎么自定義都可以。
而 play() 這個方法是后來加入的,所以我們重點關注 play() 方法,策略模式里的策略就用在 play() 方法上。
public abstract class AbstractUser {IPlayStrategy playStrategy;public void setPlayStrategy(IPlayStrategy playStrategy){this.playStrategy = playStrategy;}public void play(){playStrategy.play();}public void eat() {// 默認實現}public void drink() {// 默認實現}
}
IPlayStrategy
是策略接口,策略模式是針對行為的模式,玩兒是一種行為,當然了,你可以把之后要添加的方法都當做行為來處理。
我們定一個「玩兒」這個行為的策略接口,之后不管你玩兒什么,怎么玩兒,都可以實現這個 IPlayStrategy
接口。
public interface IPlayStrategy {void play();
}
然后現在做兩個實現類,實現兩種玩兒法。
第一個玩兒游戲的實現
public class PlayGameStrategy implements IPlayStrategy{@Overridepublic void play() {System.out.println("玩游戲");}
}
第二個玩兒足球的實現
public class PlayFootballStrategy implements IPlayStrategy{@Overridepublic void play() {System.out.println("玩兒足球");}
}
然后定義 AbstractUser
的子類
public class UserOne extends AbstractUser{@Overridepublic void eat() {//自定義實現}@Overridepublic void drink() {//自定義實現}
}
調用方式:
public static void main(String[] args) {AbstractUser userOne = new UserOne();// 玩兒游戲userOne.setPlayStrategy(new PlayGameStrategy());userOne.play();// 玩兒足球userOne.setPlayStrategy(new PlayFootballStrategy());userOne.play();
}
整體的類關系圖大概是這個樣子:
最后
通過適配器模式和策略模式,我們即可以保證具體的實現類實現共同的接口或繼承共同的基類,同時,又能在新增功能(方法)的時候,盡可能的保證設計的清晰。不像之前那種破罐子破摔的方式,接口和實現類幾乎脫離了關系,每個實現類,各玩兒各的。
您的點贊、收藏、評論都是我前進路上的動力
推薦閱讀
? 劍走偏鋒,無頭瀏覽器是什么神奇的家伙
? 新項目決定用 JDK 17了
? 5000字,10張圖,完全掌握 MySQL 事務隔離級別