行爲型模式:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式
策略模式(Strategy)
策略模式定義了一系列算法,並將每個算法封裝起來,使他們可以相互替換,且算法的變化不會影響到使用算法的客戶。需要設計一個接口,爲一系列實現類提供統一的方法,多個實現類實現該接口,也可以設計一個抽象類(可有可無,屬於輔助類),提供輔助函數
//統一的接口
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;
}
}
//接口的三個實現類:
public class Plus extends AbstractCalculator implements ICalculator {
public int calculate(String exp) {
int arrayInt[] = split(exp,"[+]");
return arrayInt[0]+arrayInt[1];
}
}
public class Minus extends AbstractCalculator implements ICalculator {
public int calculate(String exp) {
int arrayInt[] = split(exp,"-");
return arrayInt[0]-arrayInt[1];
}
}
public class Multiply extends AbstractCalculator implements ICalculator {
public int calculate(String exp) {
int arrayInt[] = split(exp,"[*]");
return arrayInt[0]*arrayInt[1];
}
}
//測試類
public class Test {
public static void main(String[] args) {
String exp = "2+8";
ICalculator cal = new Plus();
int result = cal.calculate(exp);
System.out.println(result);
}
}
策略模式的決定權在用戶,系統本身提供不同算法的實現,新增或者刪除算法,對各種算法做封裝。因此,策略模式多用在算法決策系統中,外部用戶只需要決定用哪個算法即可。
我們之前在學校TreeSet排序的時候,有一種叫做資客戶化排序的方式,就是給TreeSet傳一個比較器對象,這個其實就是使用了策略模式
模板方法模式(Template Method)
模板方法模式,就是指:一個抽象類中,有一個主方法,再定義1…n個方法,可以是抽象的,也可以是實際的方法,定義一個類,繼承該抽象類,重寫抽象方法,通過調用抽象類的方法,實現對子類的調用其實就是我們之前所說的: 子類重新/實現父類中的方法,那麼調用該方法的時候則是調用到了子類中重寫之後的方法
//父類
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 {
public int calculate(int num1,int num2) {
return num1 + num2;
}
}
//測試類
public class Test {
public static void main(String[] args) {
String exp = "8+8";
AbstractCalculator cal = new Plus();
int result = cal.calculate(exp, "\\+");
System.out.println(result);
}
}
觀察者模式(Observer)
觀察者模式很好理解,類似於郵件訂閱和RSS訂閱,當我們瀏覽一些博客時,經常會看到RSS圖標,就這的意思是,當你訂閱了該文章,如果後續有更新,會及時通知你。其實,簡單來講就一句話:當一個對象變化時,其它依賴該對象的對象都會收到通知,並且隨着變化!對象之間是一種一對多的關係。
我們在GUI那一章學習的事件監聽機制就是可以這種設置模式來構建的代碼
//觀察者接口
public interface Observer {
public void update();
}
//觀察者1
public class Observer1 implements Observer {
public void update() {
System.out.println("observer1 has received!");
}
}
//觀察者2
public class Observer2 implements Observer {
public void update() {
System.out.println("observer2 has received!");
}
}
//被觀察者接口
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>();
public void add(Observer observer) {
vector.add(observer);
}
public void del(Observer observer) {
vector.remove(observer);
}
public void notifyObservers() {
Iterator<Observer> it = vector.iterator();
while(it.hasNext()){
Observer next = it.next();
next.update();
}
}
}
//我們自己的一個被觀察者實現 裏面可以有我們自己的各種屬性和方法
public class MySubject extends AbstractSubject {
public void operation() {
System.out.println("update self!");
notifyObservers();
}
}
//測試類
public class Test {
public static void main(String[] args) {
Subject sub = new MySubject();
sub.add(new Observer1());
sub.add(new Observer2());
sub.operation();
}
}
迭代器模式(Iterator)
顧名思義,迭代器模式就是順序訪問聚集中的對象,一般來說,集合中非常常見,如果對集合類比較熟悉的話,理解本模式會十分輕鬆。
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"};
public Iterator iterator() {
return new MyIterator(this);
}
public Object get(int i) {
return string[i];
}
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;
}
public Object previous() {
if(pos > 0){
pos--;
}
return collection.get(pos);
}
public Object next() {
if(pos<collection.size()-1){
pos++;
}
return collection.get(pos);
}
public boolean hasNext() {
if(pos<collection.size()-1){
return true;
}else{
return false;
}
}
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());
}
}
}
責任鏈模式(Chain of Responsibility)
責任鏈模式,有多個對象,每個對象持有對下一個對象的引用,這樣就會形成一條鏈,請求在這條鏈上傳遞,直到某一對象決定處理該請求。但是發出者並不清楚到底最終那個對象會處理該請求,所以,責任鏈模式可以實現,在隱瞞客戶端的情況下,對系統進行動態的調整。
(web應該中學習到的Filter其實就是一個責任鏈設計模式)
public interface Handler {
public void operator();
}
public class MyHandler implements Handler {
private String name;
private Handler handler;
public MyHandler(String name) {
this.name = name;
}
public Handler getHandler() {
return handler;
}
public void setHandler(Handler handler) {
this.handler = handler;
}
public void operator() {
System.out.println("name = "+name);
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();
}
}
命令模式(Command)
命令模式很好理解,舉個例子,司令員下令讓士兵去幹件事情,從整個事情的角度來考慮,司令員的作用是,發出口令,口令經過傳遞,傳到了士兵耳朵裏,士兵去執行。這個過程好在,三者(司令、命令、士兵)相互解耦,任何一方都不用去依賴其他人的具體實現,只需要做好自己的事兒就行,司令員要的是結果,不會去關注到底士兵是怎麼實現的。
//命令執行的接口
public interface Command {
public void exe();
}
//具體實現的命令
public class MyCommand implements Command {
private Receiver receiver;
public MyCommand(Receiver receiver) {
this.receiver = receiver;
}
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();
}
}
這個很好理解,命令模式的目的就是達到命令的發出者和執行者之間解耦,實現請求和執行分開。
對於大多數請求-響應模式的功能,比較適合使用命令模式。
備忘錄模式(Memento)
也可以叫備份模式,主要目的是保存一個對象的某個狀態,以便在適當的時候恢復對象:假設有原始類A,A中有各種屬性,A可以決定需要備份的屬性,備忘錄類B是用來存儲A的一些內部狀態,類C呢,就是一個用來存儲備忘錄的,且只能存儲,不能修改等操作。
//原始類,裏面有需要保存的屬性value
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();
}
}
//備忘錄類,用來保存value值
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;
}
}
//存儲備忘錄的類,持有Memento類的實例
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());
}
}
狀態模式(State)
核心思想就是:當對象的狀態改變時,同時改變其行爲,很好理解!就拿QQ來說,有幾種狀態,在線、隱身、忙碌等,每個狀態對應不同的操作。再比如交通燈,有紅黃綠三種狀態,每種狀態下操作也是不一樣的
//狀態類
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!");
}
}
//Context類可以實現切換狀態
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();
}
}
訪問者模式(Visitor)
訪問者模式把數據結構和作用於結構上的操作解耦合,使得對數據操作可相對自由地演化。訪問者模式適用於數據結構相對穩定,算法又易變化的系統。
因爲訪問者模式使得算法操作增加變得容易。若系統數據結構對象易於變化,經常有新的數據對象增加進來,則不適合使用訪問者模式。訪問者模式的優點是增加操作很容易,因爲增加操作意味着增加新的訪問者。訪問者模式將有關行爲集中到一個訪問者對象中,其改變不影響系統數據結構。其缺點就是增加新的數據結構很困難。
簡單來說,訪問者模式就是一種分離對象數據結構與行爲的方法,通過這種分離,可達到爲一個被訪問者動態添加新的操作而無需做其它的修改的效果。
//訪問者接口
public interface Visitor {
public void visit(Subject sub);
}
//訪問者的一個具體實現
public class MyVisitor implements Visitor {
public void visit(Subject sub) {
System.out.println("visit the subject:"+sub.getSubject());
}
}
//被訪問者接口
public interface Subject {
public void accept(Visitor visitor);
public String getSubject();
}
//被訪問者的一個具體實現
public class MySubject implements Subject {
public void accept(Visitor visitor) {
visitor.visit(this);
}
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);
}
}
該模式適用場景:如果我們想爲一個現有的類增加新功能,不得不考慮幾個事情:
1、新功能會不會與現有功能出現兼容性問題?
2、以後會不會再需要添加?
3、如果類不允許修改代碼怎麼辦?
面對這些問題,最好的解決方法就是使用訪問者模式,訪問者模式適用於數據結構相對穩定的系統,把數據結構和算法解耦
中介者模式(Mediator)
這個模式也是用來降低類和類之間的耦合的,因爲如果類和類之間有依賴關係的話,不利於功能的拓展和維護,因爲只要修改一個對象,其它關聯的對象都得進行修改。如果使用中介者模式,只需關心和Mediator類的關係,具體類類之間的關係及調度交給Mediator就行
//中間者接口
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;
}
public void createMediator() {
user1 = new User1(this);
user2 = new User2(this);
}
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();
}
//User1
public class User1 extends User {
public User1(Mediator mediator){
super(mediator);
}
public void work() {
System.out.println("user1 exe!");
}
}
//User2
public class User2 extends User {
public User2(Mediator mediator){
super(mediator);
}
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();
}
}
適用場景
在面向對象編程中,一個類必然會與其他的類發生依賴關係,完全獨立的類是沒有意義的。一個類同時依賴多個類的情況也相當普遍,既然存在這樣的情況,說明,一對多的依賴關係有它的合理性,適當的使用中介者模式可以使原本凌亂的對象關係清晰,但是如果濫用,則可能會帶來反的效果。一般來說,只有對於那種同事類之間是網狀結構的關係,纔會考慮使用中介者模式。可以將網狀結構變爲星狀結構,使同事類之間的關係變的清晰一些。
解釋器模式(Interpreter)
在以下情況下可以使用解釋器模式:
- 有一個簡單的語法規則,比如一個sql語句,如果我們需要根據sql語句進行其他語言的轉換,就可以使用解釋器模式來對語句進行解釋。
- 一些重複發生的問題,比如加減乘除四則運算,但是公式每次都不同,有時是a+b-c*d,有時是a*b+c-d,等等等等個,公式千變萬化,但是都是由加減乘除四個非終結符來連接的,這時我們就可以使用解釋器模式。
//解釋器接口(這裏的是專門解析數學運算表達式)
public interface Expression {
public int interpret(Context context);
}
//加法
public class Plus implements Expression {
public int interpret(Context context) {
return context.getNum1()+context.getNum2();
}
}
//減法
public class Minus implements Expression {
public int interpret(Context context) {
return context.getNum1()-context.getNum2();
}
}
//Context類是一個上下文環境類 持有運行中所需的數據
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));
//相當於:new Minus().interpret(new Context(11, 8));
System.out.println(result);
}
}