PC的blog

          Finding... Thinking... Solving...

          BlogJava 首頁 新隨筆 聯系 聚合 管理
            9 Posts :: 0 Stories :: 54 Comments :: 0 Trackbacks

          2008年8月4日 #

          本文緊接使用重構移除丑陋的if else代碼(4)

          上篇文章談到如何能夠徹底把這個switch也移除掉呢?很簡單,我們只需要在getSystemStatePerformer()方法被調用之前先創建所有 performer匿名類的實例,然后在該方法被調用時直接返回對應的實力。 如何具體實現呢? 用Map, 請看代碼:

          package de.jingge.refactoring;

           

          import static de.jingge.refactoring.SystemState.*;

          import java.awt.Image;

          import java.awt.image.BufferedImage;

          import java.lang.reflect.Method;

          import java.util.Collections;

          import java.util.HashMap;

          import java.util.Map;

           

          /**

           *

           * 
          @author gejing@gmail.com

           
          */

          public class SystemStatePerformerFactory {

           

          private static SystemStatePerformerFactory INSTANCE = new SystemStatePerformerFactory();

             

              
          private Map<SystemState, SystemStatePerformer> performers;

           

              
          private SystemStatePerformerFactory() {

          }

           

              
          public static SystemStatePerformerFactory getInstance() {

                  
          return INSTANCE;

              }

             

              
          private synchronized Map<SystemState, SystemStatePerformer> getPerformers()

                      
          throws Exception {

                  
          if (performers == null) {

                      performers 
          = new HashMap<SystemState, SystemStatePerformer>();

                      
          // call all @FactoryMethod using reflection

                      
          for (Method m : getClass().getDeclaredMethods()) {

                          
          if (m.getAnnotation(FactoryMethod.class!= null) {

                              SystemStatePerformer p 
          = (SystemStatePerformer) m.invoke(

                                      
          thisnew Object[]{});

                              performers.put(p.getState(), p);

                          }

                      }

                      
          // make it readonly

                      performers 
          = Collections.unmodifiableMap(performers);

                  }

                  
          return performers;

              }

           

              
          public SystemStatePerformer getSystemStatePerformer(SystemState state) throws Exception{

                  
          return getPerformers().get(state);

              }

           

          @FactoryMethod

              
          private SystemStatePerformer createLoggedInPerformer() {

                  
          return new SystemStatePerformer(LOGGEDIN, getImage("loggedin.gif")) {

           

                      @Override

                      
          public void perform() {

                          
          // do something after logging in is successful,

                          
          // for example: show welcome dialog, open the last edit document, etc.

                      }

                  };

              }

           

          @FactoryMethod

              
          private SystemStatePerformer createLoggedOutPerformer() {

                  
          return new SystemStatePerformer(LOGGEDOUT, getImage("loggedout.gif")) {

           

                      @Override

                      
          public void perform() {

                          
          // do something after logging out is successful,

                          
          // for example: free used resource, dispose GUI components, etc.            }

                      }

                  };

              }

           

          @FactoryMethod

              
          private SystemStatePerformer createIdlePerformer() {

                  
          return new SystemStatePerformer(IDLE, getImage("idle.gif")) {

           

                      @Override

                      
          public void perform() {

                          
          // do something after the user is idle,

                          
          // for example: save the application state temporarily, lock the application, etc.

                      }

                  };

              }

           

              
          private Image getImage(String string) {

                  
          return new BufferedImage(1010, BufferedImage.TYPE_4BYTE_ABGR);

              }

          }

          從代碼中可以看出,當getPerformers()方法被第一次調用時,我們會為每一個performer匿名類創建一個實例,并且將它們納入Map的管 理之中,以后每次調用的時候,直接從Map里面提取對應某個狀態的performer就可以了, switch可以舍棄了。 @FactoryMethod這個注釋是我自己寫的,使用它主要是為了避免每次新增加一個create***Performer()方法后,都必須修改 getSystemStatePerformer()。

          @FactoryMethod的代碼如下:

          package de.jingge.refactoring;

           

          import java.lang.annotation.ElementType;

          import java.lang.annotation.Retention;

          import java.lang.annotation.RetentionPolicy;

          import java.lang.annotation.Target;

           


          @Retention(RetentionPolicy.RUNTIME)

          @Target({ElementType.METHOD})

          public @interface FactoryMethod {


          }

          到這里整個重構已經結束了, 我們已經將if else, switch完全從代碼里剔除了。

          讀過Refactoring to Patterns這本書的朋友可能會覺得,這里所作的一些和書中第七章最后一節Replace Conditional Dispatcher with Command完全一樣。 Well,第一眼看上去確實很像,但是看完我寫的所有代碼后,再仔細想一想,兩者還是有區別的(Refactoring to Patterns這本書寫的非常好,對此書,我可以說是愛不釋手,還曾經寫過一篇書評。事實上,我這篇文章正式基于這本書的):

          1. Factory + annonymous類而不是每一個狀態一個具體的實體類。

              這樣處理問題, 類的數量大大減少,類關聯的復雜程度也大大減少,維護起來很方便。

          2. performer并不單單是一個command,它擁有狀態,并且可以處理更多的邏輯。


          全文完。
          posted @ 2008-08-04 03:48 polygoncell 閱讀(4634) | 評論 (37)編輯 收藏

          本文緊接使用重構移除丑陋的if else代碼(3)

          OK, 到目前為止,所有的邏輯代碼已經從SystemManager重構到了SystemStatePerformer。下一步應該繼續重構SystemManager, 將SystemState替換為performer:

          1, 使用IDE的重構功能,將變量SystemState改為SystemStatePerformer

          2. 在updateState()方法中調用SystemStatePerformerFactory

          3. 在測試代碼里面,調用manager.statePerformer.getState()

          重構后的代碼如下:

          package de.jingge.refactoring;

           

          import static de.jingge.refactoring.SystemState.*;


          public class SystemManager {

           

              SystemStatePerformer statePerformer;

           

              
          public void login() {

                  
          // call service#login()

                  updateState(LOGGEDIN);

              }

           

              
          public void logout() {

                  
          // call service#logout()

                  updateState(LOGGEDOUT);

              }

           

              
          public void idle() {

                  
          // call some other services

                  updateState(IDLE);

              }

           

              
          public void updateState(SystemState state) {

                  
          this.statePerformer = SystemStatePerformerFactory.getInstance()

                          getSystemStatePerformer(state);

                  statePerformer.perform();

              }

          }

          可以看到if else已經消失了。


          測試代碼也要做相應修改:
          package de.jingge.refactoring;

           

          import org.junit.AfterClass;

          import org.junit.BeforeClass;

          import org.junit.Test;

          import static org.junit.Assert.*;

          import static de.jingge.refactoring.SystemState.*;


          public class SystemManagerTest {

              
          private static SystemManager manager;

              @BeforeClass
              
          public static void setUpClass() throws Exception {

                  manager 
          = new SystemManager();

                  
          // add some service mock objects

              }

              @AfterClass
              
          public static void tearDownClass() throws Exception {

              }

              @Test
              
          public void login() {

                  manager.login();

                  assertEquals(manager.statePerformer.getState(), LOGGEDIN);

              }

              @Test
              
          public void logout() {

                  manager.logout();

                  assertEquals(manager.statePerformer.getState(), LOGGEDOUT);

              }

              @Test
              
          public void idle() {

                  manager.idle();

                  assertEquals(manager.statePerformer.getState(), IDLE);

              }

          }

          到這里重構已經差不多完成了,代碼已經更加面向對象了。這里還有一個小問題,在factory里面還有一個switch,這個和if else其實是沒有本質區別的,也就是說if else并沒有被完全移除掉。


          那么如何能夠徹底把這個switch也移除掉呢?很簡單,我們只需要在getSystemStatePerformer()方法被調用之前先創建所有 performer匿名類的實例,然后在該方法被調用時直接返回對應的實力。 那么具體如何實現呢,請看下一篇文章使用重構移除丑陋的if else代碼(5)
          posted @ 2008-08-04 03:08 polygoncell 閱讀(1945) | 評論 (1)編輯 收藏

          本文緊接使用重構移除丑陋的if else代碼(2)

          移除if else

          首先仔細觀察一 下updateState()方法,我們會發現,導致該方法內存在大量if else的原因是它的參數僅僅是一個enum。由于enum本身并不含有任何邏輯代碼,因此導致處理enum的方法需要使用if else來分析enum然后調用相應的邏輯。明白了這個道理之后,重構的方向就明了了。簡單的說,我們需要要將方法參數由enum替換成一個更加強壯的抽 象類,每一個繼承該類的子類將具體負責處理一個enum實例,之后再將updateState()方法中相應的邏輯代碼轉移到這些子類中。這樣處理之后, 令人討厭的if else就會消失了。


          我們將這個替換enum的抽象類命名為SystemStatePerformer,代碼如下:

          package de.jingge.refactoring;

           

          import java.awt.Image;


          public abstract class SystemStatePerformer {

              
          private final SystemState state;

              
          private Image image;

              
          public SystemStatePerformer(SystemState state, Image image) {

                  
          this.state = state;

                  
          this.image = image;

              }

              
          public SystemState getState() {

                  
          return state;

              }

              
          public Image getImage() {

                  
          return image;

              }
              
              
          public abstract void perform();

          }

          從代碼中可以看出,每 一個performer都含義有一個SystemState,這個SystemState屬性,將只能通過構建器映射方式射入一個performer的對 象實例。換句話說SystemState只是一個只讀屬性,而且每一個performer實體類都只負責處理一個enum的實例(下面馬上會解釋如何實現 的)。這里使用的Image作為一個例子,它表示用戶的每一個狀態都可以使用一個圖標來表示。performer()方法將負責處理具體的邏輯。這個 SystemStatePerformer的實體子類可以引用任何類型的對象,然后在perform()方法里面進行調用。




          下 一步就是編寫SystemStatePerformer的實體子類。我首先想到的是為每一個enum實例編寫一個實際的子類,理論上來說是沒問題的,但是 這樣做必須編寫一大堆的子類,不便于管理。所以我決定使用Factory + annonymous classes來構建具體的實體子類,讓Factory來管理所有的實體子類。 代碼如下:

          package de.jingge.refactoring;

           

          import static de.jingge.refactoring.SystemState.*;

          import java.awt.Image;

          import java.awt.image.BufferedImage;


          public class SystemStatePerformerFactory {

           

          private static SystemStatePerformerFactory INSTANCE = new SystemStatePerformerFactory();

             

              
          private SystemStatePerformerFactory() {

          }

           

              
          public static SystemStatePerformer getSystemStatePerformer(SystemState state) {

                  
          switch (state) {

                      
          case LOGGEDIN:

                          
          return createLoggedInPerformer();

                      
          case IDLE:

                          
          return createIdlePerformer();

                      
          case LOGGEDOUT:

                          
          return createLoggedOutPerformer();

                      
          default:

                          
          throw new IllegalAccessError("Unkonw status");

                  }

              }

           

              
          private static SystemStatePerformer createLoggedInPerformer() {

                  
          return new SystemStatePerformer(LOGGEDIN, getImage("loggedin.gif")) {

           

                      @Override

                      
          public void perform() {

                          
          // do something after logging in is successful,

                          
          // for example: show welcome dialog, open the last edit document, etc.

                      }

                  };

              }

           

              
          private static SystemStatePerformer createLoggedOutPerformer() {

                  
          return new SystemStatePerformer(LOGGEDOUT, getImage("loggedout.gif")) {

           

                      @Override

                      
          public void perform() {

                          
          // do something after logging out is successful,

                          
          // for example: free used resource, dispose GUI components, etc.            }

                      }

                  };

              }

           

              
          private static SystemStatePerformer createIdlePerformer() {

                  
          return new SystemStatePerformer(IDLE, getImage("idle.gif")) {

           

                      @Override

                      
          public void perform() {

                          
          // do something after the user is idle,

                          
          // for example: save the application state temporarily, lock the application, etc.

                      }

                  };

              }

           

              
          private static Image getImage(String string) {

                  
          return new BufferedImage(1010, BufferedImage.TYPE_4BYTE_ABGR);

              }

          }

          從 代碼中可以看到,針對每一個enum狀態都有一個創建performer的方法,該方法返回一個匿名類。邏輯代碼將會被轉移至個匿名類的 perform()方法之內。整個Factory只有一個公開的方 法:getSystemStatePerformer(SystemState),SystemManager可以調用這個方法來獲得相應的 Performer實例。


          在 這篇文章中,我希望專屬于if else的問題。對于其他設計方面的問題,我采取的態度是能省略就省略。實際開發中,還有有很多問題需要處理,例如,使用static方法會導致系統的可 測試性下降,在實際開發中應該盡量避免,解決這類問題的方法之一是使用DI框架,例如Google Guice。

          下一篇文章使用重構移除丑陋的if else代碼(4)繼續講解。
          posted @ 2008-08-04 02:54 polygoncell 閱讀(2249) | 評論 (4)編輯 收藏

          本文緊接使用重構移除丑陋的if else代碼(1)

          使用Enum替換int常量

          這一步比較簡單,先創建一個enum類:

          package de.jingge.refactoring;

          public enum SystemState {

              LOGGEDIN,

              LOGGEDOUT,

              IDLE;

          }


          然后開始重構SystemManager, 使用SystemState代替SystemManager里的int狀態:

             1. 添加 import static de.jingge.refactoring.SystemState.*;
             2. 刪除所有的integer常量  
             3. 將變量state的類型改為SystemState.

          代碼如下:



          package de.jingge.refactoring;

          import static de.jingge.refactoring.SystemState.*;

          public class SystemManager {

              SystemState state;

              
          public void login() {
                  
          // call service#login()
                  updateState(LOGGEDIN);
              }
             
              
          public void logout() {
                  
          // call service#logout()
                  updateState(LOGGEDOUT);
              }
             
              
          public void idle() {
                  
          // call some other services
                  updateState(IDLE);
              }
             
              
          public void updateState(SystemState state) {
                  
          if (state == LOGGEDIN) {
                      
          // do something after logging in is successful,
                      
          // for example: show welcome dialog, open the last edit document, etc.
                  } else if (state == LOGGEDOUT) {
                      
          // do something after logging out is successful,
                      
          // for example: free used resource, dispose GUI components, etc.
                  } else if (state == IDLE) {
                      
          // do something after the user is idle,
                      
          // for example: save the application state temporarily, lock the application, etc.
                  } else {
                      
          throw new IllegalArgumentException("unknown state");
                  }
                  
          this.state = state;
              }
          }

          然后重構測試類:

          1.    添加import static de.jingge.refactoring.SystemState.*;
          2.    刪除所有常量前引用的SystemManager.

          package de.jingge.refactoring;

          import org.junit.AfterClass;
          import org.junit.BeforeClass;
          import org.junit.Test;
          import static org.junit.Assert.*;
          import static de.jingge.refactoring.SystemState.*;


          public class SystemManagerTest {

              
          private static SystemManager manager;

              @BeforeClass
              
          public static void setUpClass() throws Exception {
                  manager 
          = new SystemManager();
                  
          // add some service mock objects
              }
             
              @AfterClass
              
          public static void tearDownClass() throws Exception {
              }
             
              @Test
              
          public void login() {
                  manager.login();
                  assertEquals(manager.state, LOGGEDIN);
              }
            
              @Test
              
          public void logout() {
                  manager.logout();
                  assertEquals(manager.state, LOGGEDOUT);
              }

              @Test
              
          public void idle() {
                  manager.idle();
                  assertEquals(manager.state, IDLE);
              }
          }


          運行這個測試類->通過

          下一篇文章使用重構移除丑陋的if else代碼(3)開始處理if else hell
          posted @ 2008-08-04 02:45 polygoncell 閱讀(2097) | 評論 (0)編輯 收藏

          我們知道因為編程語言的限制,歷史遺留下來的系統總是有很多的毛病,不夠面向對象,尤其是很多系統濫用if else。我曾經見過一個項目,大家基本上就是寫一個方法,然后在里面if else套if esle得嵌套了好幾層,難看就不必說了,這種代碼根本就沒法維護。

          今天我就使用從實際項目中提煉出來的例子來講解一下如何將這類代碼變得更加面向對象 - 重構成模式并且添加測試代碼,

          先來看一個丑陋的類:

          package de.jingge.refactoring;

           

          public class SystemManager {

           

              
          public static final int LOGGEDIN = 0;

              
          public static final int LOGGEDOUT = 1;

              
          public static final int IDLE = 2;

              
          int state;

           

              
          public void login() {

                  
          // call service#login()

                  updateState(LOGGEDIN);

              }

             

              
          public void logout() {

                  
          // call service#logout()

                  updateState(LOGGEDOUT);

              }

             

              
          public void idle() {

                  
          // call some other services

                  updateState(IDLE);

              }

              
          public void updateState(int state) {

                  
          if (state == LOGGEDIN) {

                      
          // do something after logging in is successful,

                      
          // for example: show welcome dialog, open the last edit document, etc.

                  } 
          else if (state == LOGGEDOUT) {

                      
          // do something after logging out is successful,

                      
          // for example: free used resource, dispose GUI components, etc.

                  } 
          else if (state == IDLE) {

                      
          // do something after the user is idle,

                      
          // for example: save the application state temporarily, lock the application, etc.

                  } 
          else {

                      
          throw new IllegalArgumentException("unknown state");

                  }

                  
          this.state = state;

              }

          }


          這里我們展示了一個 SystemManager,它負責處理用戶在系統中的狀態:登入(logged in),登出(logged out),以及空閑(idle)。從代碼中可以看到,這個類用了int來定義狀態并且因此導致了updatteState()方法里面出現大量if else。從目前看來這些if else是無法避免的,應為這個類需要針對不同的狀態作出反應。隨著狀態的增加,if else的數量也會繼續增加。這個解決方案顯然很差。

          那么怎么樣才能讓這個類更加地面向對象呢?

          在處理面向對象之前,我們首先要編寫一個測試類,這也是處理這類歷史遺留下來代碼所必需做的第一步,只有在測試代碼的保護下,我們才能放心大膽地進行重構。

          初步的測試代碼如下:

          package de.jingge.refactoring;

          import org.junit.AfterClass;
          import org.junit.BeforeClass;
          import org.junit.Test;
          import static org.junit.Assert.*;



          public class SystemManagerTest {
              
          private static SystemManager manager;

              @BeforeClass
              
          public static void setUpClass() throws Exception {
                  manager 
          = new SystemManager();
                  
          // add some service mock objects
              }

              @AfterClass
              
          public static void tearDownClass() throws Exception {

              }

              @Test
              
          public void login() {

                  manager.login();

                  assertEquals(manager.state, SystemManager.LOGGEDIN);

              }

              @Test
              
          public void logout() {

                  manager.logout();

                  assertEquals(manager.state, SystemManager.LOGGEDOUT);

              }

              @Test
              
          public void idle() {

                  manager.idle();

                  assertEquals(manager.state, SystemManager.IDLE);

              }

          }

          運行測試代碼->通過。

          在下一篇文章我們將正式開始重構。地址:使用重構移除丑陋的if else代碼(2)
          posted @ 2008-08-04 02:36 polygoncell 閱讀(2705) | 評論 (3)編輯 收藏

          主站蜘蛛池模板: 齐齐哈尔市| 什邡市| 海口市| 台州市| 林口县| 龙里县| 神池县| 乐安县| 新龙县| 望谟县| 达孜县| 北川| 兰西县| 南乐县| 宝丰县| 云霄县| 盐城市| 五台县| 吴堡县| 开江县| 龙口市| 凤城市| 凤翔县| 宜黄县| 石嘴山市| 屯门区| 天台县| 兴仁县| 孙吴县| 东阿县| 西乡县| 龙胜| 交城县| 武汉市| 高州市| 阳春市| 富宁县| 修文县| 黔西县| 宜城市| 井冈山市|