1. 程式人生 > 程式設計 >Java如何利用狀態模式(state pattern)替代if else

Java如何利用狀態模式(state pattern)替代if else

大多數開發人員現在還在使用if else的過程結構,曾看過jdon的banq大哥寫的一篇文章,利用command,aop模式替代if else過程結構。當時還不太明白,這幾天看了《重構》第一章的影片租賃案例,感觸頗深。下面我來談一談為什麼要用state pattern替代if else,替代if else有什麼好處,以及給出詳細程式碼怎麼替代if else。本文參考jdon的“你還在使用if else嗎?”及《重構》第一章。

首先我們模仿影片租賃過程,顧客租憑影片,影片分為兒童片、普通片、新片。根據影片型別及租憑天數價格各不相同(優惠程度不同),使用者累計積分不同。

OK ,現在我們使用 if else 表示。

 package com.qujingbo.movie;

 /**
 * <p/> Title:影片基類
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 15:47:55
 * </p>
 * 
 * @author EOMS 曲靜波
 * @version 1.0
 */
 public class Movie {

  // 普通片標識
  public static int REGULAR = 1 ;
 
  // 新片標識
  public static int NEW_RELEASE = 2 ;
 
  // 兒童片標識
  public static int CHILDREN = 3 ;
 
  /**
  * 獲取租賃影片總價
  *
  * @param movieCode
  * 影片型別
  * @param days
  * 租憑天數
  * @return 租賃影片總價
  * @throws MovieException
  * 沒有影片型別丟擲異常
  */
  public double getCharge( int movieCode,int days) throws MovieException {
  double result = 0 ;
  // 普通片
  if (movieCode == Movie.REGULAR)
  // 單價為2
  {
  result = 2 ;
  // 如果租賃天數大於2則,則優惠
  if (days > 2 ) {
  result += (days - 2 ) * 1.5 ;
  }
  // 返回總價
  return result;
  }
  // 最新發布片
  else if (movieCode == Movie.NEW_RELEASE) {
  // 新片沒有優惠,單價為3
  return days * 3 ;
  }
  // 兒童片
  else if (movieCode == Movie.CHILDREN) {
  // 影片單價
  result = 1.5 ;
  // 如果租賃時間大於3天則做價格優惠
  if (days > 3 ) {
  result += (days - 3 ) * 1.5 ;
  }
  // 返回租賃影片總價
  return result;
  } else
  throw new MovieException( " 影片不存在 " );
  }
 
  /**
  * 獲取租賃影片積分
  *
  * @param movieCode
  * 影片型別
  * @param days
  * 租憑天數
  * @return 租賃影片積分
  * @throws MovieException
  * 沒有影片型別丟擲異常
  */
  public double getIntegral( int movieCode,int days) throws MovieException
  {
  // 普通片
  if (movieCode == Movie.REGULAR)
  return days * 2 ;
  // 最新發布片
  else if (movieCode == Movie.NEW_RELEASE)
  return days * 3 ;
  // 兒童片
  else if (movieCode == Movie.CHILDREN)
  return days * 1.5 ;
    else
     throw new MovieException( " 影片不存在 " );
 
  }
}

OK ,我們看一下,現在的 Movie 完全符合租賃需求,通過 getIntegral(int movieCode,int days) 和 getCharge(int movieCode,int days) 來獲得租賃積分及租賃價格。從開閉原則角度來看,如果要新增新的影片型別,我們必須修改 getIntegral(int movieCode,int days) 這兩個方法。而若要改變租賃價格、積分的優惠規則時,仍需要修改 getIntegral(int movieCode,int days) 方法。現在看來,只有三種影片型別,維護還較方便。而當影片型別較多時,例如 10 種, 100 種影片型別,這樣就是不可以想像的維護。

現在我們來看一下,使用 state pattern 來代替 if else 。先來個類圖。

Java如何利用狀態模式(state pattern)替代if else

首先我們建立一個 abstract class Price 做為影片型別的基類,基類中含有兩個 abstract 方法,獲取總價格 getCharge(int days),獲取總積分 getIntegral(int days) 方法,繼承 abstract classPrice 的三個影片型別兒童片 class ChilerenPrice,普通片 class RegularPrice,最新片 class NewReleasePrice 。分別實現 getCharge(int days),getIntegral(int days) 方法,實現方法寫入計算價格的優惠方案及積分的方案。當需要修改方案時,我們只需在某個影片類的方法中對應修改就可以。若新增一個影片分類時,我們只需新增一個實現類實現 abstract class Price 類就 OK 。

class Movie 代表影片,其關聯一個 Price 類,而 setPrice(String movieClass) 方法類似於一個工廠類,傳入 movieClass 為包名類名,用 java 反射機制例項化一個具體傳入 movieClass 的影片型別實現類,這樣我們通過這幾行程式碼就可以獲得該影片型別的價格和積分。

 Movie regularMovie = new Movie();
regularMovie.setPrice(Movie.REGULAR);
System.out.println( " 普通影片租賃10天的價格 " + regularMovie.getPrice().getCharge( 10 ));
System.out.println( " 普通影片租賃10天的積分 " + regularMovie.getPrice().getIntegral( 10 ));

下面我們給出詳細程式碼

abstract class Price價格基類

package com.qujingbo.movie;

/**
 * <p/> Title:
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 15:48:22
 * </p>
 * 
 * @author EOMS 曲靜波
 * @version 1.0
 */
public abstract class Price {

 /**
  * 獲取租賃影片價格需實現該此方法
  * 
  * @param days
  *   租賃天數
  * @return 返回影片價格
  */
 public abstract double getCharge(int days);

 /**
  * 獲取租賃影片積分需實現此方法
  * 
  * @param days
  *   租賃天數
  * @return 返回影片積分
  */
 public abstract double getIntegral(int days);

}

兒童片ChildrenPrice類,實現abstract class Price ,實現兒童片租賃總價getCharge(int days)及兒童片租賃積分getIntegral(int days)。

package com.qujingbo.movie;

/**
 * <p/> Title:兒童片租賃積分、價格實現
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 15:49:04
 * </p>
 * 
 * @author EOMS 曲靜波
 * @version 1.0
 */
public class ChildrenPrice extends Price {

 /**
  * 兒童片返回租賃積分,兒童片積分規則為: 根據
  */
 public double getIntegral(int days) {
  // 返回租賃影片積分
  return days * 1.5;
 }

 /**
  * 兒童片返回租賃價格
  */
 public double getCharge(int days) {
  // 影片單價
  double result = 1.5;
  // 如果租賃時間大於3天則做價格優惠
  if (days > 3) {
   result += (days - 3) * 1.5;
  }
  // 返回租賃影片總價
  return result;
 }

}

普通片RegularlPrice類,實現abstract class Price ,實現普通片租賃總價getCharge(int days)及普通片租賃積分getIntegral(int days)。

package com.qujingbo.movie;

/**
 * <p/> Title:普通片租賃積分、價格實現
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 15:50:10
 * </p>
 * 
 * @author EOMS 曲靜波
 * @version 1.0
 */
public class RegularlPrice extends Price {
 /**
  * 普通片返回租賃積分,普通片積分規則
  */
 public double getIntegral(int days) {
  // 返回租賃影片積分
  return days * 2;
 }

 /**
  * 普通片返回租賃價格
  */
 public double getCharge(int days) {
  // 單價為2
  double result = 2;
  // 如果租賃天數大於2則,則優惠
  if (days > 2) {
   result += (days - 2) * 1.5;
  }
  // 返回總價
  return result;
 }

}

最新發布片NewReleasePrice類,實現abstract class Price ,實現最新發布片租賃總價getCharge(int days)及最新發布片租賃積分getIntegral(int days)。

package com.qujingbo.movie;

/**
 * <p/> Title:最新發布片租賃積分、價格實現
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 15:48:51
 * </p>
 * 
 * @author EOMS 曲靜波
 * @version 1.0
 */
public class NewReleasePrice extends Price {
 /**
  * 最新發布片返回租賃積分,最新發布片積分規則
  */
 public double getIntegral(int days) {
  // 返回租賃影片積分
  return days * 3;
 }

 /**
  * 最新發布片返回租賃價格
  */
 public double getCharge(int days) {
  // 新片沒有優惠,單價為3
  return days * 3;
 }

}

電影Movie類,setPrice(String movieClass)(工廠)方法,通過java反射機制實現movieClass(包名,類名)類。若沒有movieClass這個類,則丟擲MovieException異常。

package com.qujingbo.movie;

/**
 * <p/> Title:影片類
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 15:47:55
 * </p>
 * 
 * @author EOMS 曲靜波
 * @version 1.0
 */
public class Movie {
 // 普通片標識
 public static String REGULAR = "com.qujingbo.movie.RegularlPrice";

 // 新片標識
 public static String NEW_RELEASE = "com.qujingbo.movie.NewReleasePrice";

 // 兒童片標識
 public static String CHILDREN = "com.qujingbo.movie.ChildrenPrice";

 private Price price;

 public Price getPrice() {
  return price;
 }

 /**
  * 確定返回具體某個影片型別的實現類,有點像工廠
  * 
  * @param movieCode
  *   影片型別
  * @throws MovieException
  *    若無影片型別則拋異常。
  */
 public void setPrice(String movieClass) throws MovieException {
  try {
   Class cls = Class.forName(movieClass);
   this.price = (Price) cls.newInstance();
  } catch (Exception e) {
   throw new MovieException("影片不存在");
  }
 }
}

給出MovieException原始碼。

package com.qujingbo.movie;

/**
 * <p/> Title:自定義異常
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 19:21:08
 * </p>
 * 
 * @author EOMS 曲靜波
 * @version 1.0
 */
public class MovieException extends Exception {
 public MovieException(String msg) {
  super(msg);
 }
}

下面模訪一個顧客租賃影片。

package com.qujingbo.movie;

/**
 * <p/> Title:
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 19:26:23
 * </p>
 * 
 * @author EOMS 曲靜波
 * @version 1.0
 */
public class Customer {
 /**
  * 消費(測試程式)
  * 
  * @throws MovieException
  *    若沒有影片,丟擲異常
  */
 public void consume() throws MovieException {
  // 普通電影
  Movie regularMovie = new Movie();
  regularMovie.setPrice(Movie.REGULAR);
  // 最新發布電影
  Movie newReleaseMovie = new Movie();
  newReleaseMovie.setPrice(Movie.NEW_RELEASE);
  // 兒童電影
  Movie childrenMovie = new Movie();
  childrenMovie.setPrice(Movie.CHILDREN);

  System.out.println("普通影片租賃10天的價格"
    + regularMovie.getPrice().getCharge(10));
  System.out.println("最新影片租賃10天的價格"
    + newReleaseMovie.getPrice().getCharge(10));
  System.out.println("兒童影片租賃10天的價格"
    + childrenMovie.getPrice().getCharge(10));
  
  System.out.println("普通影片租賃10天的積分"
    + regularMovie.getPrice().getIntegral(10));
  System.out.println("最新影片租賃10天的積分"
    + newReleaseMovie.getPrice().getIntegral(10));
  System.out.println("兒童影片租賃10天的積分"
    + childrenMovie.getPrice().getIntegral(10));
  
  
 }
}

寫一 junit 測試類執行 class Customer 的 consume() 方法。

 package com.qujingbo.movie;

 import junit.framework.TestCase;

 /**
 * <p/> Title:junit測試類
 * </p>
 * <p/> Description:
 * </p>
 * <p/> Date:2006-10-14 19:32:57
 * </p>
 * 
 * @author EOMS 曲靜波
 * @version 1.0
 */
 public class CustomerTest extends TestCase {

  private Customer customer = null ;

  protected void setUp() throws Exception {
   super .setUp();
  customer = new Customer();
 }

  protected void tearDown() throws Exception {
   super .tearDown();
 }

  /*
  * Test method for 'com.qujingbo.movie.Customer.consume()'
  */
  public void testConsume() {
   try {
   customer.consume();
  } catch (MovieException e) {
   System.out.println( " 沒有該類影片 " );
  }
 }

}

OK 。結果為:

普通影片租賃 10 天的價格 14.0

最新影片租賃 10 天的價格 30.0

兒童影片租賃 10 天的價格 12.0

普通影片租賃 10 天的積分 20.0

最新影片租賃 10 天的積分 30.0

兒童影片租賃 10 天的積分 15.0

最後我要說,我們用 OO 表示的租賃過程並不完整,因為顧客不一定只租賃一部影片,而要租賃多部影片,這樣我們缺少一個 Rental (租賃類)。而只是為說明 state pattern 替代 if else ,所以我們沒有新增 Rental (租賃類),若需要參考,請查閱《重構》第一章。 點選下載原始碼.

到此這篇關於Java如何利用狀態模式(state pattern)替代if else的文章就介紹到這了,更多相關Java用狀態模式(state pattern)替代if else內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!