程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> 設計模式讀書筆記——行為型模式

設計模式讀書筆記——行為型模式

編輯:JAVA綜合教程

設計模式讀書筆記——行為型模式


1. chain of responsibility 職責鏈

使用: · 有多個對象可以處理一個請求, 而具體由哪個對象處理請求需要在運行時刻自動確定。
· 可以處理一個請求的對象集合應被動態指定

原理: 在鏈上的每個對象都有一致的處理請求的接口, 和訪問鏈上後繼者的接口。
從鏈上的第一個對象開始,要麼親自處理他,要麼轉發給鏈中的下一個候選者。
提交請求的客戶並不明確知道哪一個對象會處理他,提交請求的可以並不直接引用最終響應他的對象。

實現:class HelpHandler ///////// 基類,維護鏈的父節點引用(successor)
{
HelpHandler(HelpHander * successor):_successor(successor){}
virtual void HandleEvent(Event e)
{
_successor->HandleEvent(e);
}
private:
HelpHandler * _successor;
};
class Bottun: public HelpHandler
{
Bottun(HelpHandler * h):HelpHandler(h){}
void HandleEvent(Event e)
{
if( can deal with event)
{
do some thing;
}
else
{
HelpHandler::HandleEvent(e); //////交給鏈的下一個對象
}
}
};
class Dialog: public HelpHandler
{
Dialog(HelpHandler * h):HelpHandler(h){}
void HandleEvent(Event e)
{
if( can deal with event)
{
do some thing;
}
else
{
HelpHandler::HandleEvent(e); //////交給鏈的下一個對象
}
}
};

app:
{
dialog = new Dialog(0); /////他下面沒有結點
button = new Button(&dialog); //鏈上他的下一結點
button->HandEvent(event); ///// event 可能會被dialog 或 button中的一個處理。
}


2. command 命令

使用: 1. command將調用操作的對象,與知道如何實現該操作的對象解耦。(命令的接收者在聲明命令對象的時候裝配)
但是這帶來的好處是什麼呢? 命令作為一個對象可以自己提供execute方法執行自己,省去了響應命令時的switch?或者是
響應命令的總入口(對象)不需要知道自己管理提供的所有功能了,也不需要自己去區分那個命令由那個功能處理了。
可能命令接收者僅能提供一些基礎功能,而處理每個命令則需要對基礎功能進行排序組合來實現,這樣可以放到command裡面自己去
實現(使用基礎功能)。

2. 把命令作為一個對象處理,他有了一定的聲明周期,為回滾提供了可能,只要每個command實現UnExecute方法。

3. 可以將多個命令組合為一個復合命令(組合模式)。
4. 新增command容易,不影響現有的類。

原理:將command封裝成一個類,自己提供Execute方法。 同時自己可能會維護一個命令接收者的引用(在聲明命令時注冊進去),這是提供具體動作的對象。
必須考慮: 命令到底達到何種智能程度,是自己把命令全處理完,還是直接轉交給命令接收者。 這裡有一個度要根據情況把握。

多個命令還可以組合成一個。

實現:
class Command
{
virtual void Execute() = 0;
};
class OpenCommand : Command
{
  OpenCommand(Application * a):__application(_application){}
void Execute()
{
_application->AddDocument();
}
private:
Application *_application;
};

app:
{
Application app; ////命令的接收者
Command * open = new OpenCommand(&app);
open->Execute();
}


3. Interpreter 解釋器

解釋器和組合類似,實現中一定會用到組合。 他們的不同一點是組合重在通過一種通用的方式表達對象,即便
是一群對象,表達起來也像是一個對象一樣。

解釋器是為了解決特定問題,自行設定了一種語音給clientAPP,或用戶界面。通過翻譯這種語音,確定了解釋器樹(或列表)
中的每個對象(解釋某一特定語法的子類)。

他和組合模式比首先多了一步翻譯語音,將語言翻譯成為各個解釋器子類(構建語法數)。 並且各個子類的行為也可以不一樣,比如字符串查找 “以什麼開頭,以什麼結尾,包含什麼。。。”
#a 以a開頭; $a 以a結尾 %a 包含a。
則可通過定義三個子類, 並且通過翻譯語言 : #ss$b 查找以ss開頭以b結尾的字符串.


4. Iterator 迭代器

動機: 提供一個方法來遍歷一個聚合對象,而又不暴露該對象的內部表示。
List的iterator就是一個實例, 我們並不知道數據在list中是怎麼存儲的。

這樣還可以支持對同一個聚合對象同時進行多個遍歷、不同的遍歷。使用不同的迭代器就可以了。
為遍歷聚合提供了統一的接口。

實現:
template < class Item>
class Aggregate
{
long count();
Item & getitem(long index); /////實際的聚合對象,他自己提供一個根據下標獲取元素的方法,這樣iterator就可以不聲明為他的友元了。
把迭代器聲明為聚合的一個友元,也是一種方法,但是破壞了聚合的封裝性。
};

class Iterator ////抽象迭代器接口
{
virtual void first() = 0;
virtual void next() = 0;
virtual bool isDone() const = 0;
virtual Item CurrentItem() const = 0;
};
class ConceretIterator : public Iterator
{
ConceretIterator(const Aggregate & a):aggregate(a),_current(0){}
void first()
{
_current = 0;
}
void next()
{
_current++;
}
bool isDone() const
{
_current == aggregate.count();
}
virtual Item CurrentItem() const
{
return aggregate.getitem(_current);
}
private:
long _current;
Aggregate & aggregate;
};
上面是一個外部迭代器,就是對item的操作在外面app中進行,迭代器只是把item返回出去了。
app:
{
ConceretIterator i(Aggregate);
for(i.first();!i.IsDone();i.next()
{
item & item = i.CurrentItem();
}
}

還可以在aggregate裡面定義工廠,通過工廠方法創建相應的迭代器。★ 因為工廠通常要使用new,所以迭代器也可以再包一個代理,以便迭代器對象的釋放。
即 IteratorProxy(CreateIterate());IteratorProxy本身是迭代器的proxy模式,CreateIterate是工廠模式,裡面使用new方法。
這時需要IteratorProxy重載->和*方法,把包含的對象吐出來。在IteratorProxy的析構裡面delete工廠new出來的對象。

還有一種內部迭代器,即把對象的操作自己搞定。
class ListTraverser{
ListTraverser(Aggregate & a):iter(a){}
bool traverse()
{
for(iter.first();!iter.IsDone();iter.next()
{
ProcessItem( iter.CurrentItem());
}
}
protected :
virtual bool ProcessItem(const Item &); ///// item的處理函數
private:
Iterator & iter; // 自己持有一個迭代器
};


5. mediator 中介者

意圖: 當一組對象以復雜的方式通信,產生的互相依賴關系混亂切難以理解時。
想定義一個分布在多個類中的行為,又不想生成太多的子類。
當一個類引用了很多其他對象,導致其復用性下降。
這時可以通過一個中介者,由中介者來持有和維護一系列對象的交互,協調各個同事對象協作行為。

原理:用一個中介來封裝一系列對象,使得各對象不需要顯式的互相引用,從而使其松散耦合。

實現:
class DialogDirector
{
void changed() //////中介對外的接口
{
_ok.xxx();
_cancel.xxx();
if(_fontlist changed)
{
xxxx;
}
}
private:
Button & _ok; /////////維護幾個同事對象,協調他們之間工作
Button & _cancel;
ListBox & _fontlist;
};

6. memento 備忘錄

意圖: 在不破壞封裝性的前提下獲取一個對象的內部狀態,並將其保存在對象之外,以便以後需要時恢復對象原先保存的狀態。

原理: 被保存狀態的對象叫原發器。 原發器自己提供接口生成一個自己的備忘錄對象,並提供恢復狀態接口來裝入備忘錄。
即原發器自己定義自己的備忘錄,並提供接口返回出來。 應用程序可以拿到這個備忘錄,並且在需要時還給原發器去恢復數據。

實現:

class OriginatorMemento ///備忘錄
{
~Originator();
private:
firend class Originator;
OriginatorMemento();
};

class Originator ///原發器
{
OriginatorMemento * CreateMemento();
void setMemento(OriginatorMemento & memento);
};

app
{
OriginatorMemento * _state = Originator.CreateMemento();
/////////////////////////////// ........

Originator.setMemento(_state);/////可通過此方式導入備忘錄
}


其實就好像以加密的方式把數據備份一個文件出來一樣,怎麼備份和使用數據全是原發器的事情,對外完全封裝。


7. observer 觀察者

意圖: 定義對象間的一種一對多的關系。 當一個對象的狀態發生改變時,所有依賴於他的對象都得到通知並自動做更新。

原理: 觀察者的是一個對象,他可以持有被觀察對象的引用。 被觀察對象提供一個注冊方法,把希望觀察他的對象們注冊到他內部。
所有的觀察者都繼承自抽象的接口。
當被觀察者發生改變時,通過自己的notify接口,依次調用注冊到他內部的觀察者的update方法,並把自己傳過去,以此達到觀察者們感知他變化的目的。

實現:

class Observer
{
virtual void Update(Subject *) = 0;
};

class ConceretObserver : public Observer
{
void Update(Subject * s)
{
XXXXXXXXXXXXXXXX;
}
};

class Subject
{
void Register(Observer * o)
{
m_oberList->push_back(o);
}
void Notify()
{
for(i = m_oberList.begin(); i!= m_oberList.end(); i++)
{
(*i)->Update(this);
}
}
void Function()//////////// 目標類某個具體函數。 當裡面發生數據改變時,調用Notify觸發他的觀察者。
{
////////////
Notify();
}
private:
list m_oberList;
};

★ 如果有這種情況:當一個操作涉及多個互相依賴的目標,當這些目標的修改全部完成之後才能通知他們的觀察者,這時可以引入一個ChangeManager,負責維護目標和觀察者之間的關系和通知時機。
這種情況下,changeManager是觀察者和目標之間的Mediator模式。 changeManager通常用單件來實現。

還有可能,觀察者僅希望觀察特定事件的改變,這時可以通過注冊接口定義他感興趣的觀察事件。可以節省一定的無效通知。

8. state 狀態

意圖: 允許一個對象在其內部狀態改變時改變他的行為。對象看起來就像修改了他的類。

原理: Context定義客戶感興趣的接口,並維護一個具體的狀態子類的引用,這個狀態子類對應Context的當前狀態。
通過抽象的state定義一個接口,封裝與context一個特定狀態相關的行為。 然後通過具體狀態子類來實現與一個context狀態相關的行為。
context將與狀態相關的請求委托給當前的具體state子類。context可以將自己作為參數傳遞給處理該請求的狀態對象,狀態對象可以訪問context。
客戶程序調用的接口還是context的接口。
context和具體state子類都可以決定哪個狀態是另外一個狀態的後繼者。

他將與特定狀態相關的行為局部化。


9. strategy 策略

意圖: 定義一系列算法,把他們一個一個封裝起來,並且他們可以互相替換。使得算法可以獨立於使用他的客戶而變化。
比如可以有很多個文字換行算法,每一個算法封裝成一個策略。
程序在當不同的時候可以用不同的算法。

如果在context裡面直接實現算法,則會將context的實現與算法實現混合起來,從而是context難以維護。並且算法是硬編碼的,不能動態改變算法。
如果在這種情況下派生context,則會得到一堆相關的類,而唯一的區別就是算法。

這樣不如將算法的實現獨立於context,使得context可以切換他使用的算法。


實現:可以用strategy作為類模版參數來配置類,但這樣可以實現在編譯時選擇strategy,無法在運行時實現選擇strategy。
另一種方式:

class Context
{
Context( strategy * s ):_s(s){}

void dosth()////////// 應用程序實現某個功能
{
_s->compose(); /////////調用算法的抽象接口
}
private:
strategy * _s;
};

class strategy
{
virtual void compose() = 0;
};

class conceretStrategy1 : public strategy
{
void compose()
{
XXXXXXXXXXXXXX;
}
};

class conceretStrategy2 : public strategy
{
void compose()
{
XXXXXXXXXXXXXX;
}
};

app
{
Context * s1Context = new Context( new conceretStrategy1);
Context * s2Context = new Context( new conceretStrategy2);

s1Context->compose();
}

10. template method 模版方法

意圖: 定義一個操作中的算法的骨架,並將一些步驟延遲到子類中去。 使得子類可以不改變算法的結構即可重定義算法的某些特定步驟(或具體操作)。

一次性實現一個算法的不變部分,並將可變的行為留給子類來實現。

實現: 一個模版方法調用的原語操作可以定義為protected,保證只被模版方法調用。 必須重定義的原語操作定義為純虛函數。
模版方法自身不需要被重定義,因此定義為非虛函數。

class AbstractClass
{
void TemplateMethod() //////////實現一個算法的骨架,裡面使用可被重載的方法DoOperate(), 並在他的子類中重載; 子類使用父類的算法骨架,重定義自身的具體操作。
{
XXXXXXXX;
DoOperate1();
XXXXXXXXXXXXX;
DoOperate2();
xxxxxxxxxxx;
}
virtual void DoOperate1() = 0;
virtual void DoOperate2() = 0;
};

class ConceretClass : public AbstractClass
{
virtual void DoOperate1()
{
XXXXXXXXXXXXXXXX;
}
virtual void DoOperate2()
{
XXXXXXXXXXX;
}
};
模版方法基於繼承技術,不如strategy靈活。

11. visitor 訪問者

意圖: 一個作用於某對象結構中的各元素的操作, 可以在不改變各元素類的前提下定義對這些元素的新的操作。
需要對一個對象結構中的對象進行很多不相關的操作,而不想這些操作互相干擾。

訪問者使得增加依賴於負責對象的操作變得容易。僅需增加一個新的訪問者幾個在對象結構上定義一個新操作。
訪問者可以集中相關的操作而分離無關的操作。使得相關的操作行為集中在訪問者中而不是分布在對象結構中。
增加新的操作變得容易。

他強調的是操作的多樣和獨立。

原理: 定義一個抽象的visitor,裡面確定一個訪問對象元素的接口。
用具體的conceretVisitor繼承自visitor。
用抽象元素定一個接受訪問者的接口,比如Accept操作,以一個訪問者為參數。
具體的元素繼承自抽象元素,實現accept操作。

實現:
class Equipment
{
virtual int discountPrice();
virtual accept(EquipmentVisitor * v);
};

class FloppyDisk : Equipment
{
accept(EquipmentVisitor * v)
{
v.visitFloppyDisk(this); /////////////每一個具體的Element對象的accept方法實現都不同,使用的是visitor中不同的方法,但是這些方法都是被重載的、有統一接口原型的。即
每個具體的Element對象都可以有多個visitor,他的多個visitor有共同的抽象接口。
}
}
class EquipmentVisitor
{
virtual void visitFloppyDisk(FloppyDisk * f);
virtual void visitBus(Bus * b);
};

class InventoryVisitor: public EquipmentVisitor
{
void visitFloppyDisk(FloppyDisk * f)
{
f->Accumulate(); ////////計算FloppyDisk的存貨數量
}
}; 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved