23種Java設計模式(4)-關係模式

C、關係模式(11種)

先來張圖,看看這11中模式的關係:java

第一類:經過父類與子類的關係進行實現。正則表達式

第二類:兩個類之間。算法

第三類:類的狀態。spring

第四類:經過中間類小程序

父類與子類關係

1、策略模式(strategy)

策略模式定義了一系列算法,並將每一個算法封裝起來,使他們能夠相互替換,且算法的變化不會影響到使用算法的客戶。須要設計一個接口,爲一系列實現類提供統一的方法,多個實現類實現該接口,設計一個抽象類(無關緊要,屬於輔助類),提供輔助函數,關係圖以下:設計模式

圖中ICalculator提供贊成的方法,
AbstractCalculator是輔助類,提供輔助方法,接下來,依次實現下每一個類:數據結構

首先統一接口:框架

public interface ICalculator {  
    public int calculate(String exp);  
}

輔助類:數據結構和算法

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;  
    }  
}

三個實現類:ide

public class Plus extends AbstractCalculator implements ICalculator {  
  
    @Override  
    public int calculate(String exp) {  
        int arrayInt[] = split(exp,"\\+");  
        return arrayInt[0]+arrayInt[1];  
    }  
}
public class Minus extends AbstractCalculator implements ICalculator {  
  
    @Override  
    public int calculate(String exp) {  
        int arrayInt[] = split(exp,"-");  
        return arrayInt[0]-arrayInt[1];  
    }  
  
}
public class Multiply extends AbstractCalculator implements ICalculator {  
  
    @Override  
    public int calculate(String exp) {  
        int arrayInt[] = split(exp,"\\*");  
        return arrayInt[0]*arrayInt[1];  
    }  
}

簡單的測試類:

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

策略模式的決定權在用戶,系統自己提供不一樣算法的實現,新增或者刪除算法,對各類算法作封裝。所以,策略模式多用在算法決策系統中,外部用戶只須要決定用哪一個算法便可。

2、模板方法模式(Template Method)

解釋一下模板方法模式,就是指:一個抽象類中,有一個主方法,再定義1...n個方法,能夠是抽象的,也能夠是實際的方法,定義一個類,繼承該抽象類,重寫抽象方法,經過調用抽象類,實現對子類的調用,先看個關係圖:

就是在AbstractCalculator類中定義一個主方法calculate,calculate()調用spilt()等,Plus和Minus分別繼承AbstractCalculator類,經過對AbstractCalculator的調用實現對子類的調用,看下面的例子:

public abstract class AbstractCalculator {  
      
    /*主方法,實現對本類其它方法的調用*/  
    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;  
    }  
}
public class Plus extends AbstractCalculator {  
  
    @Override  
    public int calculate(int num1,int num2) {  
        return num1 + num2;  
    }  
}

測試類:

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,打印出來。正好驗證了咱們開頭的思路。

類之間的關係

3、觀察者模式(Observer)

包括這個模式在內的接下來的四個模式,都是類和類之間的關係,不涉及到繼承,學的時候應該 記得概括,記得本文最開始的那個圖。觀察者模式很好理解,相似於郵件訂閱和RSS訂閱,當咱們瀏覽一些博客或wiki時,常常會看到RSS圖標,就這的意思是,當你訂閱了該文章,若是後續有更新,會及時通知你。其實,簡單來說就一句話:當一個對象變化時,其它依賴該對象的對象都會收到通知,而且隨着變化!對象之間是一種一對多的關係。先來看看關係圖:


我解釋下這些類的做用:MySubject類就是咱們的主對象,Observer1和Observer2是依賴於MySubject的對象,當MySubject變化時,Observer1和Observer2必然變化。AbstractSubject類中定義着須要監控的對象列表,能夠對其進行修改:增長或刪除被監控對象,且當MySubject變化時,負責通知在列表內存在的對象。咱們看實現代碼:

一個Observer接口:

public interface Observer {  
    public void update();  
}
public class Observer1 implements Observer {  
  
    @Override  
    public void update() {  
        System.out.println("observer1 has received!");  
    }  
}
public class Observer2 implements Observer {  
  
    @Override  
    public void update() {  
        System.out.println("observer2 has received!");  
    }  
  
}

Subject接口及實現類:

public interface Subject {  
      
    /*增長觀察者*/  
    public void add(Observer observer);  
      
    /*刪除觀察者*/  
    public void del(Observer observer);  
      
    /*通知全部的觀察者*/  
    public void notifyObservers();  
      
    /*自身的操做*/  
    public void operation();  
}
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();  
        }  
    }  
}
public class MySubject extends AbstractSubject {  
  
    @Override  
    public void operation() {  
        System.out.println("update self!");  
        notifyObservers();  
    }  
  
}

測試類:

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!

 這些東西,其實不難,只是有些抽象,不太容易總體理解,建議讀者:根據關係圖,新建項目,本身寫代碼(或者參考個人代碼),按照整體思路走一遍,這樣才能體會它的思想,理解起來容易!

4、迭代子模式(Iterator)

顧名思義,迭代器模式就是順序訪問彙集中的對象,通常來講,集合中很是常見,若是對集合類比較熟悉的話,理解本模式會十分輕鬆。這句話包含兩層意思:一是須要遍歷的對象,即彙集對象,二是迭代器對象,用於對彙集對象進行遍歷訪問。咱們看下關係圖:

這個思路和咱們經常使用的如出一轍,MyCollection中定義了集合的一些操做,MyIterator中定義了一系列迭代操做,且持有Collection實例,咱們來看看實現代碼:

兩個接口:

public interface Collection {  
      
    public Iterator iterator();  
      
    /*取得集合元素*/  
    public Object get(int i);  
      
    /*取得集合大小*/  
    public int size();  
}
public interface Iterator {  
    //前移  
    public Object previous();  
      
    //後移  
    public Object next();  
    public boolean hasNext();  
      
    //取得第一個元素  
    public Object first();  
}

兩個實現:

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;  
    }  
}
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){  
            return true;  
        }else{  
            return false;  
        }  
    }  
  
    @Override  
    public Object first() {  
        pos = 0;  
        return collection.get(pos);  
    }  
  
}

測試類:

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中各個類也都是這些基本的東西,加一些設計模式,再加一些優化放到一塊兒的,只要咱們把這些東西學會了,掌握好了,咱們也能夠寫出本身的集合類,甚至框架!

5、責任鏈模式(Chain of Responsibility)

接下來咱們將要談談責任鏈模式,有多個對象,每一個對象持有對下一個對象的引用,這樣就會造成一條鏈,請求在這條鏈上傳遞,直到某一對象決定處理該請求。可是發出者並不清楚到底最終那個對象會處理該請求,因此,責任鏈模式能夠實現,在隱瞞客戶端的狀況下,對系統進行動態的調整。先看看關係圖:

Abstracthandler類提供了get和set方法,方便MyHandle類設置和修改引用對象,MyHandle類是核心,實例化後生成一系列相互持有的對象,構成一條鏈。

public interface Handler {  
    public void operator();  
}
public abstract class AbstractHandler {  
      
    private Handler handler;  
  
    public Handler getHandler() {  
        return handler;  
    }  
  
    public void setHandler(Handler handler) {  
        this.handler = handler;  
    }  
      
}
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!");  
        if(getHandler()!=null){  
            getHandler().operator();  
        }  
    }  
}
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!

此處強調一點就是,連接上的請求能夠是一條鏈,能夠是一個樹,還能夠是一個環,模式自己不約束這個,須要咱們本身去實現,同時,在一個時刻,命令只容許由一個對象傳給另外一個對象,而不容許傳給多個對象。

6、命令模式(Command)

命令模式很好理解,舉個例子,司令員下令讓士兵去幹件事情,從整個事情的角度來考慮,司令員的做用是,發出口令,口令通過傳遞,傳到了士兵耳朵裏,士兵去執行。這個過程好在,三者相互解耦,任何一方都不用去依賴其餘人,只須要作好本身的事兒就行,司令員要的是結果,不會去關注到底士兵是怎麼實現的。咱們看看關係圖:

Invoker是調用者(司令員),Receiver是被調用者(士兵),MyCommand是命令,實現了Command接口,持有接收對象,看實現代碼:

public interface Command {  
    public void exe();  
}
public class MyCommand implements Command {  
  
    private Receiver receiver;  
      
    public MyCommand(Receiver receiver) {  
        this.receiver = receiver;  
    }  
  
    @Override  
    public void exe() {  
        receiver.action();  
    }  
}
public class Receiver {  
    public void action(){  
        System.out.println("command received!");  
    }  
}
public class Invoker {  
      
    private Command command;  
      
    public Invoker(Command command) {  
        this.command = command;  
    }  
  
    public void action(){  
        command.exe();  
    }  
}
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其實就是一種將請求和呈現分離的技術,其中必然涉及命令模式的思想!

類的狀態

7、備忘錄模式(Memento)

主要目的是保存一個對象的某個狀態,以便在適當的時候恢復對象,我的以爲叫備份模式更形象些,通俗的講下:假設有原始類A,A中有各類屬性,A能夠決定須要備份的屬性,備忘錄類B是用來存儲A的一些內部狀態,類C呢,就是一個用來存儲備忘錄的,且只能存儲,不能修改等操做。作個圖來分析一下:

Original類是原始類,裏面有須要保存的屬性value及建立一個備忘錄類,用來保存value值。Memento類是備忘錄類,Storage類是存儲備忘錄的類,持有Memento類的實例,該模式很好理解。直接看源碼:

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();  
    }  
}
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;  
    }  
}
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;  
    }  
}

測試類:

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,最後倒數第二行進行恢復狀態,結果成功恢復了。其實我以爲這個模式叫「備份-恢復」模式最形象。

8、狀態模式(State)

核心思想就是:當對象的狀態改變時,同時改變其行爲,很好理解!就拿QQ來講,有幾種狀態,在線、隱身、忙碌等,每一個狀態對應不一樣的操做,並且你的好友也能看到你的狀態,因此,狀態模式就兩點:一、能夠經過改變狀態來得到不一樣的行爲。二、你的好友能同時看到你的變化。看圖:

State類是個狀態類,Context類能夠實現切換,咱們來看看代碼:

package com.xtfggef.dp.state;  
  
/** 
 * 狀態類的核心類 
 * 2012-12-1 
 * @author erqing 
 * 
 */  
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!");  
    }  
}
package com.xtfggef.dp.state;  
  
/** 
 * 狀態模式的切換類   2012-12-1 
 * @author erqing 
 *  
 */  
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();  
        }  
    }  
}

測試類:

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!

根據這個特性,狀態模式在平常開發中用的挺多的,尤爲是作網站的時候,咱們有時但願根據對象的某一屬性,區別開他們的一些功能,好比說簡單的權限控制等。

經過中間類

9、訪問者模式(Visitor)

訪問者模式把數據結構和做用於結構上的操做解耦合,使得操做集合可相對自由地演化。訪問者模式適用於數據結構相對穩定算法又易變化的系統。由於訪問者模式使得算法操做增長變得容易。若系統數據結構對象易於變化,常常有新的數據對象增長進來,則不適合使用訪問者模式。訪問者模式的優勢是增長操做很容易,由於增長操做意味着增長新的訪問者。訪問者模式將有關行爲集中到一個訪問者對象中,其改變不影響系統數據結構。其缺點就是增長新的數據結構很困難。—— From 百科

簡單來講,訪問者模式就是一種分離對象數據結構與行爲的方法,經過這種分離,可達到爲一個被訪問者動態添加新的操做而無需作其它的修改的效果。簡單關係圖:

來看看原碼:一個Visitor類,存放要訪問的對象

public interface Visitor {  
    public void visit(Subject sub);  
}
public class MyVisitor implements Visitor {  
  
    @Override  
    public void visit(Subject sub) {  
        System.out.println("visit the subject:"+sub.getSubject());  
    }  
}

Subject類,accept方法,接受將要訪問它的對象,getSubject()獲取將要被訪問的屬性

public interface Subject {  
    public void accept(Visitor visitor);  
    public String getSubject();  
}
public class MySubject implements Subject {  
  
    @Override  
    public void accept(Visitor visitor) {  
        visitor.visit(this);  
    }  
  
    @Override  
    public String getSubject() {  
        return "love";  
    }  
}

測試:

public class Test {  
  
    public static void main(String[] args) {  
          
        Visitor visitor = new MyVisitor();  
        Subject sub = new MySubject();  
        sub.accept(visitor);      
    }  
}

輸出:visit the subject:love


該模式適用場景:若是咱們想爲一個現有的類增長新功能,不得不考慮幾個事情:一、新功能會不會與現有功能出現兼容性問題?二、之後會不會再須要添加?三、若是類不容許修改代碼怎麼辦?面對這些問題,最好的解決方法就是使用訪問者模式,訪問者模式適用於數據結構相對穩定的系統,把數據結構和算法解耦,

10、中介者模式(Mediator)

中介者模式也是用來下降類類之間的耦合的,由於若是類類之間有依賴關係的話,不利於功能的拓展和維護,由於只要修改一個對象,其它關聯的對象都得進行修改。若是使用中介者模式,只需關心和Mediator類的關係,具體類類之間的關係及調度交給Mediator就行,這有點像spring容器的做用。先看看圖:

User類統一接口,User1和User2分別是不一樣的對象,兩者之間有關聯,若是不採用中介者模式,則須要兩者相互持有引用,這樣兩者的耦合度很高,爲了解耦,引入了Mediator類,提供統一接口,MyMediator爲其實現類,裏面持有User1和User2的實例,用來實現對User1和User2的控制。這樣User1和User2兩個對象相互獨立,他們只須要保持好和Mediator之間的關係就行,剩下的全由MyMediator類來維護!基本實現:

public interface Mediator {  
    public void createMediator();  
    public void workAll();  
}
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();  
    }  
}
public abstract class User {  
      
    private Mediator mediator;  
      
    public Mediator getMediator(){  
        return mediator;  
    }  
      
    public User(Mediator mediator) {  
        this.mediator = mediator;  
    }  
  
    public abstract void work();  
}
public class User1 extends User {  
  
    public User1(Mediator mediator){  
        super(mediator);  
    }  
      
    @Override  
    public void work() {  
        System.out.println("user1 exe!");  
    }  
}
public class User2 extends User {  
  
    public User2(Mediator mediator){  
        super(mediator);  
    }  
      
    @Override  
    public void work() {  
        System.out.println("user2 exe!");  
    }  
}

測試類:

public class Test {  
  
    public static void main(String[] args) {  
        Mediator mediator = new MyMediator();  
        mediator.createMediator();  
        mediator.workAll();  
    }  
}

輸出:

user1 exe!
user2 exe!

11、解釋器模式(Interpreter)

解釋器模式是咱們暫時的最後一講,通常主要應用在OOP開發中的編譯器的開發中,因此適用面比較窄。

Context類是一個上下文環境類,Plus和Minus分別是用來計算的實現,代碼以下:

public interface Expression {  
    public int interpret(Context context);  
}
public class Plus implements Expression {  
  
    @Override  
    public int interpret(Context context) {  
        return context.getNum1()+context.getNum2();  
    }  
}
public class Minus implements Expression {  
  
    @Override  
    public int interpret(Context context) {  
        return context.getNum1()-context.getNum2();  
    }  
}
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;  
    }  
      
      
}
public class Test {  
  
    public static void main(String[] args) {  
  
        // 計算9+2-8的值  
        int result = new Minus().interpret((new Context(new Plus()  
                .interpret(new Context(9, 2)), 8)));  
        System.out.println(result);  
    }  
}

最後輸出正確的結果:3。

基本就這樣,解釋器模式用來作各類各樣的解釋器,如正則表達式等的解釋器等等!

相關文章
相關標籤/搜索