23種設計模式Java版第八篇

ps:本文系轉載文章,閱讀原文可獲取源碼,文章末尾有原文連接數組

ps:這一篇是寫備忘錄模式、中介者模式和解釋器模式ide

一、備忘錄模式測試

在保持封裝性的條件下,獲取一個對象的內部狀態,在其餘地方保存這個對象的狀態,當須要用到該對象狀態時就恢復到原先保存的狀態。this

備忘錄模式具備如下幾種角色:日誌

(1)發起人角色:記錄當前的內部狀態,具備建立備忘錄和恢復備忘錄的方法。code

(2)備忘錄角色:存儲發起人的內部狀態,在須要用到的時候返回這些內部狀態給發起人。對象

(3)管理者角色:對備忘錄進行管理,只提供保存與獲取備忘錄的方法。繼承

下面用代碼舉個例子:遞歸

(1)發起人角色,新建一個 Emp 類:接口

public class Emp {
private String ename;
private int age;
private double salary;
public Emp(String ename, int age, double salary) {

super();
this.ename = ename;
this.age = age;
this.salary = salary;

}

public EmpMemento memento() {

return new EmpMemento(this);

}

public void recovery(EmpMemento e) {

this.ename = e.getEname();
this.age = e.getAge();
this.salary = e.getSaraly();

}
public String getEname() {

return ename;

}
public void setEname(String ename) {

this.ename = ename;

}
public int getAge() {

return age;

}
public void setAge(int age) {

this.age = age;

}
public double getSalary() {

return salary;

}
public void setSalary(double salary) {

this.salary = salary;

}

}

(2)備忘錄角色,新建一個 EmpMemento 類:

public class EmpMemento {
private String ename;
private int age;
private double saraly;

public EmpMemento(Emp emp) {

ename = emp.getEname();
age = emp.getAge();
saraly = emp.getSalary();

}
public String getEname() {

return ename;

}
public void setEname(String ename) {

this.ename = ename;

}
public int getAge() {

return age;

}
public void setAge(int age) {

this.age = age;

}
public double getSaraly() {

return saraly;

}
public void setSaraly(double saraly) {

this.saraly = saraly;

}

}

(3)管理者角色,新建一個 CareTaker 類:

public class CareTaker {
private EmpMemento memento;

public EmpMemento getMemento() {

return memento;

}

public void setMemento(EmpMemento memento) {

this.memento = memento;

}

}

(4)客戶端進行測試調用:

CareTaker taker = new CareTaker();
    Emp emp = new Emp("小周", 11, 300);
    System.out.println("第一次打印" + emp.getEname() + "---" + emp.getAge() + "---" + emp.getSalary());
    taker.setMemento(emp.memento());
    System.out.println("已經保存了以前的數據,如今對數據進行修改");
    emp.setEname("小二");
    emp.setAge(19);
    emp.setSalary(99);
    System.out.println("第二次打印" + emp.getEname() + "---" + emp.getAge() + "---" + emp.getSalary());
    System.out.println("正在撤銷,恢復以前保存的數據");
    emp.recovery(taker.getMemento());
    System.out.println("第三次打印" + emp.getEname() + "---" + emp.getAge() + "---" + emp.getSalary());

日誌打印以下所示:

圖片

首先一開始就實例化一個發起人角色 Emp,它的內部狀態就是 ename、age 和 salary 屬性,發起人角色建立備忘錄角色 EmpMemento,順便把本身的狀態賦值給 EmpMemento 的狀態,而後管理者角色 CareTaker 就把備忘錄角色 EmpMemento 存儲起來;當咱們把發起人角色 Emp 的狀態修改後,咱們要想恢復以前的狀態,就必須經過 CareTaker 的 getMemento 方法獲取發起人角色 Emp,並經過 Emp 的 recovery 方法最終實現恢復以前的狀態
;這個就像咱們用文件編輯文字同樣,咱們要想恢復上一次編輯的記錄,就按下 Ctrl + z 就能撤回到上一次的記錄了。

備忘錄模式雖然給用戶提供了一種能夠恢復狀態的機制,可使用戶可以比較方便地回到某個歷史的狀態;實現了信息的封裝,使得用戶不須要關心狀態的保存細節,且狀態信息都保存到備忘錄裏由管理者管理;可是若是類的成員變量過多,會佔用很大的資源,同時保存也會消耗必定的內存;改動也很麻煩,當發起人角色的狀態發生改變時,備忘錄角色的狀態也會發生改變。

二、中介者模式

用一箇中介對象來封裝一系列對象的交互過程,中介者使各對象能夠隱式的相互引用,使其耦合鬆散,並且改變它們之間的交互是獨立的。

中介者模式具備如下幾種角色:

(1)抽象中介者角色:具體中介者要實現的接口,定義了同事對象註冊和發送給同事對象信息的抽象方法。

(2)具體中介者角色:實現抽象中介者角色的子類,聲明一個容器來管理同事對象,協助同事類角色的交互,它依賴於同事角色。

(3)抽象同事類角色:聲明具體同事類角色要實現的接口,定義同事對象交互的抽象方法。

(4)具體同事類角色:抽象同事類角色的子類,實現抽象同事類角色全部的抽象方法,須要與其餘同事對象進行交互時,由中介者對象協助。

下面用代碼舉個例子:

(1)抽象中介者角色,新建一個 Mediator接口:

public interface Mediator {

public void register(String name,Department d);
public void command(String name);

}

(2)抽象同事類角色,新建一個 Department接口:

public interface Department {
public void selfAction();
public void outAction();
}

(3)具體中介者角色,新建一個 President 類並實現 Mediator接口:

public class President implements Mediator{
private Map<String, Department> map = new HashMap<String, Department>();
@Override
public void register(String name, Department d) {

map.put(name, d);

}

@Override
public void command(String name) {

map.get(name).selfAction();

}

}

(4)具體同事類角色,新建一個 Market 類並實現 Department接口:

public class Market implements Department{
private Mediator mediator;
public Market(Mediator mediator) {

this.mediator = mediator;
mediator.register("market", this);

}
@Override
public void selfAction() {

System.out.println("Market---跑市場");

}

@Override
public void outAction() {

System.out.println("Market---彙報工做,項目正在進度");
mediator.command("finacial");
mediator.command("development");

}

}

(5)具體同事類角色,新建一個 Finacial 類並實現 Department接口:

public class Finacial implements Department{
public Finacial(Mediator mediator) {

mediator.register("finacial", this);

}
@Override
public void selfAction() {

System.out.println("Finacial---數錢");

}

@Override
public void outAction() {

System.out.println("Finacial---彙報工做,出帳");

}

}

(6)具體同事類角色,新建一個 Development 類並實現 Department接口:

public class Development implements Department{
public Development(Mediator mediator) {

mediator.register("development", this);

}
@Override
public void selfAction() {

System.out.println("Development---專心科研項目");

}

@Override
public void outAction() {

System.out.println("Development---沒錢了,須要資金支持");

}

}

(7)客戶端進行測試調用:

Mediator mediator = new President();
    Market market = new Market(mediator);
    new Finacial(mediator);
    new Development(mediator);
    market.selfAction();
    market.outAction();

日誌打印以下所示:

圖片

首先實例化一箇中介者角色對象 mediator (實現類是 President),而後實例化具體同事類角色對象 new Market(mediator)、new Finacial(mediator) 和 new Development(mediator),並順便把這些實例化具體同事類角色對象註冊到中介者角色對象 mediator 中,此時具體同事類角色對象就會被保存到 Mediator 的實現類 President 的 Map 容器中;當 Market 調用 outAction 方法時,在該方法內部順便經過中介者 mediator 的 command 方法調用 Finacial 的 outAction 方法和 Development 的 outAction 方法,最終實現了中介者模式的效果。

中介者模式雖然使類變得簡單,將一對多轉化成了一對一的關聯,下降了各個類之間的耦合性,遵循迪米特原則;可是中介者會龐大,變得複雜難以維護,當同事類越多時,中介者就會越臃腫,每添加一個同事類,調用同事類的中介者頗有可能要修改。

三、解釋器模式

定義一個語言,定義它的文法表示,再定義一個解釋器,這個解釋器用來解釋語言中的句子。

解釋器模式具備如下幾種角色:

(1)抽象表達式角色:聲明解釋器的接口,規範解釋器的解釋行爲。

(2)終結符表達式角色:是抽象表達式的子類,用來實現文法中與終結符相關的操做。

(3)非終結符表達式角色:是抽象表達式的子類,用來實現文法中與非終結符相關的操做,它最終也是調用文法中與終結符相關的操做。

(4)環境角色:通常包含傳遞被全部解釋器共享的數據或是公共的功能。

下面用代碼舉個例子:

(1)抽象表達式角色,新建一個 Expression 接口:

public interface Expression {
public boolean interpret(String s);
}

(2)終結符表達式角色,新建一個 TerminalExpression 類並實現 Expression 接口:

public class TerminalExpression implements Expression {
private Set<String> set = new HashSet<String>();

public TerminalExpression(String[] data) {

for (int i = 0; i < data.length; i++)
  set.add(data[i]);

}

public boolean interpret(String info) {

if (set.contains(info)) {
  return true;
}
return false;

}
}

(3)非終結符表達式角色,新建一個 AndExpression 類並實現 Expression 接口:

public class AndExpression implements Expression {
private Expression city = null;
private Expression person = null;

public AndExpression(Expression city, Expression person) {

this.city = city;
this.person = person;

}

public boolean interpret(String info) {

String s[] = info.split("的");
return city.interpret(s[0]) && person.interpret(s[1]);

}
}

(4)環境角色,新建一個 Context 類:

public class Context {
private String[] citys = { "深圳", "東莞" };
private String[] persons = { "老人", "學生", "兒童" };
private Expression cityPerson;

public Context() {

Expression city = new TerminalExpression(citys);
Expression person = new TerminalExpression(persons);
cityPerson = new AndExpression(city, person);

}

public void freeRide(String info) {

boolean b = cityPerson.interpret(info);
if (b) {
  System.out.println("這是" + info + ",本次進園旅遊打5折!");
} else {
  System.out.println(info + ",不屬於享半價優惠人員,按原價收費");

}

}
}

(5)客戶端進行測試調用:

Context c = new Context();
c.freeRide("深圳的老人");
c.freeRide("東莞的年輕人");
c.freeRide("北京的婦女");
c.freeRide("上海的兒童");
c.freeRide("東莞的學生");

日誌打印以下所示:

圖片

首先實例化一個 Context 對象,在其構造方法中實例化2個終結符表達式角色對象 TerminalExpression 並傳遞字符串數組 citys 和 persons,建立好2個 TerminalExpression 對象以後,而後再建立非終結符表達式角色對象 AndExpression 並把2個 TerminalExpression 對象做爲參數傳入到它(AndExpression)的構造方法中;最後調用 Context 對象的 freeRide 方法,freeRide 方法的調用過程是這樣的 freeRide --> AndExpression.interpret--> TerminalExpression.interpret,最終由終結符表達式角色 TerminalExpression 來解釋語言中的句子;在這過程當中 AndExpression 的 interpret 方法將「的」字切割出來,而後用 city.interpret 方法判斷地方,person.interpret 判斷年齡段的人。

雖然解釋器模式在解釋器模式中使用類來展現語言的文法規則,經過繼承可讓拓展性變得更好,語法樹中的表達式節點類是類似的,比較好實現;可是它會引發類膨脹,採用遞歸調用方法層次太多會容易出現棧溢出,可利用場景比較少。

相關文章
相關標籤/搜索