熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java高級技術 >> 正文

設計模式分解java(6)

2022-06-13   來源: Java高級技術 

  /***/

  行為模式Command

  Command模式是最讓我疑惑的一個模式我在閱讀了很多代碼後

  才感覺隱約掌握其大概原理我認為理解設計模式最主要是掌握起原理構造

  這樣才對自己實際編程有指導作用Command模式實際上不是個很具體

  規定很多的模式正是這個靈活性讓人有些confuse

  Command定義

  不少Command模式的代碼都是針對圖形界面的它實際就是菜單命令

  我們在一個下拉菜單選擇一個命令時然後會執行一些動作

  將這些命令封裝成在一個類中然後用戶(調用者)再對這個類進行操作這就是Command模式

  換句話說本來用戶(調用者)是直接調用這些命令的如菜單上打開文檔(調用者)

  就直接指向打開文檔的代碼使用Command模式就是在這兩者之間增加一個中間者

  將這種直接關系拗斷同時兩者之間都隔離基本沒有關系了

  顯然這樣做的好處是符合封裝的特性降低耦合度

  Command是將對行為進行封裝的典型模式

  Factory是將創建進行封裝的模式

  從Command模式我也發現設計模式一個通病:好象喜歡將簡單的問題復雜化

  喜歡在不同類中增加第三者當然這樣做有利於代碼的健壯性 可維護性 還有復用性

  如何使用?

  具體的Command模式代碼各式各樣因為如何封裝命令不同系統有不同的做法

  下面事例是將命令封裝在一個Collection的List中任何對象一旦加入List中實際上裝入了一個封閉的黑盒中

  對象的特性消失了只有取出時才有可能模糊的分辨出:

  典型的Command模式需要有一個接口接口中有一個統一的方法這就是將命令/請求封裝為對象:

  public interface Command {

  public abstract void execute ( );

  }

  具體不同命令/請求代碼是實現接口Command下面有三個具體命令

  public class Engineer implements Command {

  public void execute( ) {

  //do Engineers command

  }

  }

  public class Programmer implements Command {

  public void execute( ) {

  //do programmers command

  }

  }

  public class Politician implements Command {

  public void execute( ) {

  //do Politicians command

  }

  }

  按照通常做法我們就可以直接調用這三個Command但是使用Command模式

  我們要將他們封裝起來扔到黑盒子List裡去:

  public class producer{

  public static List produceRequests() {

  List queue = new ArrayList();

  queueadd( new DomesticEngineer() );

  queueadd( new Politician() );

  queueadd( new Programmer() );

  return queue;

  }

  }

  這三個命令進入List中後已經失去了其外表特征以後再取出

  也可能無法分辨出誰是Engineer 誰是Programmer了看下面如何調用Command模式:

  public class TestCommand {

  public static void main(String[] args) {

  List queue = ProducerproduceRequests();

  for (Iterator it = erator(); ithasNext(); )

  //取出List中東東其他特征都不能確定只能保證一個特征是%正確

  // 他們至少是接口Command的兒子所以強制轉換類型為接口Command

  ((Command)itnext())execute();

  }

  }

  由此可見調用者基本只和接口打交道不合具體實現交互這也體現了一個原則

  面向接口編程這樣以後增加第四個具體命令時就不必修改調用者TestCommand中的代碼了

  理解了上面的代碼的核心原理在使用中就應該各人有自己方法了特別是在如何分離調用者和具體命令上

  有很多實現方法上面的代碼是使用從List過一遍的做法這種做法只是為了演示

  使用Command模式的一個好理由還因為它能實現Undo功能

  每個具體命令都可以記住它剛剛執行的動作並且在需要時恢復

  Command模式在界面設計中應用廣泛Java的Swing中菜單命令都是使用Command模式

  /***/

  行為模式State

  State的定義: 不同的狀態不同的行為;或者說每個狀態有著相應的行為

  何時使用?

  State模式在實際使用中比較多適合狀態的切換因為我們經常會使用If elseif else 進行狀態切換

  如果針對狀態的這樣判斷切換反復出現我們就要聯想到是否可以采取State模式了

  不只是根據狀態也有根據屬性如果某個對象的屬性不同對象的行為就不一樣

  這點在數據庫系統中出現頻率比較高我們經常會在一個數據表的尾部

  加上property屬性含義的字段用以標識記錄中一些特殊性質的記錄

  這種屬性的改變(切換)又是隨時可能發生的就有可能要使用State

  是否使用?

  在實際使用類似開關一樣的狀態切換是很多的但有時並不是那麼明顯取決於你的經驗和對系統的理解深度

  這裡要闡述的是開關切換狀態 一般的狀態判斷是有一些區別的

  一般的狀態判斷也是有 ifelseif結構例如:

  if (which==) state=hello;

  else if (which==) state=hi;

  else if (which==) state=bye;

  這是一個 一般的狀態判斷state值的不同是根據which變量來決定的which和state沒有關系如果改成:

  if (stateeuqals(bye)) state=hello;

  else if (stateeuqals(hello)) state=hi;

  else if (stateeuqals(hi)) state=bye;

  這就是 開關切換狀態是將state的狀態從hello切換到hi

  再切換到bye;在切換到hello好象一個旋轉開關這種狀態改變就可以使用State模式了

  如果單純有上面一種將hello>hi>bye>hello這一個方向切換

  也不一定需要使用State模式因為State模式會建立很多子類復雜化

  但是如果又發生另外一個行為:將上面的切換方向反過來切換或者需要任意切換就需要State了

  請看下例:

  public class Context{

  private Color state=null;

  public void push(){

  //如果當前red狀態 就切換到blue

  if (state==Colorred) state=Colorblue;

  //如果當前blue狀態 就切換到green

  else if (state==Colorblue) state=Colorgreen;

  //如果當前black狀態 就切換到red

  else if (state==Colorblack) state=Colorred;

  //如果當前green狀態 就切換到black

  else if (state==Colorgreen) state=Colorblack;

  Sample sample=new Sample(state);

  sampleoperate();

  }

  public void pull(){

  //與push狀態切換正好相反

  if (state==Colorgreen) state=Colorblue;

  else if (state==Colorblack) state=Colorgreen;

  else if (state==Colorblue) state=Colorred;

  else if (state==Colorred) state=Colorblack;

  Sample sample=new Sample(state);

  sampleoperate();

  }

  }

  在上例中我們有兩個動作push推和pull拉這兩個開關動作改變了Context顏色

  至此我們就需要使用State模式優化它

  另外注意:但就上例state的變化只是簡單的顏色賦值這個具體行為是很簡單的

  State適合巨大的具體行為因此在就本例實際使用中也不一定非要使用State模式

  這會增加子類的數目簡單的變復雜

  例如: 銀行帳戶 經常會在Open 狀態和Close狀態間轉換

  例如: 經典的TcpConnection Tcp的狀態有創建 偵聽 關閉三個

  並且反復轉換其創建 偵聽 關閉的具體行為不是簡單一兩句就能完成的適合使用State

  例如:信箱POP帳號 會有四種狀態 start HaveUsername Authorized quit

  每個狀態對應的行為應該是比較大的適合使用State

  例如:在工具箱挑選不同工具可以看成在不同工具中切換適合使用State

  如具體繪圖程序用戶可以選擇不同工具繪制方框 直線 曲線這種狀態切換可以使用State

  如何使用

  State需要兩種類型實體參與:

  state manager 狀態管理器 就是開關 如上面例子的Context實際就是一個state manager

  在state manager中有對狀態的切換動作

  用抽象類或接口實現的父類不同狀態就是繼承這個父類的不同子類

  以上面的Context為例我們要修改它建立兩個類型的實體

  第一步: 首先建立一個父類:

  public abstract class State{

  public abstract void handlepush(Context c);

  public abstract void handlepull(Context c);

  public abstract void getcolor();

  }

  父類中的方法要對應state manager中的開關行為在state manager中

  本例就是Context中有兩個開關動作push推和pull拉

  那麼在狀態父類中就要有具體處理這兩個動作:handlepush() handlepull();

  同時還需要一個獲取push或pull結果的方法getcolor()

  下面是具體子類的實現:

  public class BlueState extends State{

  public void handlepush(Context c){

  //根據push方法如果是blue狀態的切換到green ;

  csetState(new GreenState());

  }

  public void handlepull(Context c){

  //根據pull方法如果是blue狀態的切換到red ;

  csetState(new RedState());

  }

  public abstract void getcolor(){ return (Colorblue)}

  }

  同樣 其他狀態的子類實現如blue一樣

  第二步: 要重新改寫State manager 也就是本例的Context:

  public class Context{

  private Sate state=null; //我們將原來的 Color state 改成了新建的State state;

  //setState是用來改變state的狀態 使用setState實現狀態的切換

  pulic void setState(State state){

  thisstate=state;

  }

  public void push(){

  //狀態的切換的細節部分在本例中是顏色的變化已經封裝在子類的handlepush中實現這裡無需關心

  statehandlepush(this);

  //因為sample要使用state中的一個切換結果使用getColor()

  Sample sample=new Sample(stategetColor());

  sampleoperate();

  }

  public void pull(){

  statehandlepull(this);

  Sample sample=new Sample(stategetColor());

  sampleoperate();

  }

  }

  至此我們也就實現了State的refactorying過程

  以上只是相當簡單的一個實例在實際應用中handlepush或handelpull的處理是復雜的

  /***/

  行為模式Strategy

  Strategy是屬於設計模式中 對象行為型模式主要是定義一系列的算法把這些算法一個個封裝成單獨的類

  Stratrgy應用比較廣泛比如 公司經營業務變化圖

  可能有兩種實現方式一個是線條曲線一個是框圖(bar)這是兩種算法可以使用Strategy實現

  這裡以字符串替代為例 有一個文件我們需要讀取後希望替代其中相應的變量

  然後輸出關於替代其中變量的方法可能有多種方法這取決於用戶的要求所以我們要准備幾套變量字符替代方案

  首先我們建立一個抽象類RepTempRule 定義一些公用變量和方法:

  public abstract class RepTempRule{

  protected String oldString=;

  public void setOldString(String oldString){

  thisoldString=oldString;

  }

  protected String newString=;

  public String getNewString(){

  return newString;

  }

  public abstract void replace() throws Exception;

  }

  在RepTempRule中 有一個抽象方法abstract需要繼承明確這個replace裡其實是替代的具體方法

  我們現在有兩個字符替代方案

  將文本中aaa替代成bbb;

  將文本中aaa替代成ccc;

  對應的類分別是RepTempRuleOne RepTempRuleTwo

  public class RepTempRuleOne extends RepTempRule{

  public void replace() throws Exception{

  //replaceFirst是jdk新特性

  newString=oldStringreplaceFirst(aaa bbbb)

  Systemoutprintln(this is replace one);

  }

  }

  public class RepTempRuleTwo extends RepTempRule{

  public void replace() throws Exception{

  newString=oldStringreplaceFirst(aaa ccc)

  Systemoutprintln(this is replace Two);

  }

  }

  第二步我們要建立一個算法解決類用來提供客戶端可以自由選擇算法

  public class RepTempRuleSolve {

  private RepTempRule strategy;

  public RepTempRuleSolve(RepTempRule rule){

  thisstrategy=rule;

  }

  public String getNewContext(Site siteString oldString) {

  return strategyreplace(siteoldString);

  }

  public void changeAlgorithm(RepTempRule newAlgorithm) {

  strategy = newAlgorithm;

  }

  }

  調用如下:

  public class test{

  

  public void testReplace(){

  //使用第一套替代方案

  RepTempRuleSolve solver=new RepTempRuleSolve(new RepTempRuleSimple());

  solvergetNewContext(sitecontext);

  //使用第二套

  solver=new RepTempRuleSolve(new RepTempRuleTwo());

  solvergetNewContext(sitecontext);

  }

  

  }

  我們達到了在運行期間可以自由切換算法的目的

  實際整個Strategy的核心部分就是抽象類的使用使用Strategy模式可以在用戶需要變化時修改量很少而且快速

  Strategy和Factory有一定的類似Strategy相對簡單容易理解並且可以在運行時刻自由切換

  Factory重點是用來創建對象

  Strategy適合下列場合:

  以不同的格式保存文件;

  以不同的算法壓縮文件;

  以不同的算法截獲圖象;

  以不同的格式輸出同樣數據的圖形比如曲線 或框圖bar等

  /***/

  行為模式Mediator

  Mediator定義:

  用一個中介對象來封裝一系列關於對象交互行為

  為何使用Mediator?

  各個對象之間的交互操作非常多;每個對象的行為操作都依賴彼此對方

  修改一個對象的行為同時會涉及到修改很多其他對象的行為如果使用Mediator模式

  可以使各個對象間的耦合松散只需關心和 Mediator的關系

  使多對多的關系變成了一對多的關系可以降低系統的復雜性提高可修改擴展性

  如何使用?

  首先 有一個接口用來定義成員對象之間的交互聯系方式:

  public interface Mediator { }

  Meiator具體實現真正實現交互操作的內容:

  public class ConcreteMediator implements Mediator {

  //假設當前有兩個成員

  private ConcreteColleague colleague = new ConcreteColleague();

  private ConcreteColleague colleague = new ConcreteColleague();

  

  }

  再看看另外一個參與者:成員因為是交互行為都需要雙方提供一些共同接口

  這種要求在Visitor Observer等模式中都是相同的

  public class Colleague {

  private Mediator mediator;

  public Mediator getMediator() {

  return mediator;

  }

  public void setMediator( Mediator mediator ) {

  diator = mediator;

  }

  }

  public class ConcreteColleague { }

  public class ConcreteColleague { }

  每個成員都必須知道Mediator並且和 Mediator聯系而不是和其他成員聯系

  至此Mediator模式框架完成可以發現Mediator模式規定不是很多

  大體框架也比較簡單但實際使用起來就非常靈活

  Mediator模式在事件驅動類應用中比較多例如界面設計GUI;聊天消息傳遞等

  在聊天應用中需要有一個MessageMediator專門負責request/reponse之間任務的調節

  MVC是JEE的一個基本模式View Controller是一種Mediator它是Jsp和服務器上應用程序間的Mediator

  /***/

  行為模式Interpreter

  Interpreter定義:

  定義語言的文法 並且建立一個解釋器來解釋該語言中的句子

  Interpreter似乎使用面不是很廣它描述了一個語言解釋器是如何構成的

  在實際應用中我們可能很少去構造一個語言的文法我們還是來簡單的了解一下:

  首先要建立一個接口用來描述共同的操作

  public interface AbstractExpression {

  void interpret( Context context );

  }

  再看看包含解釋器之外的一些全局信息

  public interface Context { }

  AbstractExpression的具體實現分兩種:終結符表達式和非終結符表達式:

  public class TerminalExpression implements AbstractExpression {

  public void interpret( Context context ) { }

  }

  對於文法中沒一條規則非終結符表達式都必須的:

  public class NonterminalExpression implements AbstractExpression {

  private AbstractExpression successor;

  public void setSuccessor( AbstractExpression successor ) {

  thissuccessor = successor;

  }

  public AbstractExpression getSuccessor() {

  return successor;

  }

  public void interpret( Context context ) { }

  }

  /***/

  行為模式Visitor

  Visitor定義

  作用於某個對象群中各個對象的操作 它可以使你在不改變這些對象本身的情況下定義作用於這些對象的新操作

  在Java中Visitor模式實際上是分離了collection結構中的元素和對這些元素進行操作的行為

  為何使用Visitor?

  Java的Collection(包括Vector和Hashtable)是我們最經常使用的技術

  可是Collection好象是個黑色大染缸本來有各種鮮明類型特征的對象一旦放入後再取出時這些類型就消失了

  那麼我們勢必要用If來判斷如:

  Iterator iterator = erator()

  while (iteratorhasNext()) {

  Object o = iteratornext();

  if (o instanceof Collection)

  messyPrintCollection((Collection)o);

  else if (o instanceof String)

  Systemoutprintln(+otoString()+);

  else if (o instanceof Float)

  Systemoutprintln(otoString()+f);

  else

  Systemoutprintln(otoString());

  }

  在上例中我們使用了 instanceof來判斷 o的類型

  很顯然這樣做的缺點代碼If else if 很繁瑣我們就可以使用Visitor模式解決它

  如何使用Visitor?

  針對上例我們設計一個接口visitor訪問者:

  public interface Visitor

  {

  public void visitCollection(Collection collection);

  public void visitString(String string);

  public void visitFloat(Float float);

  }

  在這個接口中將我們認為Collection有可能的類的類型放入其中

  有了訪問者我們需要被訪問者被訪問者就是我們Collection的每個元素Element

  我們要為這些Element定義一個可以接受訪問的接口(訪問和被訪問是互動的

  只有訪問者被訪問者如果表示不歡迎訪問者就不能訪問)

  我們定義這個接口叫Visitable用來定義一個Accept操作也就是說讓Collection每個元素具備可訪問性

  public interface Visitable

  {

  public void accept(Visitor visitor);

  }

  好了有了兩個接口我們就要定義他們的具體實現(Concrete class):

  public class ConcreteElement implements Visitable

  {

  private String value;

  public ConcreteElement(String string) {

  value = string;

  }

  //定義accept的具體內容 這裡是很簡單的一句調用

  public void accept(Visitor visitor) {

  visitorvisitString(this);

  }

  }

  再看看訪問者的Concrete實現:

  public class ConcreteVisitor implements Visitor

  {

  //在本方法中我們實現了對Collection的元素的成功訪問

  public void visitCollection(Collection collection) {

  Iterator iterator = erator()

  while (iteratorhasNext()) {

  Object o = iteratornext();

  if (o instanceof Visitable)

  ((Visitable)o)accept(this);

  }

  public void visitString(String string) {

  Systemoutprintln(+string+);

  }

  public void visitFloat(Float float) {

  Systemoutprintln(floattoString()+f);

  }

  }

  在上面的visitCollection我們實現了對Collection每個元素訪問

  只使用了一個判斷語句只要判斷其是否可以訪問

  至此我們完成了Visitor模式基本架構

  使用Visitor模式的前提

  對象群結構中(Collection) 中的對象類型很少改變也就是說訪問者的身份類型很少改變

  如上面中Visitor中的類型很少改變如果需要增加新的操作比如上例中我們在ConcreteElement具體實現外

  還需要新的ConcreteElement ConcreteElement

  可見使用Visitor模式是有前提的在兩個接口Visitor和Visitable中

  確保Visitor很少變化變化的是Visitable這樣使用Visitor最方便

  如果Visitor也經常變化 也就是說對象群中的對象類型經常改變一般建議是

  不如在這些對象類中逐個定義操作但是Java的Reflect技術解決了這個問題

  /***/


From:http://tw.wingwit.com/Article/program/Java/gj/201311/27375.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.