一、簡介
軟件設計模式(Design pattern),又稱設計模式,是一套被反覆使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性、程序的重用性。
二、六大原則
1、開閉原則
開閉原則(Open Close Principle)的意思是:對擴展開放,對修改關閉。在程序需要進行拓展的時候,不能去修改原有的代碼,實現一個熱插拔的效果。簡言之,是爲了使程序的擴展性好,易於維護和升級。想要達到這樣的效果,我們需要使用接口和抽象類,後面的具體設計中我們會提到這點。
2、里氏代換原則
里氏代換原則(Liskov Substitution Principle)是面向對象設計的基本原則之一。 里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。LSP 是繼承複用的基石,只有當派生類可以替換掉基類,且軟件單位的功能不受到影響時,基類才能真正被複用,而派生類也能夠在基類的基礎上增加新的行爲。里氏代換原則是對開閉原則的補充。實現開閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範。
3、依賴倒轉原則
依賴倒轉原則(Dependence Inversion Principle)是開閉原則的基礎,具體內容:針對接口編程,依賴於抽象而不依賴於具體。
4、接口隔離原則
接口隔離原則(Interface Segregation Principle)的意思是:使用多個隔離的接口,比使用單個接口要好。它還有另外一個意思是:降低類之間的耦合度。由此可見,其實設計模式就是從大型軟件架構出發、便於升級和維護的軟件設計思想,它強調降低依賴,降低耦合。
5、最少知道原則
迪米特法則,又稱最少知道原則(Demeter Principle)是指:一個實體應當儘量少地與其他實體之間發生相互作用,使得系統功能模塊相對獨立。
6、合成複用原則
合成複用原則(Composite Reuse Principle)是指:儘量使用合成/聚合的方式,而不是使用繼承。
三、實例詳解
總體來說設計模式分爲三大類:
- 創建型模式(5種):工廠模式、抽象工廠模式、單例模式、構建者模式、原型模式。
- 結構型模式(7種):適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
- 行爲型模式(11種):策略模式、模板模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式。
根據分類,我畫了一份腦圖,如下圖:
1、工廠模式
定義一個用於創建產品的接口,由子類決定生產什麼產品。典型的案例:JDBC連接數據庫,通過加載不同的數據庫驅動,得到不同的數據庫連接。
Phone類:手機標準規範類
public interface Phone {
void make();
}
HuaWeiPhone類:製造華爲手機
public class HuaWeiPhone implements Phone{
@Override
public void make() {
System.out.println("make huawei phone");
}
}
XiaoMiPhone類:製造小米手機
public class XiaoMiPhone implements Phone{
@Override
public void make() {
System.out.println("make xiaomi phone");
}
}
PhoneFactory類:手機代工廠(Factory)
public class PhoneFactory {
public Phone makePhone(String phoneType) {
if(phoneType.equalsIgnoreCase("xiaomi")){
return new XiaoMiPhone();
}
else if(phoneType.equalsIgnoreCase("huawei")) {
return new HuaWeiPhone();
}
return null;
}
}
演示:
public class Demo {
public static void main(String[] args) {
PhoneFactory phoneFactory = new PhoneFactory();
Phone huawei = phoneFactory.makePhone("huawei");
huawei.make();
Phone xiaomi = phoneFactory.makePhone("xiaomi");
xiaomi.make();
}
}
控制檯輸出:
make huawei phone
make xiaomi phone
2、抽象工廠模式
提供一個創建產品族的接口,其每個子類可以生產一系列相關的產品。例如,創建小米工廠和華爲工廠,生產多個產品(手機、電腦、電視…)。
Phone類:手機標準規範類
public interface Phone {
void make();
}
HuaWeiPhone類:製造華爲手機
public class HuaWeiPhone implements Phone{
@Override
public void make() {
System.out.println("make huawei phone");
}
}
XiaoMiPhone類:製造小米手機
public class XiaoMiPhone implements Phone{
@Override
public void make() {
System.out.println("make xiaomi phone");
}
}
AbstractFactory類:抽象工廠類
public interface AbstractFactory {
Phone makePhone();
}
XiaoMiFactory類:製造小米產品的工廠
public class XiaoMiFactory implements AbstractFactory {
@Override
public Phone makePhone() {
return new XiaoMiPhone();
}
}
HuaWeiFactory類:製造華爲產品的工廠
public class HuaWeiFactory implements AbstractFactory{
@Override
public Phone makePhone() {
return new HuaWeiPhone();
}
}
演示:
public class Demo {
public static void main(String[] args) {
HuaWeiFactory huaWeiFactory = new HuaWeiFactory();
Phone huawei = huaWeiFactory.makePhone();
huawei.make();
XiaoMiFactory xiaoMiFactory = new XiaoMiFactory();
Phone xiaomi = xiaoMiFactory.makePhone();
xiaomi.make();
}
}
控制檯輸出:
make huawei phone
make xiaomi phone
3、單例模式
某個類只能生成一個實例,該類提供了一個全局訪問點供外部獲取該實例,其拓展是有限多例模式。例如,一些設備管理器常常設計爲單例模式,比如一個電腦有兩臺打印機,在輸出的時候就要處理不能兩臺打印機打印同一個文件。
Singleton類:懶漢式,線程安全。
- 優點:第一次調用才初始化,避免內存浪費。
- 缺點:必須加鎖 synchronized 才能保證單例,但加鎖會影響效率。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
Singleton類:餓漢式,線程安全
- 優點:沒有加鎖,執行效率會提高。
- 缺點:類加載時就初始化,浪費內存。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
4、構建者模式
將一個複雜對象分解成多個相對簡單的部分,然後根據不同需要分別創建它們,最後構建成該複雜對象。例如:電腦是顯示器、鍵盤、鼠標等等組成使用的,但客戶只需購買就可以了,無需關注產品內部組成的細節。
- Product:最終要生成的對象,例如 Computer實例。
- Builder:構建者的抽象基類(有時會使用接口代替)。其定義了構建Product的抽象步驟,其實體類需要實現這些步驟。其會包含一個用來返回最終產品的方法Product getProduct()。
- ConcreteBuilder:Builder的實現類。
- Director:決定如何構建最終產品的算法. 其會包含一個負責組裝的方法void Construct(Builder builder), 在這個方法中通過調用builder的方法,就可以設置builder,等設置完成後,就可以通過builder的 getProduct() 方法獲得最終的產品。
PC類:電腦標準規範類
public class PC {
private String keyboard;//鍵盤
private String mouse;//鼠標
private String monitor;//顯示器
private String master;//主機
public PC(String keyboard, String mouse) {
this.keyboard = mouse;
this.mouse = mouse;
}
public void setMonitor(String monitor) {
this.monitor = monitor;
}
public void setMaster(String master) {
this.master = master;
}
@Override
public String toString() {
return "PC{" +
"keyboard='" + keyboard + '\'' +
", mouse='" + mouse + '\'' +
", monitor='" + monitor + '\'' +
", master='" + master + '\'' +
'}';
}
}
PCBuilder類:抽象構建者類
public abstract class PCBuilder {
public abstract void setMonitor();
public abstract void setMaster();
public abstract PC getPC();
}
MacPCBuilder類:蘋果電腦構建者類
public class MacPCBuilder extends PCBuilder {
private PC pc;
public MacPCBuilder(String keyboard, String mouse) {
pc = new PC(keyboard, mouse);
}
@Override
public void setMonitor() {
pc.setMonitor("蘋果顯示器");
}
@Override
public void setMaster() {
pc.setMaster("蘋果主機");
}
@Override
public PC getPC() {
return pc;
}
}
MiPCBuilder類:小米電腦構建者類
public class MiPCBuilder extends PCBuilder {
private PC pc;
public MiPCBuilder(String keyboard, String mouse) {
pc = new PC(keyboard, mouse);
}
@Override
public void setMonitor() {
pc.setMonitor("小米顯示器");
}
@Override
public void setMaster() {
pc.setMaster("小米主機");
}
@Override
public PC getPC() {
return pc;
}
}
PCDirector類:指導者類
public class PCDirector {
public void makePC(PCBuilder builder){
builder.setMonitor();
builder.setMaster();
}
}
演示:
public class Demo {
public static void main(String[] args) {
PCDirector director=new PCDirector();
PCBuilder builder=new MacPCBuilder("德國櫻桃鍵盤","羅技鼠標");
director.makePC(builder);
PC macPC=builder.getPC();
System.out.println("mac pc:"+macPC.toString());
PCBuilder miPCBuilder=new MiPCBuilder("ikbc鍵盤","雷柏鼠標");
director.makePC(miPCBuilder);
PC miPC=miPCBuilder.getPC();
System.out.println("mi pc:"+miPC.toString());
}
}
控制檯輸出:
mac pc:PC{keyboard='羅技鼠標', mouse='羅技鼠標', monitor='蘋果顯示器', master='蘋果主機'}
mi pc:PC{keyboard='雷柏鼠標', mouse='雷柏鼠標', monitor='小米顯示器', master='小米主機'}
5、原型模式
將一個對象作爲原型,通過對其進行復制而克隆出多個和原型類似的新實例。
這種模式是實現了一個原型接口,該接口用於創建當前對象的克隆。當直接創建對象的代價比較大時,則採用這種模式。例如,一個對象需要在一個高代價的數據庫操作之後被創建。我們可以緩存該對象,在下一個請求時返回它的克隆,在需要的時候更新數據庫,以此來減少數據庫調用。
Shape類:實現了Cloneable接口的抽象類
public abstract class Shape implements Cloneable {
private String id;
protected String type;
abstract void buy();
public String getType(){
return type;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
Apple類:蘋果類
public class Apple extends Shape {
public Apple(){
type = "Apple";
}
@Override
public void buy() {
System.out.println("buy() method: Apple");
}
}
Banana類:香蕉類
public class Banana extends Shape {
public Banana(){
type = "Banana";
}
@Override
public void buy() {
System.out.println("buy() method: Banana");
}
}
Orange類:橙子類
public class Orange extends Shape {
public Orange(){
type = "Orange";
}
@Override
public void buy() {
System.out.println("buy() method: Orange");
}
}
ShapeCache類:
public class ShapeCache {
private static HashMap<String, Shape> shapeMap = new HashMap<String, Shape>();
public static Shape getShape(String shapeId) {
Shape cachedShape = shapeMap.get(shapeId);
return (Shape) cachedShape.clone();
}
// 對每種水果都運行數據庫查詢,並創建該水果。shapeMap.put(shapeKey, shape);
public static void loadCache() {
Apple apple = new Apple();
apple.setId("1");
shapeMap.put(apple.getId(),apple);
Banana banana = new Banana();
banana.setId("2");
shapeMap.put(banana.getId(),banana);
Orange orange = new Orange();
orange.setId("3");
shapeMap.put(orange.getId(),orange);
}
}
演示:
public class Demo {
public static void main(String[] args) {
ShapeCache.loadCache();
Shape clonedShape = (Shape) ShapeCache.getShape("1");
System.out.println("Shape : " + clonedShape.getType());
Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
System.out.println("Shape : " + clonedShape2.getType());
Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
System.out.println("Shape : " + clonedShape3.getType());
}
}
控制檯輸出:
Shape : Apple
Shape : Banana
Shape : Orange