設計模式之外觀模式

本文通過老王改造小王公司的整體架構來說明外觀模式,所謂的外觀模式其實就是在各種複雜的子系統中抽象出來一個介面,隱藏具體的實現細節,調用方調用時只需要調用介面即可。為了加深理解我們會選出外觀模式在源碼中的應用進行重點的介紹,最後是我對設計模式學習過程中的一些思考。

讀者可以拉取完整程式碼到本地進行學習,實現程式碼均測試通過後上傳到碼雲

一、引出問題

隨著小王創業的不斷深入,公司各個業務模組越來越複雜,每當客戶們與他的合作時都要深入各個模組內部,而且客戶要依賴小王的各個模組,給使用模組的客戶帶來了困難。

小王就想請老王幫他規劃一下公司的架構。

老王聽完了小王的需求,開始給他分析問題。

現在的公司的架構已經演變的相當複雜了,客戶訪問你的時候都要通過各個子系統,你應該將你所有的子系統整合到一個前天(介面),客戶訪問你的子系統只需要通過這個前台(介面)即可。這樣就能很好的解決這個問題。

二、概念與運用

老王提出來的解決辦法正是外觀模式,是一種通過為多個複雜的子系統提供一個一致的介面,而使這些子系統更加容易被訪問的模式。

該模式對外有一個統一介面,外部應用程式不用關心內部子系統的具體細節,這樣會大大降低應用程式的複雜度,提高了程式的可維護性。

該模式應該是包含兩個角色:

①各個子系統角色

②外觀角色

我們接著看其實現程式碼:

子系統:

/**
 * @author tcy
 * @Date 11-08-2022
 */
public class SystemWork01 {
    public void method1() {
        System.out.println("子系統01的業務模式!");
    }
}

/**
 * @author tcy
 * @Date 11-08-2022
 */
public class SystemWork02 {
    public void method1() {
        System.out.println("子系統02的業務模式!");
    }
}

/**
 * @author tcy
 * @Date 11-08-2022
 */
public class SystemWork03 {
    public void method1() {
        System.out.println("子系統03的業務模式!");
    }
}

外觀角色:

/**
 * @author tcy
 * @Date 11-08-2022
 */
public class Facade {
    private SystemWork01 obj1 = new SystemWork01();
    private SystemWork02 obj2 = new SystemWork02();
    private SystemWork03 obj3 = new SystemWork03();
    public void method() {
        obj1.method1();
        obj2.method1();
        obj3.method1();
    }
}

客戶端:

/**
 * @author tcy
 * @Date 11-08-2022
 */
public class Client {
    public static void main(String[] args) {
        Facade f = new Facade();
        f.method();
    }

}

外觀模式的實現程式碼很簡單,讀者想必看一遍就知道什麼意思了。但學會和會用是兩碼事,我們舉一些外觀模式以便讀者在使用時可以參考程式碼。

三、應用

看似外觀模式很簡單,實際應用中應該不多,其實在實際應用中處處有體現,比如Java開發學習的第一個框架肯定就是SSM,而SSM採用分層,而各個層之間的訪問就是外觀模式的體現。

還有就是我們在維護一個複雜的系統時,新系統不得不依賴老系統的某些功能,那使用外觀模式是最合適不過的。

在Mybatis的Configuration就是使用的外觀模式。

image-20220812091631730

客戶端使用Mybatis的功能時,只需要調用Configuration的功能即可。

我們簡單看下Configuration的源碼。

 //Configuration 類:
public class Configuration {
	protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
	protected ObjectFactory objectFactory = new DefaultObjectFactory();
  	protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();

	 public MetaObject newMetaObject(Object object) {
    	return MetaObject.forObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
    }
}

//MetaObject類
public class MetaObject {
	private Object originalObject;
	private ObjectWrapper objectWrapper;
	private ObjectFactory objectFactory;
	private ObjectWrapperFactory objectWrapperFactory;
	private ReflectorFactory reflectorFactory;

	public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
    	if (object == null) {
    		return SystemMetaObject.NULL_META_OBJECT;
    	} else {
      		return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
    	}
 	}

	private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
    	this.originalObject = object;
    	this.objectFactory = objectFactory;
    	this.objectWrapperFactory = objectWrapperFactory;
   		this.reflectorFactory = reflectorFactory;

    	if (object instanceof ObjectWrapper) {
      		this.objectWrapper = (ObjectWrapper) object;
    	} else if (objectWrapperFactory.hasWrapperFor(object)) {
      		this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
    	} else if (object instanceof Map) {
      		this.objectWrapper = new MapWrapper(this, (Map) object);
    	} else if (object instanceof Collection) {
     		this.objectWrapper = new CollectionWrapper(this, (Collection) object);
    	} else {
      		this.objectWrapper = new BeanWrapper(this, object);
  		}
	}
}

在使用MetaObject時,客戶端只需要調用Configuration的newMetaObject(Object object)方法,並傳遞一個Object參數,就可以獲取對應的MetaObject。

至於具體的產生什麼樣的MetaObject,則有MetaObject的類的forObject(object, objectFactory, objectWrapperFactory, reflectorFactory)方法實現。

具體深究Mybatis 的內部實現細節還是很麻煩的,這裡是淺談一下,有興趣的讀者可以拉Mybatis源碼進行重點學習。

四、總結

前幾天在一個技術公眾號上看到了一個爭論,關於設計模式在新手期要不要學的問題,一些人的觀點就是新手壓根看不懂設計模式,看懂了實際開發也不會用。

另外一派的觀點則是,設計模式一定要學,在你開發中慢慢訓練有意識的使用設計模式,在你開發了一段時間的系統後再學習設計模式的話,那時候你壓根沒有時間去重構你的程式碼。

我的觀點更趨向於後者,自從我學了設計模式以後,再寫程式碼的時候,尤其是在老程式碼之上加一些新功能時,我會下意識的回憶一下學過的設計模式,思考使用設計模式對我的程式碼有沒有幫助。

學過設計模式以後,在日常開發中技術水平不知不覺就提高了,不像以前那樣為了實現功能而實現功能。

推薦讀者,參考軟體設計七大原則 認真閱讀往期的文章,認真體會。

創建型設計模式

一、設計模式之工廠方法和抽象工廠

二、設計模式之單例和原型

三、設計模式之建造者模式

結構型設計模式

四、設計模式之代理模式

五、設計模式之適配器模式

六、橋接模式

七、組合模式

八、設計模式之裝飾器模式