Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 設計模式

Android 設計模式

編輯:關於Android編程

簡介

項目開發中發現問題、解決問題這個過程中會出現很多問題,比如重復出現、某個問題的遺留,這些問題的本質就是設計模式。今天記錄設計模式的知識點。

內容

在java以及其他的面向對象設計模式中,類與類之間主要有6種關系,他們分別是:依賴、關聯、聚合、組合、繼承、實現。它們的耦合度依次增強。

依賴關系:
對於兩個相對獨立的對象,當一個對象負責構造另一個對象的實例,或者依賴另一個對象的服務時,這兩個對象之間主要體現為依賴關系。
關聯關系:
分為單向關聯和雙向關聯。在java中,單向關聯表現為:類A當中使用了類B,其中類B是作為類A的成員變量。雙向關聯表現為:類A當中使用了類B作為成員變量;同時類B中也使用了類A作為成員變量。
聚合關系:
是關聯關系的一種,耦合度強於關聯,他們的代碼表現是相同的,僅僅是在語義上有所區別:關聯關系的對象間是相互獨立的,而聚合關系的對象之間存在著包容關系,他們之間是“整體-個體”的相互關系。
組合關系:
是一種耦合度更強的關聯關系。存在組合關系的類表示“整體-部分”的關聯關系,“整體”負責“部分”的生命周期,他們之間是共生共死的;並且“部分”單獨存在時沒有任何意義。
繼承:
表示類與類(或者接口與接口)之間的父子關系。
實現:
表示一個類實現一個或多個接口的方法。


設計原則
要點 定義 描述單一職責原則不要存在多於一個導致類變更的原因。通俗的說,即一個類只負責一項職責。問題由來:類T負責兩個不同的職責:職責P1,職責P2。當由於職責P1需求發生改變而需要修改類T時,有可能會導致原本運行正常的職責P2功能發生故障。

解決方案:遵循單一職責原則。分別建立兩個類T1、T2,使T1完成職責P1功能,T2完成職責P2功能。這樣,當修改類T1時,不會使職責P2發生故障風險;同理,當修改T2時,也不會使職責P1發生故障風險。裡氏替換原則定義1:如果對每一個類型為 T1的對象 o1,都有類型為 T2 的對象o2,使得以 T1定義的所有程序 P 在所有的對象 o1 都代換成 o2 時,程序 P 的行為沒有發生變化,那麼類型 T2 是類型 T1 的子類型。
定義2:所有引用基類的地方必須能透明地使用其子類的對象。問題由來:有一功能P1,由類A完成。現需要將功能P1進行擴展,擴展後的功能為P,其中P由原有功能P1與新功能P2組成。新功能P由類A的子類B來完成,則子類B在完成新功能P2的同時,有可能會導致原有功能P1發生故障。

解決方案:當使用繼承時,遵循裡氏替換原則。類B繼承類A時,除添加新的方法完成新增功能P2外,盡量不要重寫父類A的方法,也盡量不要重載父類A的方法。依賴倒置原則高層模塊不應該依賴低層模塊,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。問題由來:類A直接依賴類B,假如要將類A改為依賴類C,則必須通過修改類A的代碼來達成。這種場景下,類A一般是高層模塊,負責復雜的業務邏輯;類B和類C是低層模塊,負責基本的原子操作;假如修改類A,會給程序帶來不必要的風險。

解決方案:將類A修改為依賴接口I,類B和類C各自實現接口I,類A通過接口I間接與類B或者類C發生聯系,則會大大降低修改類A的幾率。接口隔離原則客戶端不應該依賴它不需要的接口;一個類對另一個類的依賴應該建立在最小的接口上。問題由來:類A通過接口I依賴類B,類C通過接口I依賴類D,如果接口I對於類A和類B來說不是最小接口,則類B和類D必須去實現他們不需要的方法。

解決方案:將臃腫的接口I拆分為獨立的幾個接口,類A和類C分別與他們需要的接口建立依賴關系。也就是采用接口隔離原則。迪米特法則一個對象應該對其他對象保持最少的了解。問題由來:類與類之間的關系越密切,耦合度越大,當一個類發生改變時,對另一個類的影響也越大。

解決方案:盡量降低類與類之間的耦合。開閉原則一個軟件實體如類、模塊和函數應該對擴展開放,對修改關閉。問題由來:在軟件的生命周期內,因為變化、升級和維護等原因需要對軟件原有代碼進行修改時,可能會給舊代碼中引入錯誤,也可能會使我們不得不對整個功能進行重構,並且需要原有代碼經過重新測試。

解決方案:當軟件需要變化時,盡量通過擴展軟件實體的行為來實現變化,而不是通過修改已有的代碼來實現變化。

設計模式

要點 定義 描述單例模式確保一個類只有一個實例,而且自行實例化並向整個系統提供這個實例。單例模式注意事項:
只能使用單例類提供的方法得到單例對象,不要使用反射,否則將會實例化一個新對象。不要做斷開單例類對象與類中靜態引用的危險操作。多線程使用單例使用共享資源時,注意線程安全問題。工廠方法模式定義一個用於創建對象的接口,讓子類決定實例化哪一個類,工廠方法使一個類的實例化延遲到其子類。在工廠方法模式中,核心的工廠類不再負責所有的對象的創建,而是將具體創建的工作交給子類去做。這個核心類則搖身一變,成為了一個抽象工廠角色,僅負責給出具體工廠子類必須實現的接口,而不接觸哪一個類應當被實例化這種細節。抽象工廠模式為創建一組相關或相互依賴的對象提供一個接口,而且無需指定他們的具體類。在以下情況下,適用於工廠方法模式:
(1) 當一個類不知道它所必須創建的對象的類的時候。
(2) 當一個類希望由它的子類來指定它所創建的對象的時候。
(3) 當類將創建對象的職責委托給多個幫助子類中的某一個,並且你希望將哪一個幫助子類是代理者這一信息局部化的時候。模版方法模式定義一個操作中算法的框架,而將一些步驟延遲到子類中,使得子類可以不改變算法的結構即可重定義該算法中的某些特定步驟。子類可以置換掉父類的可變部分,但是子類卻不可以改變模板方法所代表的頂級邏輯。
  每當定義一個新的子類時,不要按照控制流程的思路去想,而應當按照“責任”的思路去想。換言之,應當考慮哪些操作是必須置換掉的,哪些操作是可以置換掉的,以及哪些操作是不可以置換掉的。使用模板模式可以使這些責任變得清晰。建造者模式將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。與抽象工廠的區別:在建造者模式裡,有個指導者,由指導者來管理建造者,用戶是與指導者聯系的,指導者聯系建造者最後得到產品。即建造模式可以強制實行一種分步驟進行的建造過程。
  建造模式是將復雜的內部創建封裝在內部,對於外部調用的人來說,只需要傳入建造者和建造工具,對於內部是如何建造成成品的,調用者無需關心。
在Java的應用中JavaMail使用到了該模式。代理模式為其他對象提供一種代理以控制對這個對象的訪問。所謂代理,就是一個人或者機構代表另一個人或者機構采取行動。在一些情況下,一個客戶不想或者不能夠直接引用一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。原型模式用原型實例指定創建對象的種類,並通過拷貝這些原型創建新的對象。

原型模式要求對象實現一個可以“克隆”自身的接口,這樣就可以通過復制一個實例對象本身來創建一個新的實例。這樣一來,通過原型實例創建新的對象,就不再需要關心這個實例本身的類型,只要實現了克隆自身的方法,就可以通過這個方法來獲取新的對象,而無須再去通過new來創建。在Java語言裡深度克隆一個對象,常常可以先使對象實現Serializable接口,然後把對象(實際上只是對象的拷貝)寫到一個流裡(序列化),再從流裡讀回來(反序列化),便可以重建對象。

原型模式的優點
  原型模式允許在運行時動態改變具體的實現類型。原型模式可以在運行期間,由客戶來注冊符合原型接口的實現類型,也可以動態地改變具體的實現類型,看起來接口沒有任何變化,但其實運行的已經是另外一個類實例了。因為克隆一個原型就類似於實例化一個類。

原型模式的缺點
  原型模式最主要的缺點是每一個類都必須配備一個克隆方法。配備克隆方法需要對類的功能進行通盤考慮,這對於全新的類來說不是很難,而對於已經有的類不一定很容易,特別是當一個類引用不支持序列化的間接對象,或者引用含有循環結構的時候。中介者模式用一個中介者對象封裝一系列的對象交互,中介者使各對象不需要顯示地相互作用,從而使耦合松散,而且可以獨立地改變它們之間的交互。

中介者模式的優點
適當地使用中介者模式可以避免同事類之間的過度耦合,使得各同事類之間可以相對獨立地使用。
使用中介者模式可以將對象間一對多的關聯轉變為一對一的關聯,使對象間的關系易於理解和維護。
使用中介者模式可以將對象的行為和協作進行抽象,能夠比較靈活的處理對象間的相互作用。適用場景
在面向對象編程中,一個類必然會與其他的類發生依賴關系,完全獨立的類是沒有意義的。一個類同時依賴多個類的情況也相當普遍,既然存在這樣的情況,說明,一對多的依賴關系有它的合理性,適當的使用中介者模式可以使原本凌亂的對象關系清晰,但是如果濫用,則可能會帶來反的效果。一般來說,只有對於那種同事類之間是網狀結構的關系,才會考慮使用中介者模式。可以將網狀結構變為星狀結構,使同事類之間的關系變的清晰一些。
中介者模式是一種比較常用的模式,也是一種比較容易被濫用的模式。對於大多數的情況,同事類之間的關系不會復雜到混亂不堪的網狀結構,因此,大多數情況下,將對象間的依賴關系封裝的同事類內部就可以的,沒有必要非引入中介者模式。濫用中介者模式,只會讓事情變的更復雜。命令模式意圖:將一個請求封裝為一個對象,從而可用不同的請求對客戶進行參數化;對請求排隊或記錄日志,以及支持可撤銷的操作
動機:將”發出請求的對象”和”接收與執行這些請求的對象”分隔開來。常見應用:
1、工作隊列,線程池,日程安排
2、日志請求(系統恢復)
要點:
1、命令模式將發出請求的對象和執行請求的對象解耦
2、在被解耦的兩者之間是通過命令對象進行溝通的。命令對象封裝了接收者和一個或一組動作
3、調用者通過調用命令對象的execute()發出請求,這會使得接收者的動作被調用
4、調用者可以接受命令當作參數,甚至在運行時動態的進行
5、命令可以支持撤銷,做法是實現一個undo()方法來回到execute()被執行前的狀態
6、宏命令是命令的一種簡單的延伸,允許調用多個命令。宏方法也可以支持撤銷
7、實際操作時,很常見使用"聰明"命令對象,也就是直接實現了請求,而不是將工作委托給接受者(弊端?)
8、命令也可以用來實現日志和事物系統責任鏈模式使多個對象都有機會處理請求,從而避免了請求的發送者和接收者之間的耦合關系。將這些對象連成一條鏈,並沿著這條鏈傳遞該請求,直到有對象處理它為止。一個純的責任鏈模式要求一個具體的處理者對象只能在兩個行為中選擇一個:一是承擔責任,而是把責任推給下家。不允許出現某一個具體處理者對象在承擔了一部分責任後又 把責任向下傳的情況。
  在一個純的責任鏈模式裡面,一個請求必須被某一個處理者對象所接收;在一個不純的責任鏈模式裡面,一個請求可以最終不被任何接收端對象所接收。
  純的責任鏈模式的實際例子很難找到,一般看到的例子均是不純的責任鏈模式的實現。有些人認為不純的責任鏈根本不是責任鏈模式,這也許是有道理的。但是在實際的系統裡,純的責任鏈很難找到。如果堅持責任鏈不純便不是責任鏈模式,那麼責任鏈模式便不會有太大意義了。裝飾模式又名包裝(Wrapper)模式,裝飾模式以對客戶端透明的方式擴展對象的功能,是繼承關系的一個替代方案。裝飾模式與類繼承的區別:
1) 裝飾模式是一種動態行為,對已經存在類進行隨意組合,而類的繼承是一種靜態的行為,一個類定義成什麼樣的,該類的對象便具有什麼樣的功能,無法動態的改變。
2) 裝飾模式擴展的是對象的功能,不需要增加類的數量,而類繼承擴展是類的功能,在繼承的關系中,如果我們想增加一個對象的功能,我們只能通過繼承關系,在子類中增加兩個方法。
3) 裝飾與繼承比較圖:
4) 裝飾模式是在不改變原類文件和使用繼承的情況下,動態的擴展一個對象的功能,它是通過創建一個包裝對象,也就是裝飾來包裹真是的對象。
5. 裝飾模式把對客戶端的調用委派給被裝飾的類,裝飾模式的關鍵在於這種擴展完全透明的。策略模式定義一組算法,將每個算法都封裝起來,並且使他們之間可以互換。
策略模式的好處在於你可以動態的改變對象的行為。 策略模式屬於對象行為型模式,主要針對一組算法,將每一個算法封裝到具有共同接口的獨立的類中,從而使得它們可以相互替換。策略模式使得算法可以在不影響 到客戶端的情況下發生變化。通常,策略模式適用於當一個應用程序需要實現一種特定的服務或者功能,而且該程序有多種實現方式時使用。適配器模式基於現有類所提供的服務,向客戶提供接口,以滿足客戶的期望。

適配器模式的用意是要改變源的接口,以便於目標接口相容。缺省適配的用意稍有不同,它是為了方便建立一個不平庸的適配器類而提供的一種平庸實現。適配器模式的優點
  更好的復用性
  系統需要使用現有的類,而此類的接口不符合系統的需要。那麼通過適配器模式就可以讓這些功能得到更好的復用。
  更好的擴展性
  在實現適配器功能的時候,可以調用自己開發的功能,從而自然地擴展系統的功能。
適配器模式的缺點
  過多的使用適配器,會讓系統非常零亂,不易整體進行把握。比如,明明看到調用的是A接口,其實內部被適配成了B接口的實現,一個系統如果太多出現這種情況,無異於一場災難。因此如果不是很有必要,可以不使用適配器,而是直接對系統進行重構。迭代器模式提供一種方法訪問一個容器對象中各個元素,而又不暴露該對象的內部細節。在jdk中,與迭代器相關的接口有兩個:Iterator 與 Iterable
Iterator:迭代器,Iterator及其子類通常是迭代器本身的結構與方法;
Iterable:可迭代的,那些想用到迭代器功能的其它類,如AbstractList HashMap等,需要實現該接口。 組合模式將對象組合成樹形結構以表示‘部分-整體’的層次結構。組合模式使得用戶對單個對象和組合對象的使用具有一致性。

對象通過實現(繼承)統一的接口(抽象類),調用者對單一對象和組合對象的操作具有一致性。通過實現組合模式,調用者對組合對象的操作與對單一對象的操作具有一致性。調用者不用關心這是組合對象還是文件,也不用關心組合對象內部的具體結構,就可以調用相關方法,實現功能。觀察者模式定義對象間一種一對多的依賴關系,使得當每一個對象改變狀態,則所有依賴於它的對象都會得到通知並自動更新。

觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態上發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。在JAVA語言的java.util庫裡面,提供了一個Observable類以及一個Observer接口,構成JAVA語言對觀察者模式的支持。門面模式外部與一個子系統的通信必須通過一個統一的門面對象進行。門面模式的優點:
  ●  松散耦合
  門面模式松散了客戶端與子系統的耦合關系,讓子系統內部的模塊能更容易擴展和維護。
  ●  簡單易用
  門面模式讓子系統更加易用,客戶端不再需要了解子系統內部的實現,也不需要跟眾多子系統內部的模塊進行交互,只需要跟門面類交互就可以了。
  ●  更好的劃分訪問層次
  通過合理使用Facade,可以幫助我們更好地劃分訪問的層次。有些方法是對系統外的,有些方法是系統內部使用的。把需要暴露給外部的功能集中到門面中,這樣既方便客戶端使用,也很好地隱藏了內部的細節。備忘錄模式在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。這樣就可以將該對象恢復到原先保存的狀態。備忘錄對象是一個用來存儲另外一個對象內部狀態的快照的對象。備忘錄模式的用意是在不破壞封裝的條件下,將一個對象的狀態捕捉(Capture)住,並外部化,存儲起來,從而可以在將來合適的時候把這個對象還原到存儲起來的狀態。備忘錄模式常常與命令模式和迭代子模式一同使用。訪問者模式封裝某些作用於某種數據結構中各元素的操作,它可以在不改變數據結構的前提下定義作用於這些元素的新的操作。
訪問者模式是對象的行為模式。訪問者模式的目的是封裝一些施加於某種數據結構元素之上的操作。一旦這些操作需要修改的話,接受這個操作的數據結構則可以保持不變。訪問者模式的優點
  好的擴展性
  能夠在不修改對象結構中的元素的情況下,為對象結構中的元素添加新的功能。
  好的復用性
  可以通過訪問者來定義整個對象結構通用的功能,從而提高復用程度。
  分離無關行為
  可以通過訪問者來分離無關的行為,把相關的行為封裝在一起,構成一個訪問者,這樣每一個訪問者的功能都比較單一。

訪問者模式的缺點
  對象結構變化很困難
  不適用於對象結構中的類經常變化的情況,因為對象結構發生了改變,訪問者的接口和訪問者的實現都要發生相應的改變,代價太高。
  破壞封裝
  訪問者模式通常需要對象結構開放內部數據給訪問者和ObjectStructrue,這破壞了對象的封裝性。狀態模式當一個對象的內在狀態改變時允許改變其行為,這個對象看起來像是改變了其類。
狀態模式允許一個對象在其內部狀態改變的時候改變其行為。這個對象看上去就像是改變了它的類一樣。 解釋器模式給定一種語言,定義他的文法的一種表示,並定義一個解釋器,該解釋器使用該表示來解釋語言中句子。 享元模式復用我們內存中已存在的對象,降低系統創建對象實例的性能消耗。

Flyweight在拳擊比賽中指最輕量級,即“蠅量級”或“雨量級”,這裡選擇使用“享元模式”的意譯,是因為這樣更能反映模式的用意。享元模式是對象的結構模式。享元模式以共享的方式高效地支持大量的細粒度對象。享元模式采用一個共享來避免大量擁有相同內容對象的開銷。這種開銷最常見、最直觀的就是內存的損耗。享元對象能做到共享的關鍵是區分內蘊狀態(Internal State)和外蘊狀態(External State)。橋梁模式將抽象和實現解耦,使得兩者可以獨立地變化。
橋梁模式的用意是“將抽象化(Abstraction)與實現化(Implementation)脫耦,使得二者可以獨立地變化”。橋梁模式在Java應用中的一個非常典型的例子就是JDBC驅動器。JDBC為所有的關系型數據庫提供一個通用的界面。一個應用系統動態地選擇一個合適的驅動器,然後通過驅動器向數據庫引擎發出指令。這個過程就是將抽象角色的行為委派給實現角色的過程。

項目

寫了一個Android的項目體現23中設計模式,項目如圖:

\

\


測試代碼:

public void onClickSingleMode(View view) { // 單例
		SingleMode.getInstance();
	}

	public void onClickFactoryMethodModel(View view) {// 工廠方法
		IProduct iProduct = new FactoryMethodModel();
		iProduct.productMethod();
		iProduct = new Tree();
		iProduct.productMethod();
	}

	public void onClickAbstractFactoryModel(View view) {// 抽象工廠
		AbstractFactoryModel.test();
	}

	public void onClickTemplateMethodModel(View view) {// 模版方法模式
		TemplateMethodModel.test();
	}

	public void onClickBuilderMode(View view) {// 建造者模式
		BuilderMode.test();
	}

	public void onClickProxyMode(View view) {// 代理模式
		ProxyMode.test();
	}

	public void onClickCloneMode(View view) {// 原型模式
		CloneMode.test();
	}

	public void onClickIntermediaryModel(View view) {// 中介者模式
		IntermediaryModel.test1();
		IntermediaryModel.test2();
	}

	public void onClickCommandMode(View view) {// 命令模式
		CommandMode.test();
	}

	public void onChainOfResponsibilityModel(View view) {// 責任鏈模式
		ChainOfResponsibilityModel.test();
	}

	public void onClickDecorativeMode(View view) {// 裝飾模式
		DecorativeMode.test();
	}

	public void onClickStrategyMode(View view) {// 策略模式
		StrategyMode.test();
	}

	public void onClickIteratorModel(View view) {// 模式
		IteratorModel.test();
	}

	public void onClickCombinationMode(View view) {// 組合模式
		CombinationMode.test();
	}

	public void onClickObserverMode(View view) {// 觀察者模式
		ObserverMode.test();
	}

	public void onClickWindowMode(View view) {// 門面模式
		WindowMode.test();
	}

	public void onClickMemoMode(View view) {// 備忘錄模式
		MemoMode.test();
	}

	public void onClickVisitorMode(View view) {// 訪問者模式
		VisitorMode.test();
	}

	public void onClickStateModel(View view) {// 狀態模式
		StateModel.test();
	}

	public void onClickParserMode(View view) {// 解釋器模式
		ParserMode.test();
	}

	public void onClickFlyweightMode(View view) {// 享元模式
		FlyweightMode.test();
	}

	public void onClickBridgeMode(View view) {// 橋梁模式
		BridgeMode.test();
	}


總結

如果設計模式在編碼設計生涯中用得極少,主要原因是對設計模式的理解還不夠,認識不到問題的存在。
因為不能正確的分析問題、認識問題,當然也不可能很好的解決問題了。


項目下載

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved