PC的blog

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

          BlogJava 首頁(yè) 新隨筆 聯(lián)系 聚合 管理
            9 Posts :: 0 Stories :: 54 Comments :: 0 Trackbacks

          2008年8月4日 #

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

          上篇文章談到如何能夠徹底把這個(gè)switch也移除掉呢?很簡(jiǎn)單,我們只需要在getSystemStatePerformer()方法被調(diào)用之前先創(chuàng)建所有 performer匿名類的實(shí)例,然后在該方法被調(diào)用時(shí)直接返回對(duì)應(yīng)的實(shí)力。 如何具體實(shí)現(xiàn)呢? 用Map, 請(qǐng)看代碼:

          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);

              }

          }

          從代碼中可以看出,當(dāng)getPerformers()方法被第一次調(diào)用時(shí),我們會(huì)為每一個(gè)performer匿名類創(chuàng)建一個(gè)實(shí)例,并且將它們納入Map的管 理之中,以后每次調(diào)用的時(shí)候,直接從Map里面提取對(duì)應(yīng)某個(gè)狀態(tài)的performer就可以了, switch可以舍棄了。 @FactoryMethod這個(gè)注釋是我自己寫的,使用它主要是為了避免每次新增加一個(gè)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 {


          }

          到這里整個(gè)重構(gòu)已經(jīng)結(jié)束了, 我們已經(jīng)將if else, switch完全從代碼里剔除了。

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

          1. Factory + annonymous類而不是每一個(gè)狀態(tài)一個(gè)具體的實(shí)體類。

              這樣處理問題, 類的數(shù)量大大減少,類關(guān)聯(lián)的復(fù)雜程度也大大減少,維護(hù)起來很方便。

          2. performer并不單單是一個(gè)command,它擁有狀態(tài),并且可以處理更多的邏輯。


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

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

          OK, 到目前為止,所有的邏輯代碼已經(jīng)從SystemManager重構(gòu)到了SystemStatePerformer。下一步應(yīng)該繼續(xù)重構(gòu)SystemManager, 將SystemState替換為performer:

          1, 使用IDE的重構(gòu)功能,將變量SystemState改為SystemStatePerformer

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

          3. 在測(cè)試代碼里面,調(diào)用manager.statePerformer.getState()

          重構(gòu)后的代碼如下:

          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已經(jīng)消失了。


          測(cè)試代碼也要做相應(yīng)修改:
          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);

              }

          }

          到這里重構(gòu)已經(jīng)差不多完成了,代碼已經(jīng)更加面向?qū)ο罅恕_@里還有一個(gè)小問題,在factory里面還有一個(gè)switch,這個(gè)和if else其實(shí)是沒有本質(zhì)區(qū)別的,也就是說if else并沒有被完全移除掉。


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

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

          移除if else

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


          我們將這個(gè)替換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();

          }

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




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

          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);

              }

          }

          從 代碼中可以看到,針對(duì)每一個(gè)enum狀態(tài)都有一個(gè)創(chuàng)建performer的方法,該方法返回一個(gè)匿名類。邏輯代碼將會(huì)被轉(zhuǎn)移至個(gè)匿名類的 perform()方法之內(nèi)。整個(gè)Factory只有一個(gè)公開的方 法:getSystemStatePerformer(SystemState),SystemManager可以調(diào)用這個(gè)方法來獲得相應(yīng)的 Performer實(shí)例。


          在 這篇文章中,我希望專屬于if else的問題。對(duì)于其他設(shè)計(jì)方面的問題,我采取的態(tài)度是能省略就省略。實(shí)際開發(fā)中,還有有很多問題需要處理,例如,使用static方法會(huì)導(dǎo)致系統(tǒng)的可 測(cè)試性下降,在實(shí)際開發(fā)中應(yīng)該盡量避免,解決這類問題的方法之一是使用DI框架,例如Google Guice。

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

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

          使用Enum替換int常量

          這一步比較簡(jiǎn)單,先創(chuàng)建一個(gè)enum類:

          package de.jingge.refactoring;

          public enum SystemState {

              LOGGEDIN,

              LOGGEDOUT,

              IDLE;

          }


          然后開始重構(gòu)SystemManager, 使用SystemState代替SystemManager里的int狀態(tài):

             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;
              }
          }

          然后重構(gòu)測(cè)試類:

          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);
              }
          }


          運(yùn)行這個(gè)測(cè)試類->通過

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

          我們知道因?yàn)榫幊陶Z(yǔ)言的限制,歷史遺留下來的系統(tǒng)總是有很多的毛病,不夠面向?qū)ο螅绕涫呛芏嘞到y(tǒng)濫用if else。我曾經(jīng)見過一個(gè)項(xiàng)目,大家基本上就是寫一個(gè)方法,然后在里面if else套if esle得嵌套了好幾層,難看就不必說了,這種代碼根本就沒法維護(hù)。

          今天我就使用從實(shí)際項(xiàng)目中提煉出來的例子來講解一下如何將這類代碼變得更加面向?qū)ο?- 重構(gòu)成模式并且添加測(cè)試代碼,

          先來看一個(gè)丑陋的類:

          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;

              }

          }


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

          那么怎么樣才能讓這個(gè)類更加地面向?qū)ο竽兀?br id="i7km89" />
          在處理面向?qū)ο笾埃覀兪紫纫帉懸粋€(gè)測(cè)試類,這也是處理這類歷史遺留下來代碼所必需做的第一步,只有在測(cè)試代碼的保護(hù)下,我們才能放心大膽地進(jìn)行重構(gòu)。

          初步的測(cè)試代碼如下:

          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);

              }

          }

          運(yùn)行測(cè)試代碼->通過。

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

          主站蜘蛛池模板: 姜堰市| 临颍县| 丹寨县| 大荔县| 临城县| 山西省| 建瓯市| 盐山县| 二手房| 比如县| 高青县| 乌拉特后旗| 灌云县| 东港市| 马边| 卢氏县| 宕昌县| 建阳市| 普兰县| 浮山县| 平远县| 正蓝旗| 章丘市| 南川市| 临泽县| 西吉县| 布尔津县| 元朗区| 孟连| 廊坊市| 徐州市| 云霄县| 内丘县| 关岭| 乐都县| 昭平县| 南丰县| 绥化市| 安泽县| 来凤县| 东丰县|