軟件開發中,不變的真理就是Change
。java
打開Head First
設計模式,書上的第一個例子就讓我真正明白了咱們一直說的Java
的接口優勢。設計模式
咱們常說:Java
中類不容許多繼承,而採用了更加合理的接口設計。框架
繼承是爲了代碼複用的,而接口不過是聲明方法,那爲何接口能替代多繼承呢?這是我一直以來的疑問。今天,終於找到答案。ide
注:如下代碼均省略get
、set
方法。學習
/** * 動物 */ public class Animal { private String name; // 名稱 public void eat() {} } /** * 鳥 */ public class Bird extends Animal { } /** * 魚 */ public class Fish extends Animal { } /** * 蝙蝠 */ public class Bat extends Animal { }
這是咱們的原設計,一個動物類,裏面有全部動物公有的eat
方法,而後鳥、魚和蝙蝠都繼承自動物,複用了eat
方法,減小了代碼量。this
客戶改需求了,咱們要讓能飛的動物有fly
方法,也就是咱們的Bird
和Bat
類生成的對象有要有fly
這個方法。spa
而後這些飛的行爲都是相同的,咱們須要實現複用,最不負責任的方式就是在Animal
類中添加fly
方法,讓多個類能繼承這個方法,實現複用。設計
/** * 動物 */ public class Animal { private String name; // 名稱 public void eat() { } public void fly() { System.out.println("I can fly"); } }
指正實現方式極其簡單,但會讓之後該代碼的人痛苦萬分。他們會去想,我new
一個魚,怎麼給我提示一個fly
方法?code
如此,每次修改,都是對原有設計的破壞!
/** * 接口:能飛的 */ public interface Volitant { void fly(); } /** * 鳥 */ public class Bird extends Animal implements Volitant { @Override public void fly() { System.out.println("I can fly"); } } /** * 蝙蝠 */ public class Bat extends Animal implements Volitant { @Override public void fly() { System.out.println("I can fly"); } }
創建一個能飛的接口,讓這兩個須要飛的動物實現該接口,實現了須要飛的動物能飛,不須要飛的動物不能飛。對象
書中提出了一個思想:把會變化的部分取出並封裝起來,以便之後能夠輕易地改動或擴充此部分,而不影響不須要變化的其餘部門。
我對這句話的理解就是將常常須要改動的部分抽象出來並封裝,如此,用戶改動只改動相關部分,而對原設計沒有破壞。
可是這樣要怎麼實現代碼複用呢?一樣的代碼,在鳥和蝙蝠中寫了兩次。
/** * 會飛的實現類 */ public class VolitantImpl implements Volitant { @Override public void fly() { System.out.println("I can fly"); } } /** * 鳥 */ public class Bird extends Animal implements Volitant { private Volitant volitant = new VolitantImpl(); @Override public void fly() { this.volitant.fly(); } } /** * 蝙蝠 */ public class Bat extends Animal implements Volitant { private Volitant volitant = new VolitantImpl(); @Override public void fly() { this.volitant.fly(); } }
寫一個類,這個類去實現Volitant
接口,而後在鳥和蝙蝠中引用這個實例化該類對象,調用其fly
方法。如此,實現了代碼複用。固然這個實現方法很是的low
,這裏直接去依賴實現類了,這就很差了。
這是我本身想的解決方法,歡迎批評指正。
/** * 鳥 */ public class Bird extends Animal implements Volitant { @Autowired private Volitant volitant; @Override public void fly() { this.volitant.fly(); } }
寫了一段時間的Spring
項目了,解除依賴的方法最經常使用的就是@Autowired
,而後在實現類中添加@Service
注入。
可是建的是普通的Java
項目,咱們不能被@Autowired
吊死,這是一種思想,一種控制注入類的思想。
/** * 鳥 */ public class Bird extends Animal implements Volitant { private Volitant volitant; public void setVolitant(Volitant volitant) { this.volitant = volitant; } @Override public void fly() { this.volitant.fly(); } } /** * 蝙蝠 */ public class Bat extends Animal implements Volitant { private Volitant volitant; public void setVolitant(Volitant volitant) { this.volitant = volitant; } @Override public void fly() { this.volitant.fly(); } } public class Main { public static void main(String[] args) { Volitant volitant = new VolitantImpl(); Bird bird = new Bird(); bird.setVolitant(volitant); bird.fly(); Bat bat = new Bat(); bat.setVolitant(volitant); bat.fly(); } }
爲兩個須要飛的動物添加set
方法,用於注入實現,在main
方法中實例化的時候將須要的實現注入進去。
其實Spring
的注入也與之相似,只是其更增強大,在程序運行時自動掃描須要注入的地方,而後注入實現。此處,不過是手工注入,咱們學習到這種思想就是最大的收穫。
其實從Java8
開始,能夠在接口中對方法進行默認的實現。
/** * 接口:能飛的 */ public interface Volitant { default void fly() { System.out.println("I can fly"); } } public class Main { public static void main(String[] args) { Bird bird = new Bird(); bird.fly(); Bat bat = new Bat(); bat.fly(); } }
在接口中聲明一個默認的方法,若是沒有實現,則調用默認方法。
這是一句在騰訊雲+社區一篇博客中借來的話。
框架,就是對設計模式的最佳實踐。而咱們學習一個框架,不是去爲了學多麼新多麼新的技術,而是學習其愈來愈好的設計模式。一個框架的流行,不是沒有緣由的。