java的23中設計形式,來自博客園,曾經詳細注解過。本站提示廣大學習愛好者:(java的23中設計形式,來自博客園,曾經詳細注解過)文章只能為提供參考,不一定能成為您想要的結果。以下是java的23中設計形式,來自博客園,曾經詳細注解過正文
設計形式(Design Patterns)
——可復用面向對象軟件的根底
設計形式(Design pattern)是一套被重復運用、少數人知曉的、經過火類編目的、代碼設計經歷的總結。運用設計形式是為了可重用代碼、讓代碼更容易被別人了解、保證代碼牢靠性。 毫無疑問,設計形式於己於別人於零碎都是多贏的,設計形式使代碼編制真正工程化,設計形式是軟件工程的基石,好像大廈的一塊塊磚石一樣。項目中合理的運用設計形式可以完滿的處理很多問題,每種形式在如今中都有相應的原理來與之對應,每一個形式描繪了一個在我們四周不時反復發作的問題,以及該問題的中心處理方案,這也是它能被普遍使用的緣由。本章系Java之美[從菜鳥到高手演化]系列之設計形式,我們會以實際與理論相結合的方式來停止本章的學習,希望廣闊順序喜好者,學好設計形式,做一個優秀的軟件工程師!
企業級項目實戰(帶源碼)地址:http://zz563143188.iteye.com/blog/1825168
23種形式java完成源碼下載地址 http://pan.baidu.com/share/link?shareid=372668&uk=4076915866#dir/path=%2F%E5%AD%A6%E4%B9%A0%E6%96%87%E4%BB%B6
一、設計形式的分類
總體來說設計形式分為三大類:
創立型形式,共五種:工廠辦法形式、籠統工廠形式、單例形式、建造者形式、原型形式。
構造型形式,共七種:適配器形式、裝飾器形式、代理形式、外觀形式、橋接形式、組合形式、享元形式。
行為型形式,共十一種:戰略形式、模板辦法形式、察看者形式、迭代子形式、責任鏈形式、命令形式、備忘錄形式、形態形式、訪問者形式、中介者形式、解釋器形式。
其實還有兩類:並發型形式和線程池形式。用一個圖片來全體描繪一下:
二、設計形式的六大准繩
1、開閉准繩(Open Close Principle)
開閉准繩就是說對擴展開放,對修正封閉。在順序需求停止拓展的時分,不能去修正原有的代碼,完成一個熱插拔的效果。所以一句話概括就是:為了使順序的擴展性好,易於維護和晉級。想要到達這樣的效果,我們需求運用接口和籠統類,前面的詳細設計中我們會提到這點。
2、裡氏代換准繩(Liskov Substitution Principle)
裡氏代換准繩(Liskov Substitution Principle LSP)面向對象設計的根本准繩之一。 裡氏代換准繩中說,任何基類可以呈現的中央,子類一定可以呈現。 LSP是承繼復用的基石,只要當衍生類可以交換掉基類,軟件單位的功用不遭到影響時,基類才干真正被復用,而衍生類也可以在基類的根底上添加新的行為。裡氏代換准繩是對“開-閉”准繩的補充。完成“開-閉”准繩的關鍵步驟就是籠統化。而基類與子類的承繼關系就是籠統化的詳細完成,所以裡氏代換准繩是對完成籠統化的詳細步驟的標准。—— From Baidu 百科
3、依賴倒轉准繩(Dependence Inversion Principle)
這個是開閉准繩的根底,詳細內容:針對接口編程,依賴於籠統而不依賴於詳細。
4、接口隔離准繩(Interface Segregation Principle)
這個准繩的意思是:運用多個隔離的接口,比運用單個接口要好。還是一個降低類之間的耦合度的意思,從這兒我們看出,其實設計形式就是一個軟件的設計思想,從大型軟件架構動身,為了晉級和維護方便。所以上文中屢次呈現:降低依賴,降低耦合。
5、迪米特規律(最少知道准繩)(Demeter Principle)
為什麼叫最少知道准繩,就是說:一個實體該當盡量少的與其他實體之間發作互相作用,使得零碎功用模塊絕對獨立。
6、分解復用准繩(Composite Reuse Principle)
准繩是盡量運用分解/聚合的方式,而不是運用承繼。
三、Java的23中設計形式
從這一塊開端,我們詳細引見Java中23種設計形式的概念,使用場景等狀況,並結合他們的特點及設計形式的准繩停止剖析。
1、工廠辦法形式(Factory Method)
工廠辦法形式分為三種:
11、普通工廠形式,就是樹立一個工廠類,對完成了同一接口的一些類停止實例的創立。首先看下關系圖:
舉例如下:(我們舉一個發送郵件和短信的例子)
首先,創立二者的共同接口:
[java] view plaincopy
- public interface Sender {
- public void Send();
- }
其次,創立完成類:
[java] view plaincopy
- public class MailSender implements Sender {
- @Override
- public void Send() {
- System.out.println("this is mailsender!");
- }
- }
[java] view plaincopy
- public class SmsSender implements Sender {
- @Override
- public void Send() {
- System.out.println("this is sms sender!");
- }
- }
最後,建工廠類:
[java] view plaincopy
- public class SendFactory {
- public Sender produce(String type) {
- if ("mail".equals(type)) {
- return new MailSender();
- } else if ("sms".equals(type)) {
- return new SmsSender();
- } else {
- System.out.println("請輸出正確的類型!");
- return null;
- }
- }
- }
我們來測試下:
- public class FactoryTest {
- public static void main(String[] args) {
- SendFactory factory = new SendFactory();
- Sender sender = factory.produce("sms");
- sender.Send();
- }
- }
輸入:this is sms sender!
22、多個工廠辦法形式,是對普通工廠辦法形式的改良,在普通工廠辦法形式中,假如傳遞的字符串出錯,則不能正確創立對象,而多個工廠辦法形式是提供多個工廠辦法,辨別創立對象。關系圖:
將下面的代碼做下修正,改動下SendFactory類就行,如下:
[java] view plaincopypublic class SendFactory {
public Sender produceMail(){
- return new MailSender();
- }
- public Sender produceSms(){
- return new SmsSender();
- }
- }
測試類如下:
[java] view plaincopy
- public class FactoryTest {
- public static void main(String[] args) {
- SendFactory factory = new SendFactory();
- Sender sender = factory.produceMail();
- sender.Send();
- }
- }
輸入:this is mailsender!
33、靜態工廠辦法形式,將下面的多個工廠辦法形式裡的辦法置為靜態的,不需求創立實例,直接調用即可。
[java] view plaincopy
- public class SendFactory {
- public static Sender produceMail(){
- return new MailSender();
- }
- public static Sender produceSms(){
- return new SmsSender();
- }
- }
[java] view plaincopy
- public class FactoryTest {
- public static void main(String[] args) {
- Sender sender = SendFactory.produceMail();
- sender.Send();
- }
- }
輸入:this is mailsender!
總體來說,工廠形式合適:但凡呈現了少量的產品需求創立,並且具有共同的接口時,可以經過工廠辦法形式停止創立。在以上的三種形式中,第一種假如傳入的字符串有誤,不能正確創立對象,第三種絕對於第二種,不需求實例化工廠類,所以,大少數狀況下,我們會選用第三種——靜態工廠辦法形式。
2、籠統工廠形式(Abstract Factory)
工廠辦法形式有一個問題就是,類的創立依賴工廠類,也就是說,假如想要拓展順序,必需對工廠類停止修正,這違犯了閉包准繩,所以,從設計角度思索,有一定的問題,如何處理?就用到籠統工廠形式,創立多個工廠類,這樣一旦需求添加新的功用,直接添加新的工廠類就可以了,不需求修正之前的代碼。由於籠統工廠不太好了解,我們先看看圖,然後就和代碼,就比擬容易了解。
請看例子:
[java] view plaincopy
- public interface Sender {
- public void Send();
- }
兩個完成類:
[java] view plaincopy
- public class MailSender implements Sender {
- @Override
- public void Send() {
- System.out.println("this is mailsender!");
- }
- }
[java] view plaincopy
- public class SmsSender implements Sender {
- @Override
- public void Send() {
- System.out.println("this is sms sender!");
- }
- }
兩個工廠類:
[java] view plaincopy
- public class SendMailFactory implements Provider {
- @Override
- public Sender produce(){
- return new MailSender();
- }
- }
[java] view plaincopy
- public class SendSmsFactory implements Provider{
- @Override
- public Sender produce() {
- return new SmsSender();
- }
- }
在提供一個接口:
[java] view plaincopy
- public interface Provider {
- public Sender produce();
- }
測試類:
[java] view plaincopy
- public class Test {
- public static void main(String[] args) {
- Provider provider = new SendMailFactory();
- Sender sender = provider.produce();
- sender.Send();
- }
- }
其實這個形式的益處就是,假如你如今想添加一個功用:發即時信息,則只需做一個完成類,完成Sender接口,同時做一個工廠類,完成Provider接口,就OK了,無需去改動現成的代碼。這樣做,拓展性較好!
3、單例形式(Singleton)
單例對象(Singleton)是一種常用的設計形式。在Java使用中,單例對象能保證在一個JVM中,該對象只要一個實例存在。這樣的形式有幾個益處:
1、某些類創立比擬頻繁,關於一些大型的對象,這是一筆很大的零碎開支。
2、省去了new操作符,降低了零碎內存的運用頻率,加重GC壓力。
3、有些類如買賣所的中心買賣引擎,控制著買賣流程,假如該類可以創立多個的話,零碎完全亂了。(比方一個軍隊呈現了多個司令員同時指揮,一定會亂成一團),所以只要運用單例形式,才干保證中心買賣服務器獨立控制整個流程。
首先我們寫一個復雜的單例類:
[java] view plaincopy
- public class Singleton {
- /* 持有公有靜態實例,避免被援用,此處賦值為null,目的是完成延遲加載,延遲加載,在實踐運用到對象時,才加載相應的類,否則只是聲明,進步功能*/
- private static Singleton instance = null;
- /* 公有結構辦法,避免被實例化 */
- private Singleton() {
- }
- /* 靜態工廠辦法,創立實例 */
- public static Singleton getInstance() {
- if (instance == null) {
- instance = new Singleton();
- }
- return instance;
- }
- /* 假如該對象被用於序列化,可以保證對象在序列化前後堅持分歧 */
- public Object readResolve() {
- return instance;
- }
- }
這個類可以滿足根本要求,但是,像這樣毫無線程平安維護的類,假如我們把它放入多線程的環境下,一定就會呈現問題了,如何處理?我們首先會想到對getInstance辦法加synchronized關鍵字,如下:
[java] view plaincopy
- public static synchronized Singleton getInstance() {
- if (instance == null) {
- instance = new Singleton();
- }
- return instance;
- }
但是,synchronized關鍵字鎖住的是這個對象,這樣的用法,在功能上會有所下降,由於每次調用getInstance(),都要對對象上鎖,現實上,只要在第一次創立對象的時分需求加鎖,之後就不需求了,所以,這個中央需求改良。我們改成上面這個:
[java] view plaincopy
- public static Singleton getInstance() {
- if (instance == null) {
- synchronized (instance) {
- if (instance == null) {
- instance = new Singleton();
- }
- }
- }
- return instance;
- }
似乎處理了之前提到的問題,將synchronized關鍵字加在了外部,也就是說當調用的時分是不需求加鎖的,只要在instance為null,並創立對象的時分才需求加鎖,功能有一定的提升。但是,這樣的狀況,還是有能夠有問題的,看上面的狀況:在Java指令中創立對象和賦值操作是分開停止的,也就是說instance = new Singleton();語句是分兩步執行的。但是JVM並不保證這兩個操作的先後順序,也就是說有能夠JVM會為新的Singleton實例分配空間,然後直接賦值給instance成員,然後再去初始化這個Singleton實例。這樣就能夠出錯了,我們以A、B兩個線程為例:
a>A、B線程同時進入了第一個if判別
b>A首先進入synchronized塊,由於instance為null,所以它執行instance = new Singleton();
c>由於JVM外部的優化機制,JVM先畫出了一些分配給Singleton實例的空白內存,並賦值給instance成員(留意此時JVM沒有開端初始化這個實例),然後A分開了synchronized塊。
d>B進入synchronized塊,由於instance此時不是null,因而它馬上分開了synchronized塊並將後果前往給調用該辦法的順序。
e>此時B線程計劃運用Singleton實例,卻發現它沒有被初始化,於是錯誤發作了。
所以順序還是有能夠發作錯誤,其實順序在運轉進程是很復雜的,從這點我們就可以看出,尤其是在寫多線程環境下的順序更有難度,有應戰性。我們對該順序做進一步優化:
[java] view plaincopy
- private static class SingletonFactory{
- private static Singleton instance = new Singleton();
- }
- public static Singleton getInstance(){
- return SingletonFactory.instance;
- }
實踐狀況是,單例形式運用外部類來維護單例的完成,JVM外部的機制可以保證當一個類被加載的時分,這個類的加載進程是線程互斥的。這樣當我們第一次調用getInstance的時分,JVM可以幫我們保證instance只被創立一次,並且會保證把賦值給instance的內存初始化終了,這樣我們就不必擔憂下面的問題。同時該辦法也只會在第一次調用的時分運用互斥機制,這樣就處理了低功能問題。這樣我們暫時總結一個完滿的單例形式:
[java] view plaincopy
- public class Singleton {
-
- /* 公有結構辦法,避免被實例化 */
- private Singleton() {
- }
-
- /* 此處運用一個外部類來維護單例 */
- private static class SingletonFactory {
- private static Singleton instance = new Singleton();
- }
-
- /* 獲取實例 */
- public static Singleton getInstance() {
- return SingletonFactory.instance;
- }
-
- /* 假如該對象被用於序列化,可以保證對象在序列化前後堅持分歧 */
- public Object readResolve() {
- return getInstance();
- }
- }
其實說它完滿,也不一定,假如在結構函數中拋出異常,實例將永遠得不到創立,也會出錯。所以說,非常完滿的東西是沒有的,我們只能依據實踐狀況,選擇最合適自己使用場景的完成辦法。也有人這樣完成:由於我們只需求在創立類的時分停止同步,所以只需將創立和getInstance()分開,獨自為創立加synchronized關鍵字,也是可以的:
[java] view plaincopy
- public class SingletonTest {
- Private static SingletonTest instance = null;
- private SingletonTest() {
- }
- private static synchronized void syncInit() {
- if (instance == null) {
- instance = new SingletonTest();
- }
- }
- public static SingletonTest getInstance() {
- if (instance == null) {
- syncInit();
- }
- return instance;
- }
- }
思索功能的話,整個順序只需創立一次實例,所以功能也不會有什麼影響。
補充:采用"影子實例"的方法為單例對象的屬性同步更新
[java] view plaincopy
經過單例形式的學習通知我們:
- public class SingletonTest {
- private static SingletonTest instance = null;
- private Vector properties = null;
- public Vector getProperties() {
- return properties;
- }
- private SingletonTest() {
- }
- private static synchronized void syncInit() {
- if (instance == null) {
- instance = new SingletonTest();
- }
- }
- public static SingletonTest getInstance() {
- //假如實例instance為null則調用創立辦法停止創立
- if (instance == null) {
- syncInit();
- }
- return instance;
- }
- public void updateProperties() {
- SingletonTest shadow = new SingletonTest();
- properties = shadow.getProperties();
- }
- }
1、單例形式了解起來復雜,但是詳細完成起來還是有一定的難度。
2、synchronized關鍵字鎖定的是對象,在用的時分,一定要在恰當的中央運用(留意需求運用鎖的對象和進程,能夠有的時分並不是整個對象及整個進程都需求鎖)。
到這兒,單例形式根本曾經講完了,開頭處,筆者忽然想到另一個問題,就是采用類的靜態辦法,完成單例形式的效果,也是可行的,此處二者有什麼不同?
首先,靜態類不能完成接口。(從類的角度說是可以的,但是那樣就毀壞了靜態了。由於接口中不允許有static修飾的辦法,所以即便完成了也是非靜態的)
其次,單例可以被延遲初始化,靜態類普通在第一次加載是初始化。之所以延遲加載,是由於有些類比擬龐大,所以延遲加載有助於提升功能。
再次,單例類可以被承繼,他的辦法可以被覆寫。但是靜態類外部辦法都是static,無法被覆寫。
最後一點,單例類比擬靈敏,畢竟從完成上只是一個普通的Java類,只需滿足單例的根本需求,你可以在外面為所欲為的完成一些其它功用,但是靜態類不行。從下面這些概括中,根本可以看出二者的區別,但是,從另一方面講,我們下面最後完成的那個單例形式,外部就是用一個靜態類來完成的,所以,二者有很大的關聯,只是我們思索問題的層面不同罷了。兩種思想的結合,才干培養出完滿的處理方案,好像HashMap采用數組+鏈表來完成一樣,其實生活中很多事情都是這樣,單用不同的辦法來處置問題,總是有優點也有缺陷,最完滿的辦法是,結合各個辦法的優點,才干最好的處理問題!
4、建造者形式(Builder)
工廠類形式提供的是創立單個類的形式,而建造者形式則是將各種產品集中起來停止管理,用來創立復合對象,所謂復合對象就是指某個類具有不同的屬性,其實建造者形式就是後面籠統工廠形式和最後的Test結合起來失掉的。我們看一下代碼:
還和後面一樣,一個Sender接口,兩個完成類MailSender和SmsSender。最後,建造者類如下:
- Public class Builder {
- private List<Sender> list = new ArrayList<Sender>();
- public void produceMailSender(int count){
- for(int i=0; i<count; i++){
- list.add(new MailSender());
- }
- }
- Public void produceSmsSender(int count){
- for(int i=0; i<count; i++){
- list.add(new SmsSender());
- }
- }
- }
測試類:
[java] view plaincopy
- public class Test {
- public static void main(String[] args) {
- Builder builder = new Builder();
- builder.produceMailSender(10);
- }
- }
從這點看出,建造者形式將很多功用集成到一個類裡,這個類可以發明出比擬復雜的東西。所以與工廠形式的區別就是:工廠形式關注的是創立單個產品,而建造者形式則關注創立契合對象,多個局部。因而,是選擇工廠形式還是建造者形式,依實踐狀況而定。
5、原型形式(Prototype)
原型形式雖然是創立型的形式,但是與工程形式沒有關系,從名字即可看出,該形式的思想就是將一個對象作為原型,對其停止復制、克隆,發生一個和原對象相似的新對象。本小結會經過對象的復制,停止解說。在Java中,復制對象是經過clone()完成的,先創立一個原型類:
- public class Prototype implements Cloneable {
- public Object clone() throws CloneNotSupportedException {
- Prototype proto = (Prototype) super.clone();
- return proto;
- }
- }
很復雜,一個原型類,只需求完成Cloneable接口,覆寫clone辦法,此處clone辦法可以改成恣意的稱號,由於Cloneable接口是個空接口,你可以恣意定義完成類的辦法名,如cloneA或許cloneB,由於此處的重點是super.clone()這句話,super.clone()調用的是Object的clone()辦法,而在Object類中,clone()是native(不是Java自身的辦法)的,詳細怎樣完成,我會在另一篇文章中,關於解讀Java中本中央法的調用,此處不再深究。在這兒,我將結合對象的淺復制和深復制來說一下,首先需求理解對象深、淺復制的概念:
淺復制:將一個對象復制後,根本數據類型的變量都會重新創立,而援用類型,指向的還是原對象所指向的。
深復制:將一個對象復制後,不管是根本數據類型還有援用類型,都是重新創立的。復雜來說,就是深復制停止了完全徹底的復制,而淺復制不徹底。
此處,寫一個深淺復制的例子:
[java] view plaincopy
- public class Prototype implements Cloneable, Serializable {
- private static final long serialVersionUID = 1L;
- private String string;
- private SerializableObject obj;
- /* 淺復制 */
- public Object clone() throws CloneNotSupportedException {
- Prototype proto = (Prototype) super.clone();
- return proto;
- }
- /* 深復制 */
- public Object deepClone() throws IOException, ClassNotFoundException {
- /* 寫入以後對象的二進制流 */
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(bos);
- oos.writeObject(this);
- /* 讀出二進制流發生的新對象 */
- ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
- ObjectInputStream ois = new ObjectInputStream(bis);
- return ois.readObject();
- }
- public String getString() {
- return string;
- }
- public void setString(String string) {
- this.string = string;
- }
- public SerializableObject getObj() {
- return obj;
- }
- public void setObj(SerializableObject obj) {
- this.obj = obj;
- }
- }
- class SerializableObject implements Serializable {
- private static final long serialVersionUID = 1L;
- }
要完成深復制,需求采用流的方式讀入以後對象的二進制輸出,再寫出二進制數據對應的對象。
===================================================
適配器形式:
我們接著討論設計形式,上篇文章我講完了5種創立型形式,這章開端,我將講下7種構造型形式:適配器形式、裝飾形式、代理形式、外觀形式、橋接形式、組合形式、享元形式。其中對象的適配器形式是各種形式的來源,我們看上面的圖:
適配器形式將某個類的接口轉換成客戶端希冀的另一個接口表示,目的是消弭由於接口不婚配所形成的類的兼容性問題。次要分為三類:類的適配器形式、對象的適配器形式、接口的適配器形式。首先,我們來看看類的適配器形式,先看類圖:
中心思想就是:有一個Source類,擁有一個辦法,待適配,目的接口是Targetable,經過Adapter類,將Source的功用擴展到Targetable裡,看代碼:
[java] view plaincopy
- public class Source {
- public void method1() {
- System.out.println("this is original method!");
- }
- }
[java] view plaincopy
- public interface Targetable {
- /* 與原類中的辦法相反 */
- public void method1();
- /* 新類的辦法 */
- public void method2();
- }
[java] view plaincopy
- public class Adapter extends Source implements Targetable {
- /*辦法一由被承繼的Source完成
- 辦法二由承繼的類完成*/
- @Override
- public void method2() {
- System.out.println("this is the targetable method!");
- }
- }
Adapter類承繼Source類,完成Targetable接口,上面是測試類:
[java] view plaincopy
- public class AdapterTest {
- public static void main(String[] args) {
- Targetable target = new Adapter();
- target.method1();
- target.method2();
- }
- }
輸入:
this is original method!
this is the targetable method!
這樣Targetable接口的完成類就具有了Source類的功用。
對象的適配器形式
根本思緒和類的適配器形式相反,只是將Adapter類作修正,這次不承繼Source類,而是持有Source類的實例,以到達處理兼容性的問題。看圖:
只需求修正Adapter類的源碼即可:
[java] view plaincopy
- public class Wrapper implements Targetable {
- Private Source source;
- public Wrapper(Source source){
- super();
- this.source = source;
- }
- @Override
- public void method2() {
- System.out.println("this is the targetable method!");
- }
- @Override
- public void method1() {
- source.method1();
- }
- }
測試類:
[java] view plaincopy
- public class AdapterTest {
- public static void main(String[] args) {
- Source source = new Source();
- Targetable target = new Wrapper(source);
- target.method1();
- target.method2();
- }
- }
輸入與第一種一樣,只是適配的辦法不同而已。
接口的適配器形式
第三種適配器形式是接口的適配器形式,接口的適配器是這樣的:有時我們寫的一個接口中有多個籠統辦法,當我們寫該接口的完成類時,必需完成該接口的一切辦法,這分明有時比擬糜費,由於並不是一切的辦法都是我們需求的,有時只需求某一些,此處為理解決這個問題,我們引入了接口的適配器形式,借助於一個籠統類(籠統類中可以沒有籠統的辦法,但是假如一個類中有籠統辦法,那麼這個類一定是籠統類),該籠統類完成了該接口,完成了一切的辦法,而我們和睦原始的接口打交道,只和該籠統類獲得聯絡,所以我們寫一個類,承繼該籠統類,重寫我們需求的辦法就行。看一下類圖:
這個很好了解,在實踐開發中,我們也常會遇到這種接口中定義了太多的辦法,致使於有時我們在一些完成類中並不是都需求。看代碼:
[java] view plaincopy
- public interface Sourceable {
- public void method1();
- public void method2();
- }
籠統類Wrapper2:(wrapper:包裝紙,封套,封皮)
[java] view plaincopy
- public abstract class Wrapper2 implements Sourceable{
- public void method1(){}
- public void method2(){}
- }
[java] view plaincopy
- public class SourceSub1 extends Wrapper2 {
- public void method1(){
- System.out.println("the sourceable interface's first Sub1!");
- }
- }
[java] view plaincopy
- public class SourceSub2 extends Wrapper2 {
- public void method2(){
- System.out.println("the sourceable interface's second Sub2!");
- }
- }
[java] view plaincopy
- public class WrapperTest {
- public static void main(String[] args) {
- Sourceable source1 = new SourceSub1();
- Sourceable source2 = new SourceSub2();
- source1.method1();
- source1.method2();
- source2.method1();
- source2.method2();
- }
- }
測試輸入:
the sourceable interface's first Sub1!
the sourceable interface's second Sub2!
到達了我們的效果!
講了這麼多,總結一下三種適配器形式的使用場景:
類的適配器形式:當希望將一個類轉換成滿足另一個新接口的類時,可以運用類的適配器形式,創立一個新類,承繼原有的類,完成新的接口即可。
對象的適配器形式:當希望將一個對象轉換成滿足另一個新接口的對象時,可以創立一個Wrapper類,持有原類的一個實例,在Wrapper類的辦法中,調用實例的辦法就行。
接口的適配器形式:當不希望完成一個接口中一切的辦法時,可以創立一個籠統類Wrapper,完成一切辦法,我們寫別的類的時分,承繼籠統類即可。
7、裝飾形式(Decorator:室內裝飾師,油漆匠)
望文生義,裝飾形式就是給一個對象添加一些新的功用,而且是靜態的,要求裝飾對象和被裝飾對象完成同一個接口,裝飾對象持有被裝飾對象的實例,關系圖如下:
Source類是被裝飾類,Decorator類是一個裝飾類,可以為Source類靜態的添加一些功用,代碼如下:
[java] view plaincopy
- public interface Sourceable {
- public void method();
- }
[java] view plaincopy
- public class Source implements Sourceable {
- @Override
- public void method() {
- System.out.println("the original method!");
- }
- }
[java] view plaincopy
- public class Decorator implements Sourceable {
- private Sourceable source;
- public Decorator(Sourceable source){
- super();
- this.source = source;
- }
- @Override
- public void method() {
- System.out.println("before decorator!");
- source.method();
- System.out.println("after decorator!");
- }
- }
測試類:
[java] view plaincopy
- public class DecoratorTest {
- public static void main(String[] args) {
- Sourceable source = new Source();
- Sourceable obj = new Decorator(source);
- obj.method();
- }
- }
輸入:
before decorator!
the original method!
after decorator!
裝飾器形式的使用場景:
1、需求擴展一個類的功用。
2、靜態(創立的時分,把被創裝飾的類的對象作為結構辦法的參數傳遞到裝飾類中)的為一個對象添加功用,而且還能靜態撤銷。(承繼不能做到這一點,承繼的功用是靜態的,不能靜態增刪。)
缺陷:發生過多類似的對象,不易排錯!
8、代理形式(Proxy)
其實每個形式稱號就標明了該形式的作用,代理形式就是多一個代理類出來,替原對象停止一些操作,比方我們在租房子的時分回去找中介,為什麼呢?由於你對該地域房屋的信息掌握的不夠片面,希望找一個更熟習的人去幫你做,此處的代理就是這個意思。再如我們有的時分打官司,我們需求請律師,由於律師在法律方面有特長,可以替我們停止操作,表達我們的想法。先來看看關系圖:
依據上文的論述,代理形式就比擬容易的了解了,我們看下代碼:
- public interface Sourceable {
- public void method();
- }
[java] view plaincopy
- public class Source implements Sourceable {
- @Override
- public void method() {
- System.out.println("the original method!");
- }
- }
[java] view plaincopy
- public class Proxy implements Sourceable {
- private Source source;
- Public Proxy(){
- super();
- this.source = new Source();
- }
- @Override
- Public void method() {
- before();
- source.method();
- atfer();
- }
- private void atfer() {
- System.out.println("after proxy!");
- }
- private void before() {
- System.out.println("before proxy!");
- }
- }
測試類:
[java] view plaincopy
- public class ProxyTest {
- public static void main(String[] args) {
- Sourceable source = new Proxy();
- source.method();
- }
- }
[java] view plaincopy
輸入:
before proxy!
the original method!
after proxy!
代理形式的使用場景:
假如已有的辦法在運用的時分需求對原有的辦法停止改良,此時有兩種方法:
1、修正原有的辦法來順應。這樣違背了“對擴展開放,對修正封閉”的准繩。
2、就是采用一個代理類調用原有的辦法,且對發生的後果停止控制。這種辦法就是代理形式。
運用代理形式,可以將功用劃分的愈加明晰,有助於前期維護!
補充:
被代理的類要完成了接口,然後創立一個完成了異樣接口的代理類,在代理類中先對原有的類停止前置或後置加強(在代理類的成員地位聲明被代理類的變量,然後結構辦法中創立被代理類的實例,並把該實例的援用傳遞給成員變量,最後創立詳細的加強辦法,並在代理類完成的詳細要被加強的辦法中運用被代理類的對象調用被加強的辦法,並在調用的後面或前面添加詳細的加強辦法);
9、外觀形式(Facade:表面; 修建物的正面; 虛假,假象)
外觀形式是為理解決類與類之間的依賴關系的,像spring一樣,可以將類和類之間的關系配置到配置文件中,而外觀形式就是將他們的關系放在一個Facade類中,降低了類類之間的耦合度,該形式中沒有觸及到接口,看下類圖:(我們以一個計算機的啟動進程為例)
我們先看下完成類:
[java] view plaincopy
- public class CPU {
- public void startup(){
- System.out.println("cpu startup!");
- }
- public void shutdown(){
- System.out.println("cpu shutdown!");
- }
- }
[java] view plaincopy
- public class Memory {
- public void startup(){
- System.out.println("memory startup!");
- }
- public void shutdown(){
- System.out.println("memory shutdown!");
- }
- }
[java] view plaincopy
- public class Disk {
- public void startup(){
- System.out.println("disk startup!");
- }
- public void shutdown(){
- System.out.println("disk shutdown!");
- }
- }
[java] view plaincopy
- public class Computer {
- private CPU cpu;
- private Memory memory;
- private Disk disk;
- public Computer(){
- cpu = new CPU();
- memory = new Memory();
- disk = new Disk();
- }
- public void startup(){
- System.out.println("start the computer!");
- cpu.startup();
- memory.startup();
- disk.startup();
- System.out.println("start computer finished!");
- }
- public void shutdown(){
- System.out.println("begin to close the computer!");
- cpu.shutdown();
- memory.shutdown();
- disk.shutdown();
- System.out.println("computer closed!");
- }
- }
User類如下:
[java] view plaincopy
- public class User {
- public static void main(String[] args) {
- Computer computer = new Computer();
- computer.startup();
- computer.shutdown();
- }
- }
輸入:
start the computer!
cpu startup!
memory startup!
disk startup!
start computer finished!
begin to close the computer!
cpu shutdown!
memory shutdown!
disk shutdown!
computer closed!
假如我們沒有Computer類,那麼,CPU、Memory、Disk他們之間將會互相持有實例,發生關系,這樣會形成嚴重的依賴,修正一個類,能夠會帶來其他類的修正,這不是我們想要看到的,有了Computer類,他們之間的關系被放在了Computer類裡,這樣就起到理解耦的作用,這,就是外觀形式!
10、橋接形式(Bridge)
橋接形式就是把事物和其詳細完成分開,使他們可以各自獨立的變化。橋接的意圖是:將籠統化與完成化解耦,使得二者可以獨立變化,像我們常用的JDBC橋DriverManager一樣,JDBC停止銜接數據庫的時分,在各個數據庫之間停止切換(都是完成了同一個接口標准,只需調用接口就可以操作詳細的完成類的功用),根本不需求動太多的代碼,甚至絲毫不必動,緣由就是JDBC提供一致接口,每個數據庫提供各自的完成,用一個叫做數據庫驅動的順序來橋接就行了。我們來看看關系圖:
完成代碼:
先定義接口:
[java] view plaincopy
- public interface Sourceable {
- public void method();
- }
辨別定義兩個完成類:
[java] view plaincopy
- public class SourceSub1 implements Sourceable {
- @Override
- public void method() {
- System.out.println("this is the first sub!");
- }
- }
[java] view plaincopy
- public class SourceSub2 implements Sourceable {
- @Override
- public void method() {
- System.out.println("this is the second sub!");
- }
- }
定義一個橋,持有Sourceable的一個實例:
[java] view plaincopy
- public abstract class Bridge {
- private Sourceable source;
- public void method(){
- source.method();
- }
- public Sourceable getSource() {
- return source;
- }
- public void setSource(Sourceable source) {
- this.source = source;
- }
- }
[java] view plaincopy
- public class MyBridge extends Bridge {
- public void method(){
- getSource().method();
- }
- }
測試類:
[java] view plaincopy
- public class BridgeTest {
- public static void main(String[] args) {
- Bridge bridge = new MyBridge();
- /*調用第一個對象*/
- Sourceable source1 = new SourceSub1();
- bridge.setSource(source1);
- bridge.method();
- /*調用第二個對象只需向橋中注入接口的完成類,橋就可以對完成接口的類停止一致管理*/
- Sourceable source2 = new SourceSub2();
- bridge.setSource(source2);
- bridge.method();
- }
- }
output:
this is the first sub!
this is the second sub!
這樣,就經過對Bridge類的調用,完成了對接口Sourceable的完成類SourceSub1和SourceSub2的調用。接上去我再畫個圖,大家就應該明白了,由於這個圖是我們JDBC銜接的原理,無數據庫學習根底的,一結合就都懂了。
11、組合形式(Composite)
組合形式有時又叫局部-全體形式在處置相似樹形構造的問題時比擬方便,看看關系圖:
直接來看代碼:
[java] view plaincopy
運用場景:將多個對象組合在一同停止操作,常用於表示樹形構造中,例如二叉樹,數等。
- public class TreeNode {
- private String name;
- private TreeNode parent;
- private Vector<TreeNode> children = new Vector<TreeNode>();
- public TreeNode(String name){
- this.name = name;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public TreeNode getParent() {
- return parent;
- }
- public void setParent(TreeNode parent) {
- this.parent = parent;
- }
- //添加孩子節點
- public void add(TreeNode node){
- children.add(node);
- }
- //刪除孩子節點
- public void remove(TreeNode node){
- children.remove(node);
- }
- //獲得孩子節點
- public Enumeration<TreeNode> getChildren(){
- //調用vectory的elemets辦法,前往枚舉;
- return children.elements();
- }
- }
[java] view plaincopy
- public class Tree {
- TreeNode root = null;
- public Tree(String name) {
- root = new TreeNode(name);
- }
- public static void main(String[] args) {
- Tree tree = new Tree("A");
- TreeNode nodeB = new TreeNode("B");
- TreeNode nodeC = new TreeNode("C");
- nodeB.add(nodeC);
- tree.root.add(nodeB);
- System.out.println("build the tree finished!");
- }
- }
12、享元形式(Flyweight:次最輕量級的拳擊選手)
享元形式的次要目的是完成對象的共享,即共享池,當零碎中對象多的時分可以增加內存的開支,通常與工廠形式一同運用。
FlyWeightFactory擔任創立和管理享元單元,當一個客戶端懇求時,工廠需求反省以後對象池中能否有契合條件的對象,假如有,就前往曾經存在的對象,假如沒有,則創立一個新對象,FlyWeight是超類。一提到共享池,我們很容易聯想到Java外面的JDBC銜接池,想想每個銜接的特點,我們不難總結出:適用於作共享的一些個對象,他們有一些共有的屬性,就拿數據庫銜接池來說,url、driverClassName、username、password及dbname,這些屬性關於每個銜接來說都是一樣的,所以就合適用享元形式來處置,建一個工廠類,將上述相似屬性作為外部數據,其它的作為內部數據,在辦法調用時,當做參數傳出去,這樣就節省了空間,增加了實例的數量。
看個例子:
看下數據庫銜接池的代碼:
[java] view plaincopy
- public class ConnectionPool {
- // 定義vector變量用於寄存獲取的銜接對象
- private Vector<Connection> pool;
- /*私有屬性*/
- private String url = "jdbc:mysql://localhost:3306/test"; //銜接數據庫的url
- private String username = "root";//數據庫用戶名
- private String password = "root";//用戶密碼
- private String driverClassName = "com.mysql.jdbc.Driver"; //數據庫驅動
- private int poolSize = 100; //初始化銜接數量
- private static ConnectionPool instance = null;//
- Connection conn = null;
- /*結構辦法,做一些初始化任務*/
- private ConnectionPool() {
- //創立vector實例,用於寄存銜接對象
- pool = new Vector<Connection>(poolSize);
- //循環創立銜接對象
- for (int i = 0; i < poolSize; i++) {
- try {
- //注冊數據庫驅動
- Class.forName(driverClassName);
- //獲取銜接對象
- conn = DriverManager.getConnection(url, username, password);
- //將獲取到的銜接對象放入到銜接池中
- pool.add(conn);
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
- /* 前往銜接到銜接池,辦法是同步的,在一個線程執行同步辦法時,其他線程無法執行該對象的其他任何同步辦法 */
- public synchronized void release(Connection conn) {
- pool.add(conn);
- }
- /* 前往銜接池中的一個數據庫銜接對象*/
- public synchronized Connection getConnection() {
- if (pool.size() > 0) { //判別銜接池中能否有銜接對象
- Connection conn = pool.get(0);//假如有,則從銜接池中獲取一個銜接對象
- pool.remove(conn); //將獲取到的銜接對象從銜接池中移除,防止同一個銜接對象被反復獲取
- return conn;
- } else {
- return null;
- }
- }
- }
經過銜接池的管理,完成了數據庫銜接的共享,不需求每一次都重新創立銜接,節省了數據庫重新創立的開支,提升了零碎的功能!本章解說了7種構造型形式,由於篇幅的問題,剩下的11種行為型形式,本章是關於設計形式的最後一講,會講到第三種設計形式——行為型形式,共11種:戰略形式、模板辦法形式、察看者形式、迭代子形式、責任鏈形式、命令形式、備忘錄形式、形態形式、訪問者形式、中介者形式、解釋器形式。這段時間不斷在寫關於設計形式的東西,終於寫到一半了,寫博文是個很費時間的東西,由於我得為讀者擔任,不管是圖還是代碼還是表述,都希望能盡量寫清楚,以便讀者了解,我想不管是我還是讀者,都希望看到高質量的博文出來,從我自己動身,我會不斷堅持下去,不時更新,源源動力來自於讀者冤家們的不時支持,我會盡自己的努力,寫好每一篇文章!希望大家能不時給出意見和建議,共同打造完滿的博文!
先來張圖,看看這11中形式的關系:
第一類:經過父類與子類的關系停止完成。
第二類:兩個類之間。
第三類:類的形態。
第四類:經過兩頭類
13、戰略形式(strategy)
戰略形式定義了一系列算法,並將每個算法封裝起來,使他們可以互相交換,且算法的變化不會影響到運用算法的客戶。需求設計一個接口,為一系列完成類提供一致的辦法,多個完成類完成該接口,設計一個籠統類(可有可無,屬於輔佐類,該類提供一些通用的辦法,供子類直接調用,子類不必再重寫,進步代碼的重用性),提供輔佐函數,關系圖如下:
圖中ICalculator提供一致的辦法,
AbstractCalculator是輔佐類,提供輔佐辦法,接上去,順次完成下每個類:
[java] view plaincopy
- public interface ICalculator {
- public int calculate(String exp);
- }
輔佐類:
[java] view plaincopy
- public abstract class AbstractCalculator {
- public int[] split(String exp,String opt){
- String array[] = exp.split(opt);
- int arrayInt[] = new int[2];
- arrayInt[0] = Integer.parseInt(array[0]);
- arrayInt[1] = Integer.parseInt(array[1]);
- return arrayInt;
- }
- }
三個完成類:
[java] view plaincopy
- public class Plus extends AbstractCalculator implements ICalculator {
- @Override
- public int calculate(String exp) {
- //“+”是轉移字符,需求運用“\\”對其停止本義
- int arrayInt[] = split(exp,"\\+");
- return arrayInt[0]+arrayInt[1];
- }
- }
[java] view plaincopy
- public class Minus extends AbstractCalculator implements ICalculator {
- @Override
- public int calculate(String exp) {
- int arrayInt[] = split(exp,"-");
- return arrayInt[0]-arrayInt[1];
- }
- }
[java] view plaincopy
- public class Multiply extends AbstractCalculator implements ICalculator {
- @Override
- public int calculate(String exp) {
- int arrayInt[] = split(exp,"\\*");
- return arrayInt[0]*arrayInt[1];
- }
- }
復雜的測試類:
[java] view plaincopy
- public class StrategyTest {
- public static void main(String[] args) {
- String exp = "2+8";
- ICalculator cal = new Plus();
- int result = cal.calculate(exp);
- System.out.println(result);
- }
- }
首先一致接口:
輸入:10
戰略形式的決議權在用戶,零碎自身提供不同算法的完成,新增或許刪除算法,對各種算法做封裝。因而,戰略形式多用在算法決策零碎中,內部用戶只需求決議用哪個算法即可。
14、模板辦法形式(Template Method)
解釋一下模板辦法形式,就是指:一個籠統類中,有一個主辦法,再定義1...n個辦法,可以是籠統的,也可以是實踐的辦法,定義一個類,承繼該籠統類,重寫籠統辦法,經過調用籠統類,完成對子類的調用,先看個關系圖:
就是在AbstractCalculator類中定義一個主辦法calculate,calculate()調用spilt()等,Plus和Minus辨別承繼AbstractCalculator類,經過對AbstractCalculator的調用完成對子類的調用,看上面的例子:
[java] view plaincopy
- public abstract class AbstractCalculator {
- /*主辦法,完成對本類其它辦法的調用,運用final關鍵字修飾,子類不能重寫*/
- public final int calculate(String exp,String opt){
- int array[] = split(exp,opt);
- return calculate(array[0],array[1]);
- }
- /*被子類重寫的辦法*/
- abstract public int calculate(int num1,int num2);
- public int[] split(String exp,String opt){
- String array[] = exp.split(opt);
- int arrayInt[] = new int[2];
- arrayInt[0] = Integer.parseInt(array[0]);
- arrayInt[1] = Integer.parseInt(array[1]);
- return arrayInt;
- }
- }
[java] view plaincopy
- public class Plus extends AbstractCalculator {
- @Override
- public int calculate(int num1,int num2) {
- return num1 + num2;
- }
- }
測試類:
[java] view plaincopy
- public class StrategyTest {
- public static void main(String[] args) {
- String exp = "8+8";
- AbstractCalculator cal = new Plus();
- int result = cal.calculate(exp, "\\+");
- System.out.println(result);
- }
- }
我跟蹤下這個小順序的執行進程:首先將exp和"\\+"做參數,調用AbstractCalculator類裡的calculate(String,String)辦法,在calculate(String,String)裡調用同類的split(),之後再調用calculate(int ,int)辦法,從這個辦法進入到子類中,執行完return num1 + num2後,將值前往到AbstractCalculator類,賦給result,打印出來。正好驗證了我們掃尾的思緒。
15、察看者形式(Observer:察看員; 恪守者,察看者; 察看團)
包括這個形式在內的接上去的四個形式,都是類和類之間的關系,不觸及到承繼,學的時分應該記得歸結,記得本文最開端的那個圖。察看者形式很好了解,相似於郵件訂閱和RSS訂閱,當我們閱讀一些博客或wiki時,常常會看到RSS圖標,就這的意思是,當你訂閱了該文章,假如後續有更新,會及時告訴你。其實,復雜來講就一句話:當一個對象變化時,其它依賴該對象的對象都會收到告訴,並且隨著變化!對象之間是一種一對多的關系。先來看看關系圖:
我解釋下這些類的作用:MySubject類就是我們的主對象,Observer1和Observer2是依賴於(依賴到)MySubject的對象,當MySubject變化時,Observer1和Observer2必定變化。AbstractSubject類中定義著需求監控的對象列表,可以對其停止修正:添加或刪除被監控對象,且當MySubject變化時,擔任告訴在列表內存在的對象。我們看完成代碼:
一個Observer接口:
[java] view plaincopy
- public interface Observer {
- public void update();
- }
兩個完成類:
[java] view plaincopy
- public class Observer1 implements Observer {
- @Override
- public void update() {
- System.out.println("observer1 has received!");
- }
- }
[java] view plaincopy
- public class Observer2 implements Observer {
- @Override
- public void update() {
- System.out.println("observer2 has received!");
- }
- }
Subject接口及完成類:
[java] view plaincopy
- public interface Subject {
- /*添加察看者*/
- public void add(Observer observer);
- /*刪除察看者*/
- public void del(Observer observer);
- /*告訴一切的察看者(notify:告訴,公告)*/
- public void notifyObservers();
- /*本身的操作*/
- public void operation();
- }
[java] view plaincopy
- public abstract class AbstractSubject implements Subject {
- private Vector<Observer> vector = new Vector<Observer>();
- @Override
- public void add(Observer observer) {
- vector.add(observer);
- }
- @Override
- public void del(Observer observer) {
- vector.remove(observer);
- }
- @Override
- public void notifyObservers() {
- Enumeration<Observer> enumo = vector.elements();
- while(enumo.hasMoreElements()){
- enumo.nextElement().update();
- }
- }
- }
[java] view plaincopy
- public class MySubject extends AbstractSubject {
- @Override
- public void operation() {
- System.out.println("update self!");
- notifyObservers();
- }
- }
測試類:
[java] view plaincopy
- public class ObserverTest {
- public static void main(String[] args) {
- Subject sub = new MySubject();
- sub.add(new Observer1());
- sub.add(new Observer2());
- sub.operation();
- }
- }
輸入:
update self!
observer1 has received!
observer2 has received!
這些東西,其實不難,只是有些籠統,不太容易全體了解,建議讀者:依據關系圖,新建項目,自己寫代碼(或許參考我的代碼),依照總體思緒走一遍,這樣才干領會它的思想,了解起來容易!
16、迭代子形式(Iterator)
望文生義,迭代器形式就是順序訪問聚集中的對象,普通來說,集合中十分罕見,假如對集合類比擬熟習的話,了解本形式會非常輕松。這句話包括兩層意思:
一是需求遍歷的對象,即聚集對象;
二是迭代器對象;
用於對聚集對象停止遍歷訪問。我們看下關系圖:
這個思緒和我們常用的如出一轍,MyCollection中定義了集合的一些操作,MyIterator中定義了一系列迭代操作,且持有Collection實例,我們來看看完成代碼:
兩個接口:
[java] view plaincopy
- public interface Collection {
- public Iterator iterator();
- /*獲得集合元素*/
- public Object get(int i);
- /*獲得集合大小*/
- public int size();
- }
[java] view plaincopy
- public interface Iterator {
- //前移
- public Object previous();
- //後移
- public Object next();
- //判別能否還有下一個元素
- public boolean hasNext();
- //獲得第一個元素
- public Object first();
- }
兩個完成:
[java] view plaincopy
- public class MyCollection implements Collection {
- public String string[] = {"A","B","C","D","E"};
- @Override
- public Iterator iterator() {
- return new MyIterator(this);
- }
- @Override
- public Object get(int i) {
- return string[i];
- }
- @Override
- public int size() {
- return string.length;
- }
- }
[java] view plaincopy
- public class MyIterator implements Iterator {
- //創立集合變量,讓迭代器持有以後的集合對象
- private Collection collection;
- //定義並初始化角標變量
- private int pos = -1;
- /*創立有參結構,在創立迭代器示例的時分,將要被迭代器操作的結合對象傳遞到迭代器對象中*/
- public MyIterator(Collection collection){
- this.collection = collection;
- }
- //前移
- @Override
- public Object previous() {
- /*判別角標能否大於零假如不大於零*/
- if(pos > 0){
- /*假如角標大於零,證明後面還有元素可以獲取,此時可以把角標值減一,前移一位*/
- pos--;
- }
- //調用集合的辦法,依據角標獲取元素,並前往
- return collection.get(pos);
- }
- //後移
- @Override
- public Object next() {
- /*判別角標的值能否最後一個元素的角標的值*/
- if(pos<collection.size()-1){
- /*假如不是,則證明以後角標所對應的元素前面還有元素,可以持續向後獲取,因而把以後的角標值添加一,以便依據角標獲取下一個元素*/
- pos++;
- }
- /*依據元素的角標獲取元素並前往*/
- return collection.get(pos);
- }
- /*判別能否還有下一個元素*/
- @Override
- public boolean hasNext() {
- //判別以後的角標能否小於集合的長度 減一
- if(pos<collection.size()-1){
- //假如還有下一的元素,則前往true
- return true;
- }else{
- //假如沒有下一個元素則前往false
- return false;
- }
- }
- //獲取第一個元素
- @Override
- public Object first() {
- //將第一元素的角標(0)的值賦值給角標變量
- pos = 0;
- //運用集合對象調用辦法,依據元素的角標獲取元素並前往;
- return collection.get(pos);
- }
- }
測試類:
[java] view plaincopy
- public class Test {
- public static void main(String[] args) {
- Collection collection = new MyCollection();
- Iterator it = collection.iterator();
- while(it.hasNext()){
- System.out.println(it.next());
- }
- }
- }
輸入:A B C D E
此處我們貌似模仿了一個集合類的進程,覺得是不是很爽?其實JDK中各個類也都是這些根本的東西,加一些設計形式,再加一些優化放到一同的,只需我們把這些東西學會了,掌握好了,我們也可以寫出自己的集合類,甚至框架!
17、責任鏈形式(Chain of Responsibility)
接上去我們將要談談責任鏈形式,有多個對象,每個對象持有對下一個對象的援用,這樣就會構成一條鏈,懇求在這條鏈上傳遞,直到某一對象決議處置該懇求。但是收回者並不清楚究竟最終那個對象會處置該懇求,所以,責任鏈形式可以完成,在隱瞞客戶端的狀況下,對零碎停止靜態的調整。先看看關系圖:
Abstracthandler類提供了get和set辦法,方便MyHandle類設置和修正援用對象,MyHandle類是中心,實例化後生成一系列互相持有的對象,構成一條鏈。
[java] view plaincopy
//公共的處置接口
- public interface Handler {
- //接口提供詳細的操作辦法,由詳細的完成類重寫該辦法,完成詳細的操作
- public void operator();
- }
[java] view plaincopy
//籠統辦法,提供公共的操作代碼,供承繼該類子類調用,進步代碼的重用性
- public abstract class AbstractHandler {
- //提供公有的Handler接口屬性用於對象的互相持有
- private Handler handler;
- //提供獲取以後對象所持有的對象的辦法,以便調用所持有的對象的辦法
- public Handler getHandler() {
- return handler;
- }
- //set辦法,用於持有一個對象到以後的對象中,以便構成鏈
- public void setHandler(Handler handler) {
- this.handler = handler;
- }
- }
[java] view plaincopy
//詳細的操作類,承繼籠統類,並完成接口和接口中的辦法
- public class MyHandler extends AbstractHandler implements Handler {
- //稱號屬性
- private String name;
- //提供有參結構,在創立實例的時分,設置以後對象的辦法
- public MyHandler(String name) {
- this.name = name;
- }
- //完成接口中的操作辦法
- @Override
- public void operator() {
- // 以後對象自己的操作代碼
- System.out.println(name+"deal!");
- //調用父類的getHandler辦法獲取以後對象所持有的對象,並判別能否持有對象
- if(getHandler()!=null){
- //假如以後對象持有對象,則調所持有對象的操作辦法
- getHandler().operator();
- }
- }
- }
[java] view plaincopy
- //測試類
- public class Test {
- public static void main(String[] args) {
- //創立鏈中的每一個對象
- MyHandler h1 = new MyHandler("h1");
- MyHandler h2 = new MyHandler("h2");
- MyHandler h3 = new MyHandler("h3");
- //讓對象停止鏈式持有
- h1.setHandler(h2);
- h2.setHandler(h3);
- //調用鏈首的操作辦法,每一個對象的操作辦法會調用其所持有的對象的操作辦法,會停止鏈式調用
- h1.operator();
- }
- }
輸入:
h1deal!
h2deal!
h3deal!
此處強調一點就是,鏈接上的懇求可以是一條鏈,可以是一個樹,還可以是一個環,形式自身不約束這個,需求我們自己去完成,同時,在一個時辰,命令只允許由一個對象傳給另一個對象,而不允許傳給多個對象。
18、命令形式(Command)
命令形式很好了解,舉個例子,司令員下令讓兵士去干件事情,從整個事情的角度來思索,司令員的作用是,收回口令,口令經過傳遞,傳到了兵士耳朵裡,兵士去執行。這個進程好在,三者互相解耦,任何一方都不必去依賴其別人,只需求做好自己的事兒就行,司令員要的是後果,不會去關注究竟兵士是怎樣完成的。我們看看關系圖:
Invoker是調用者(司令員),Receiver是被調用者(兵士),MyCommand是命令,完成了Command接口,持有接納對象,看完成代碼:
[java] view plaincopy
- //命令接口,由自定義命令類完成
- public interface Command {
- //提供命令執行的辦法
- public void exe();
- }
[java] view plaincopy
- //自定義命令類完成命令接口
- public class MyCommand implements Command {
- // 自定義命令類持有命令接納者的實例
- private Receiver receiver;
- //提供有參結構辦法,在對命令停止實例化的時分接納命令接納者實例,
- public MyCommand(Receiver receiver) {
- this.receiver = receiver;
- }
- //命令的詳細執行,即便用命令接納者的實例調用其詳細的辦法,執行命令
- @Override
- public void exe() {
- receiver.action();
- }
- }
[java] view plaincopy
- //命令承受者類
- public class Receiver {
- //命令的執行辦法
- public void action(){
- //執行命令的詳細代碼
- System.out.println("command received!");
- }
- }
[java] view plaincopy
- //命令收回者類
- public class Invoker {
- //創立命令接口類型的變量,用於接納並持有詳細的命令實例
- private Command command;
- //有參結構辦法,在對詳細的命令停止實例化的時分持有詳細的命令實例
- public Invoker(Command command) {
- this.command = command;
- }
- //命令收回者收回命令的詳細辦法
- public void action(){
- command.exe();
- }
- }
[java] view plaincopy
- public class Test {
- public static void main(String[] args) {
- //創立命令承受者實例
- Receiver receiver = new Receiver();
- //創立命令實例,在創立的時分持有命令接納者實例
- Command cmd = new MyCommand(receiver);
- //創立命令收回者實例,在創立愛的時分就持有詳細的命令實例
- Invoker invoker = new Invoker(cmd);
- //運用命令收回者對象調用收回命令的辦法,以使命令失掉執行
- invoker.action();
- }
- }
輸入:command received!
這個很好了解,命令形式的目的就是到達命令的收回者和執行者之間解耦,完成請求和執行分開,熟習Struts的同窗應該知道,Struts其實就是一種將懇求和出現別離的技術,其中必定觸及命令形式的思想!
其實每個設計形式都是很重要的一種思想,看上去很熟,其實是由於我們在學到的東西中都有觸及,雖然有時我們並不知道,其真實Java自身的設計之中處處都有表現,像AWT、JDBC、集合類、IO管道或許是Web框架,外面設計形式無處不在。由於我們篇幅無限,很難講每一個設計形式都講的很詳細,不過我會盡我所能,盡量在無限的空間和篇幅內,把意思寫清楚了,更好讓大家明白。本章不出不測的話,應該是設計形式最後一講了,首先還是上一下上篇掃尾的那個圖:
本章講講第三類和第四類。
19、備忘錄形式(Memento)
次要目的是保管一個對象的某個形態,以便在適當的時分恢復對象,團體覺得叫備份形式更抽象些,淺顯的講下:假定有原始類A,A中有各種屬性,A可以決議需求備份的屬性,備忘錄類B是用來存儲A的一些外部形態,類C呢,就是一個用來存儲藏忘錄的,且只能存儲,不能修正等操作。做個圖來剖析一下:
Original類是原始類,外面有需求保管的屬性value及創立一個備忘錄類,用來保管value值。Memento類是備忘錄類,Storage類是存儲藏忘錄的類,持有Memento類的實例,該形式很好了解。直接看源碼:
[java] view plaincopy
- //原始類
- public class Original {
- //原始類的公有屬性
- private String value;
- //提供獲取原始類屬性的辦法
- public String getValue() {
- return value;
- }
- //提供修正原始類屬性的辦法
- public void setValue(String value) {
- this.value = value;
- }
- //提供有參結構辦法,在實例化原始類的時分對屬性停止初始化
- public Original(String value) {
- this.value = value;
- }
- //提供獲取原始類的備忘錄類實例的辦法,該備忘錄實例保管了原始類的以後屬性形態
- public Memento createMemento(){
- return new Memento(value);
- }
- //提供從備忘錄類的實例中恢恢復始類屬性形態的辦法
- public void restoreMemento(Memento memento){
- this.value = memento.getValue();
- }
- }
[java] view plaincopy
- //備忘錄類
- public class Memento {
- //定義公有屬性,用來接納要保管形態的原始類的屬性的以後形態值
- private String value;
- //提供有參結構辦法,在創立該類的實例的時分,就接納一個原始類的以後屬性的值,停止存儲
- public Memento(String value) {
- this.value = value;
- }
- //提供獲取屬性值的辦法
- public String getValue() {
- return value;
- }
- //提供修正原始類的初始屬性形態的辦法
- public void setValue(String value) {
- this.value = value;
- }
- }
[java] view plaincopy
- //存儲類,用來存儲保管了摸一個原始類形態的備忘錄雷實例的類
- public class Storage {
- //定義備忘錄類類型的變量,用來接納備忘錄類實例
- private Memento memento;
- //有參結構辦法,在創立該類的實例的時分,就持有一個備忘錄類的實例
- public Storage(Memento memento) {
- this.memento = memento;
- }
- //提供獲取所持有的備忘錄類的實例的辦法
- public Memento getMemento() {
- return memento;
- }
- //提供修正所持有的備忘錄類的示例的辦法;
- public void setMemento(Memento memento) {
- this.memento = memento;
- }
- }
測試類:
[java] view plaincopy
- public class Test {
- public static void main(String[] args) {
- // 創立原始類
- Original origi = new Original("egg");
- // 創立備忘錄
- Storage storage = new Storage(origi.createMemento());
- // 修正原始類的形態
- System.out.println("初始化形態為:" + origi.getValue());
- origi.setValue("niu");
- System.out.println("修正後的形態為:" + origi.getValue());
- // 回恢復始類的形態
- origi.restoreMemento(storage.getMemento());
- System.out.println("恢復後的形態為:" + origi.getValue());
- }
- }
輸入:
初始化形態為:egg
修正後的形態為:niu
恢復後的形態為:egg
復雜描繪下:
新建原始類時,value被初始化為egg,後經過修正,將value的值置為niu,最後倒數第二行停止恢復形態,後果成功恢復了。其實我覺得這個形式叫“備份-恢復”形式最抽象。
20、形態形式(State)
中心思想就是:當對象的形態改動時,同時改動其行為,很好了解!就拿QQ來說,有幾種形態,在線、隱身、繁忙等,每個形態對應不同的操作,而且你的好友也能看到你的形態,所以,形態形式就兩點:
1、可以經過改動形態來取得不同的行為。
2、你的好友能同時看到你的變化。
看圖:
State類是個形態類,Context類可以完成切換,我們來看看代碼:
[java] view plaincopy
- //形態類,提供公有的形態屬性和不同的辦法
- public class State {
- //公有形態屬性
- private String value;
- //提供獲取和修正公有形態屬性的值的辦法
- public String getValue() {
- return value;
- }
- public void setValue(String value) {
- this.value = value;
- }
- //提供不同的辦法
- public void method1(){
- System.out.println("execute the first opt!");
- }
- public void method2(){
- System.out.println("execute the second opt!");
- }
- }
[java] view plaincopy
- //決策類,該類持有形態類的實例,依據所持有的形態類的轉台屬性的不同的值選擇相應的辦法執行
- public class Context {
- // 定義形態類類型的變量,用於接納不同的形態類實例
- private State state;
- //提供有參結構辦法,在實例化該類的時分就持有一個形態類實例
- public Context(State state) {
- this.state = state;
- }
- //提供獲取和修正形態類所持有的形態類的實例的辦法
- public State getState() {
- return state;
- }
- public void setState(State state) {
- this.state = state;
- }
- //詳細行為辦法,依據所持有的形態類的實例的形態屬性的不同形態選擇相應的辦法停止執行;
- public void method() {
- if (state.getValue().equals("state1")) {
- state.method1();
- } else if (state.getValue().equals("state2")) {
- state.method2();
- }
- }
- }
測試類:
[java] view plaincopy
- public class Test {
- public static void main(String[] args) {
- State state = new State();
- Context context = new Context(state);
- //設置第一種形態
- state.setValue("state1");
- context.method();
- //設置第二種形態
- state.setValue("state2");
- context.method();
- }
- }
輸入:
execute the first opt!
execute the second opt!
依據這個特性,形態形式在日常開發中用的挺多的,尤其是做網站的時分,我們有時希望依據對象的某一屬性,區別開他們的一些功用,比方說復雜的權限控制等。
21、訪問者形式(Visitor)
訪問者形式把數據構造和作用於構造上的操作解耦合,使得操作集合可絕對自在地演化。訪問者形式適用於數據構造絕對波動算法又易變化的零碎。由於訪問者形式使得算法操作添加變得容易。若零碎數據構造對象易於變化,常常有新的數據對象添加出去,則不合適運用訪問者形式。訪問者形式的優點是添加操作很容易,由於添加操作意味著添加新的訪問者。訪問者形式將有關行為集中到一個訪問者對象中,其改動不影響零碎數據構造。其缺陷就是添加新的數據構造很困難。—— From 百科
復雜來說,訪問者形式就是一種別離對象數據構造與行為的辦法,經過這種別離,可到達為一個被訪問者靜態添加新的操作而無需做其它的修正的效果。復雜關系圖:
來看看原碼:一個Visitor類,寄存要訪問的對象,
[java] view plaincopy
- //訪問者接口
- public interface Visitor {
- //訪問辦法
- public void visit(Subject sub);
- }
[java] view plaincopy
- //訪問者接口完成類
- public class MyVisitor implements Visitor {
- //完成訪問辦法
- @Override
- public void visit(Subject sub) {
- //在訪問辦法中,調用被訪問的類的辦法,訪問被訪問的類的屬性
- System.out.println("visit the subject:"+sub.getSubject());
- }
- }
Subject類,accept辦法,承受將要訪問它的對象,getSubject()獲取將要被訪問的屬性,
[java] view plaincopy
- //被訪問的類的接口
- public interface Subject {
- public void accept(Visitor visitor);
- public String getSubject();
- }
[java] view plaincopy
- //被訪問者接口的完成類
- public class MySubject implements Subject {
- //完成接口中的accept辦法,該辦法接納一個訪問者實例
- @Override
- public void accept(Visitor visitor) {
- .//調用訪問者的訪問辦法
- visitor.visit(this);
- }
- //完成辦法
- @Override
- public String getSubject() {
- //該辦法前往被訪問的以後類的屬性
- return "love";
- }
- }
測試:
[java] view plaincopy
- public class Test {
- public static void main(String[] args) {
- Visitor visitor = new MyVisitor();
- Subject sub = new MySubject();
- //調用被訪問類的accept辦法,該辦法需求承受一個參數,參數的類型是訪問類的實例,在accept辦法中運用訪問類訪問辦法visit,該辦法中又調用了被訪問類的獲取被訪問類的數據的辦法getSubject
- sub.accept(visitor);
- }
- }
輸入:visit the subject:love
該形式適用場景:
假如我們想為一個現有的類添加新功用,不得不思索幾個事情:
1、新功用會不會與現有功用呈現兼容性問題?
2、當前會不會再需求添加?
3、假如類不允許修正代碼怎樣辦?
面對這些問題,最好的處理辦法就是運用訪問者形式,訪問者形式適用於數據構造絕對波動的零碎,把數據構造和算法解耦;
22、中介者形式(Mediator:調停人,調停者,傳遞者,中介物)
中介者形式也是用來降低類類之間的耦合的,由於假如類類之間有依賴關系的話,不利於功用的拓展和維護,由於只需修正一個對象,其它關聯的對象都得停止修正。假如運用中介者形式,只需關懷和Mediator類的關系,詳細類類之間的關系及調度交給Mediator就行,這有點像spring容器的作用。先看看圖:
User類一致接口,User1和User2辨別是不同的對象,二者之間有關聯,假如不采用中介者形式,則需求二者互相持有援用,這樣二者的耦合度很高,為理解耦,引入了Mediator類,提供一致接口,MyMediator為其完成類,外面持有User1和User2的實例,用來完成對User1和User2的控制。這樣User1和User2兩個對象互相獨立,他們只需求堅持好和Mediator之間的關系就行,剩下的全由MyMediator類來維護!根本完成:
[java] view plaincopy
- //中介者接口
- public interface Mediator {
- //提供創立中介者的辦法
- public void createMediator();
- //提供公共的操作辦法
- public void workAll();
- }
[java] view plaincopy
- //中介者接口完成類
- public class MyMediator implements Mediator {
- //持有兩個類的實例,以便中介者對他們的關系停止管理
- private User user1;
- private User user2;
- //提供獲取所持有的實例的辦法
- public User getUser1() {
- return user1;
- }
- public User getUser2() {
- return user2;
- }
- //完成接口的創立中介者的辦法,在此辦法中創立被管理的類的實例
- @Override
- public void createMediator() {
- user1 = new User1(this);
- user2 = new User2(this);
- }
- //完成接口中的一致的功用辦法,在此辦法中調用所持有的類的實例的辦法,完成詳細的操作
- @Override
- public void workAll() {
- user1.work();
- user2.work();
- }
- }
[java] view plaincopy
- //創立籠統類,在此類中完成公共的功用局部,完成代碼的重用
- public abstract class User {
- //持有中介者接口的完成類實例,已經過中介者和其他類直接構成關系,有堅持低耦合度
- private Mediator mediator;
- //提供獲取中介者實例的辦法
- public Mediator getMediator(){
- return mediator;
- }
- //提供有參結構,在初始化實例的時分就構成和中介者實例的關系
- public User(Mediator mediator) {
- this.mediator = mediator;
- }
- //提供籠統的功用辦法,詳細完成的功用由完成類自定義
- public abstract void work();
- }
[java] view plaincopy
- //詳細的功用類,承繼籠統類
- public class User1 extends User {
- //提供有參結構辦法,在停止實例化的時分持有中介者的實例
- public User1(Mediator mediator){
- //調用父類的結構辦法停止實例化
- super(mediator);
- }
- //重寫父類的籠統功用辦法,完成詳細的功用
- @Override
- public void work() {
- System.out.println("user1 exe!");
- }
- }
[java] view plaincopy
- //詳細的功用類,承繼籠統類
- public class User2 extends User {
- public User2(Mediator mediator){
- super(mediator);
- }
- @Override
- public void work() {
- System.out.println("user2 exe!");
- }
- }
測試類:
[java] view plaincopy
- public class Test {
- public static void main(String[] args) {
- //創立中介者接口完成類的實例
- Mediator mediator = new MyMediator();
- //調用中介者完成類的辦法,對持有的實例停止初始化
- mediator.createMediator();
- //調用中介者完成類的功用辦法,在此辦法中功用的詳細完成,實踐是運用運用所持有的實例調用各自的辦法停止直接完成的;
- mediator.workAll();
- }
- }
輸入:
user1 exe!
user2 exe!
23、解釋器形式(Interpreter)
解釋器形式是我們暫時的最後一講,普通次要使用在OOP開發中的編譯器的開發中,所以適用面比擬窄。
OOP:Object Oriented Programming,OOP,面向對象順序設計;
Context類是一個上下文環境類,Plus和Minus辨別是用來計算的完成,代碼如下:
[java] view plaincopy
- //表達式類
- public interface Expression {
- //提供公共的解釋辦法
- public int interpreter(Context context);
- }
[java] view plaincopy
- //表達式類的完成類,完成加法運算
- public class Plus implements Expression {
- //完成解釋辦法
- @Override
- public int interpreter(Context context) {
- //獲取上下文類的實例中的屬性的值,停止處置後前往後果
- return context.getNum1()+context.getNum2();
- }
- }
[java] view plaincopy
- public class Minus implements Expression {
- @Override
- public int interpret(Context context) {
- return context.getNum1()-context.getNum2();
- }
- }
[java] view plaincopy
- //上下文環境類
- public class Context {
- private int num1;
- private int num2;
- //提供有參結構辦法,在創立實例的時分,對屬性停止初始化
- public Context(int num1, int num2) {
- this.num1 = num1;
- this.num2 = num2;
- }
- //提供獲取屬性和修正屬性的辦法
- public int getNum1() {
- return num1;
- }
- public void setNum1(int num1) {
- this.num1 = num1;
- }
- public int getNum2() {
- return num2;
- }
- public void setNum2(int num2) {
- this.num2 = num2;
- }
- }
[java] view plaincopy
- public class Test {
- public static void main(String[] args) {
- // 計算9+2-8的值
- int result = new Minus().interpreter((new Context(new Plus().interpreter(new Context(9, 2)), 8)));
- System.out.println(result);
- }
- }
最後輸入正確的後果:3。
根本就這樣,解釋器形式用來做各種各樣的解釋器,如正則表達式等的解釋器等等!
資源:http://download.csdn.net/detail/zhangerqing/4835830
原文鏈接:http://blog.csdn.net/zhangerqing