面向對象思想設計原則
在實際的開發中,我們要想更深入的了解面向對象思想,就必須熟悉前人總結過的面向對象的思想的設計原則,那么都有哪些原則呢,我們就來了解一下
- 單一職責原則
- 其實就是開發人員經常說的”高內聚,低耦合”
- 開閉原則
- 核心思想是:一個對象對擴展開放,對修改關閉。其實開閉原則的意思就是:對類的改動是通過增加代碼進行的,而不是修改現有代碼。
- 里氏替換原則
- 核心思想:在任何父類出現的地方都可以用它的子類來替代。其實就是說:同一個繼承體系中的對象應該有共同的行為特征。
- 依賴注入原則
- 核心思想:要依賴于抽象,不要依賴于具體實現。
- 接口分離原則
- 核心思想:不應該強迫程序依賴它們不需要使用的方法。
- 迪米特原則
- 核心思想:一個對象應當對其他對象盡可能少的了解
設計模式
設計模式在我們開發中還是經常用到的,那么,下面我們來學習一下
-
設計模式概述
- 設計模式(Design pattern)是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。
設計模式不是一種方法和技術,而是一種思想
設計模式和具體的語言無關,學習設計模式就是要建立面向對象的思想,盡可能的面向接口編程,低耦合,高內聚,使設計的程序可復用學習設計模式能夠促進對面向對象思想的理解,反之亦然。它們相輔相成
設計模式的幾個要素:
名字 必須有一個簡單,有意義的名字
問題 描述在何時使用模式
解決方案 描述設計的組成部分以及如何解決問題
效果 描述模式的效果以及優缺點
設計模式的分類
創建型模式 對象的創建
結構型模式 對象的組成(結構)
行為型模式 對象的行為
創建型模式:簡單工廠模式,工廠方法模式,抽象工廠模式,建造者模式,原型模式,單例模式。(6個)結構型模式:外觀模式、適配器模式、代理模式、裝飾模式、橋接模式、組合模式、享元模式。(7個)行為型模式:模版方法模式、觀察者模式、狀態模式、職責鏈模式、命令模式、訪問者模式、策略模式、備忘錄模式、迭代器模式、解釋器模式。(10個)
設計模式的分類雖然很多,但是我們平時能用到的也就下面幾種而已,所以不要被它這么多模式所嚇倒。
-
常見的設計模式 簡單工廠模式和工廠方法模式(接口)
- 模版設計模式(抽象類)
- 裝飾設計模式(IO流)
- 單例設計模式(多線程)
- 適配器模式(GUI)
下面我們就來學習我們常用的幾種設計模式,讓大家掌握,熟悉,并運用到自己的項目中,學會學以致用。
簡單工廠模式
- 簡單工廠模式概述
- 又叫靜態工廠方法模式,它定義一個具體的工廠類負責創建一些類的實例
我們來寫一個簡單的例子來解釋簡單工廠模式
/* * 抽象的動物類,里面有抽象的方法 */
public abstract class Animal {
public abstract void eat();
}
/* * 具體的動物貓繼承抽象動物類,重寫抽象方法 */
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("貓吃魚");
}
}
/* * 具體的動物狗繼承抽象動物類,重寫抽象方法 */
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃肉");
}
}
/* * 動物工廠類,可以造貓和狗 */
public class AnimalFactory {
private AnimalFactory() { }
public static Animal createAnimal(String type) {
if ("dog".equals(type)) {
return new Dog();
} else if ("cat".equals(type)) {
return new Cat();
} else {
return null;
}
}
}
/* * 測試類 */
public class AnimalDemo {
public static void main(String[] args) {
// 工廠有了后,通過工廠給造
Animal a = AnimalFactory.createAnimal("dog");
a.eat();
a = AnimalFactory.createAnimal("cat");
a.eat();
// NullPointerException
a = AnimalFactory.createAnimal("pig");
if (a != null) {
a.eat();
} else {
System.out.println("對不起,暫時不提供這種動物");
}
}
}
以前我們在學代碼的時候,不會去創建這個動物的工廠類,而是直接具體類的調用,比如:
Dog d = new Dog();
d.eat();
Cat c = new Cat();
c.eat();
現在我們運用了簡單工廠模式后,就不用每次用的時候去new對象,而是直接去調用這個工廠類里面的具體方法,它會給我們返回一個已經new好的對象。那么這樣做有什么有缺點呢,我們來總結一下。
- 優點
- 客戶端不需要在負責對象的創建,從而明確了各個類的職責
- 缺點
- 這個靜態工廠類負責所有對象的創建,如果有新的對象增加,或者某些對象的創建方式不同,就需要不斷的修改工廠類,不利于后期的維護
當我們要用一個模式時,當這個模式的優點大于缺點的時候,我們就可以使用了,但是在簡單工廠模式中我們可以看到它的缺點,當我們有新的對象增加時,就要不斷的修改工廠類,所以不推薦大家用簡單工廠模式,那么我們要用什么呢,這就引出了我們要學的下一個知識點工廠方法模式
工廠方法模式
- 工廠方法模式概述
- 工廠方法模式中抽象工廠類負責定義創建對象的接口,具體對象的創建工作由繼承抽象工廠的具體類實現。
我們就來用工廠方法模式對上面的那個例子進行改進
/* * 抽象的動物類,里面有抽象的方法 */
public abstract class Animal {
public abstract void eat();
}
/* * 工廠類接口,里面有抽象的創造動物的方法 */
public interface Factory {
public abstract Animal createAnimal();
}
/* * 具體的貓類繼承抽象動物類,重寫抽象方法 */
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("貓吃魚");
}
}
/* * 貓工廠類實現工廠類并實現它的抽象方法,返回一個貓對象 */
public class CatFactory implements Factory {
@Override
public Animal createAnimal() {
return new Cat();
}
}
/* * 具體的狗類繼承抽象動物類,重寫抽象方法 */
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃肉");
}
}
/* * 狗工廠類實現工廠類并實現它的抽象方法,返回一個狗對象 */
public class DogFactory implements Factory {
@Override
public Animal createAnimal() {
return new Dog();
}
}
/* * 測試類 */
public class AnimalDemo {
public static void main(String[] args) {
// 需求:我要買只狗
Factory f = new DogFactory();
Animal a = f.createAnimal();
a.eat();
//需求:我要買只貓
f = new CatFactory();
a = f.createAnimal();
a.eat();
}
}
運行程序,控制臺會輸出,狗吃肉 貓吃魚
我們仔細觀察用工廠方法模式比比簡單工廠模式多了幾個類,但是當我們在需要一種動物豬時,我們就不用去修改工廠類里面的代碼了,只需用創建一個豬類繼承抽象動物類,重寫抽象方法,再創建一個豬的工廠類實現工廠類并實現它的抽象方法,就可以了。代碼具有很強的維護性和擴展性,那么我們來分析一下工廠方法模式的優缺點。
- 優點
- 客戶端不需要在負責對象的創建,從而明確了各個類的職責,如果有新的對象增加,只需要增加一個具體的類和具體的工廠類即可,不影響已有的代碼,后期維護容易,增強了系統的擴展性
- 缺點
- 需要額外的編寫代碼,增加了工作量
我們可以看到工廠方法模式的優點明顯大于缺點,所以推薦大家使用。
單例設計模式
- 單例設計模式概述
- 單例模式就是要確保類在內存中只有一個對象,該實例必須自動創建,并且對外提供。
如何實現類在內存中只有一個對象呢?
- 構造私有
- 本身提供一個對象
- 通過公共的方法讓外界訪問
那么我們就來學習單例模式中餓漢式和懶漢式 這兩種模式,并做以比較
餓漢式
- 餓漢式:類一加載就創建對象
public class Student {
// 構造私有
private Student() { }
// 自己造一個對象
// 靜態方法只能訪問靜態成員變量,加靜態
// 為了不讓外界直接訪問修改這個值,加private
private static Student s = new Student();
// 提供公共的訪問方式
// 為了保證外界能夠直接使用該方法,加靜態
public static Student getStudent() { return s; }}
public class StudentDemo {
public static void main(String[] args) {
// 通過單例得到對象
Student s1 = Student.getStudent();
Student s2 = Student.getStudent();
System.out.println(s1 == s2); //true
}
}
運行程序,控制臺會輸出true,說明我們用單例模式的餓漢式確保類在內存中只有一個對象,他的特點就是類一加載就創建對象,可以在代碼中Student類中體現到。那么我們怎樣才能在用這個對象的時候才去創建它呢,我們就要來看下懶漢式了。
懶漢式
- 懶漢式:用對象的時候,才去創建對象
public class Teacher {
private Teacher() { }
private static Teacher t = null;
public static Teacher getTeacher() {
if (t == null) {
t = new Teacher();//當我們去用這個對象的時候才去創建它
}
return t;
}
}
public class TeacherDemo {
public static void main(String[] args) {
Teacher t1 = Teacher.getTeacher();
Teacher t2 = Teacher.getTeacher();
System.out.println(t1 == t2); //true
}
}
單例模式的餓漢式和懶漢式是不是很容易理解呢,那么我們什么時候用餓漢式什么時候用懶漢式呢?
我們就來總結一下
餓漢式懶漢式比較
餓漢式我們經常在開發中使用,因為餓漢式是不會出問題的單例模式
懶漢式我們在面試中回答用,因為懶漢式可能會出問題的單例模式。面試主要面兩個思想,分別是:
- 懶加載思想(延遲加載)
- 線程安全問題(就要考慮下面3個方面)
- 是否多線程環境
- b:是否有共享數據
- c:是否有多條語句操作共享數據
如果都是,就會存在線程的安全問題,我們上面的懶漢式代碼是不完整的,應該給對象中的方法加上synchronized關鍵字,這樣才算完整
public synchronized static Teacher getTeacher() {
if (t == null) {
t = new Teacher();
}
return t;
}
Runtime類
我們在這里為什么要說Runtime類,因為它在java中的設計就是按照單例模式之餓漢式設計的,我們來看一段源碼
class Runtime {
private Runtime() {}
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
}
- 每個 Java 應用程序都有一個 Runtime 類實例,使應用程序能夠與其運行的環境相連接??梢酝ㄟ^ getRuntime 方法獲取當前運行時。
- 應用程序不能創建自己的 Runtime 類實例。
- Runtime類使用
- public Process exec(String command)
這個類是用來干什么的呢,它可以幫助我們運行DOS命令,比如打開記事本、計算器之類的電腦工具,當然也有更多的功能,我們來體驗一下
public class RuntimeDemo {
public static void main(String[] args) throws IOException {
Runtime r = Runtime.getRuntime();
r.exec("notepad");
}
}
運行程序會幫我們打開記事本
r.exec("calc"); //換成calc,會幫我們打開計算機
r.exec("shutdown -s -t 1000"); // 這個命令會幫我們把電腦定時關機,上面的意思就是1000秒以后關機,我們來看運行后的效果圖
模版設計模式
- 模版設計模式概述
- 模版方法模式就是定義一個算法的骨架,而將具體的算法延遲到子類中來實現
- 優點
- 使用模版方法模式,在定義算法骨架的同時,可以很靈活的實現具體的算法,滿足用戶靈活多變的需求
- 缺點
- 如果算法骨架有修改的話,則需要修改抽象類
我們可以在計算程序的運行時間中應用模版設計模式,在代碼中我們只需用改變要計算的代碼就可以了,把計算的時間設計成一個模版。
裝飾設計模式
-
裝飾設計模式概述
- 裝飾模式就是使用被裝飾類的一個子類的實例,在客戶端將這個子類的實例交給裝飾類。是繼承的替代方案
-
優點
- 使用裝飾模式,可以提供比繼承更靈活的擴展對象的功能,它可以動態的添加對象的功能,并且可以隨意的組合這些功能
-
缺點
- 正因為可以隨意組合,所以就可能出現一些不合理的邏輯
在IO流中的裝飾模式應用
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter((new OutputStreamWriter(System.out)));