zming

          Java tech JMX Aop Ioc WebUI....

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            25 隨筆 :: 0 文章 :: 3 評(píng)論 :: 0 Trackbacks
           

          翻譯作者:zming

          翻譯自:http://today.java.net/pub/a/today/2005/04/14/dependency.html

          轉(zhuǎn)載請(qǐng)注明出處:http://blog.csdn.net/zmxj/archive/2005/05/25/380784.aspx

          <<Head First Design Patterns>>一書的Factory 模式章節(jié)中,建議我們要“Breaking the Last Dependency”,即打破最后的依賴,并且展示了如何寫出完全遠(yuǎn)離具體類的代碼。下面我們來看看這個(gè)主題。

          看看breaking the last dependency 是什么意思?它是如何來描述工廠模式的?以及我們?yōu)槭裁磻?yīng)該關(guān)注它?所有的工廠模式都是封裝具體類的實(shí)例并幫助你將代碼和具體類的依賴減少到最少。看下面的代碼:

          public class StampedeSimulator {

              Actor actor = null;

              public void addActor(String type) {

                  if (type.equals("buffalo")) {

                      actor = new Buffalo();

                  } else if (type.equals("horse")) {

                      actor = new Horse();

                  } else if (type.equals("cowboy")) {

                      actor = new Cowboy();

                  } else if (type.equals("cowgirl")) {

                      actor = new Cowgirl();

                  }

                  // rest of simulator here

              }

          }

          這段代碼中包含了四個(gè)不同的具體類(Buffalo, Horse, Cowboy, and Cowgirl)結(jié)果他建立了依賴關(guān)系在你的代碼和這些具體類之間,這為什么是一件壞事呢?你想想,如果你要加入一個(gè)新的類型(比如Coyote)或者重新配置具體類(比如你想用FastHorse類替代普通的Horse類),你將重新修改你的代碼,這造成難維護(hù)性。切記,可能類似的代碼會(huì)遍布你的所有代碼中,如果你要修改這個(gè)代碼需要到多處修改。注意我們不要寄希望于Java5.0enumerations匹配字符串來減少這些代碼,不是所有的用戶都可以在Java5平臺(tái)下的(比如蘋果系統(tǒng)的用戶),我們將作其他的實(shí)踐。

              現(xiàn)在我們有沒有一個(gè)好的方法減少具體類的依賴呢?那將使你的生活更加輕松,減少你大量的代碼維護(hù)工作,辦法就是使用Factory.有幾種類型的工廠,用哪一種你可以查相關(guān)的模式書。為了我們的事例,讓我們看看Static Factory,它由一個(gè)類組成,它提供一個(gè)靜態(tài)方法來操縱一個(gè)對(duì)象的實(shí)例。要實(shí)現(xiàn)這個(gè),我們將所有實(shí)例代碼放到一個(gè)factory里,ActorFactory,替換上面StampedeSimulator代碼,用factory來創(chuàng)建對(duì)象:

          public class ActorFactory {
              
          static public Actor createBuffalo() {
                  
          return new Buffalo();
              }

              
          static public Actor createHorse() {
                  
          return new Horse();
              }

              
          static public Actor createCowboy() {
                  
          return new Cowboy();
              }

              
          static public Actor createCowgirl() {
                  
          return new Cowgirl();
              }

          }

          And we can alter our StampedeSimulator to look like 
          this
           
          public class StampedeSimulator {
              Actor actor 
          = null;
           
              
          public void addActor(String type) {
                  
          if (type.equals("buffalo")) {
                      actor 
          = ActorFactory.createBuffalo();
                  }
           else if (type.equals("horse")) {
                      actor 
          = ActorFactory.createHorse();
                  }
           else if (type.equals("cowboy")) {
                      actor 
          = ActorFactory.createCowboy();
                  }
           else if (type.equals("cowgirl")) {
                      actor 
          = ActorFactory.createCowgirl();
                  }

           
                  
          // rest of stampede simulator here
              }

          }

          僅這樣只是得到了一點(diǎn)改善,因?yàn)榇a中還有兩個(gè)if else then子句。我們還可以進(jìn)一步改進(jìn),我們來參數(shù)化工廠,用一個(gè)String來標(biāo)示具體實(shí)例的類型:

          僅這樣只是得到了一點(diǎn)改善,因?yàn)榇a中還有兩個(gè)if else then子句。我們還可以進(jìn)一步改進(jìn),我們來參數(shù)化工廠,用一個(gè)String來標(biāo)示具體實(shí)例的類型:

          public class ActorFactory {

              static public Actor createActor(String type) {

                  if (type.equals("buffalo")) {

                      return new Buffalo();

                  } else if (type.equals("horse")) {

                      return new Horse();

                  } else if (type.equals("cowboy")) {

                      return new Cowboy();

                  } else if (type.equals("cowgirl")) {

                      return new Cowgirl();

                  } else {

                      return null;

                  }

              }

          }

           

          public class StampedeSimulator {

              Actor actor = null;

              public void addActor(String type) {

                  actor = ActorFactory.createActor(type);

                  // rest of stampede simulator here

              }

          }

           

          現(xiàn)在我們已經(jīng)很好分離了具體類和我們的代碼中的依賴。注意,工廠中的方法的返回類型是一個(gè)接口(Actor)或者也可以是一個(gè)抽象類。這使得你的客戶端不需要知道具體的類是什么,因而,在你的客戶端代碼里使用接口,你將繼續(xù)解耦和你的具體類的依賴。靜態(tài)工廠創(chuàng)建你需要的對(duì)象,你的客戶端代碼不需要擔(dān)心它。現(xiàn)在,如果你需要改變代碼,你只需要去一個(gè)地方,實(shí)例都被封裝了。

             這樣把具體類封裝到工廠中是很好的事,我們解耦了主要代碼和具體類之間的依賴。但是工廠本身仍然依賴于具體的類,如果我們需要改變那些類,就是說需要修改工廠的代碼,重新編譯,那樣不是我們想要做的,我們希望移除所有這樣的依賴在我們的代碼里。

              在我們繼續(xù)之前,我要指出靜態(tài)工廠(Static Factory是一種經(jīng)常被使用的超過真正的設(shè)計(jì)模式的慣用方法,但是象這樣使用的人常常用單詞“工廠(Factory)”來應(yīng)用這個(gè)創(chuàng)建對(duì)象的方法. 無論如何,你能使用我們正要結(jié)束的靜態(tài)工廠或者仍何使用真正的工廠模式的技術(shù)(like the Factory Method or Abstract Factory patterns).

              在我們繼續(xù)之前,我要指出靜態(tài)工廠(Static Factory是一種經(jīng)常被使用的超過真正的設(shè)計(jì)模式的慣用方法,但是象這樣使用的人常常用單詞“工廠(Factory)”來應(yīng)用這個(gè)創(chuàng)建對(duì)象的方法. 無論如何,你能使用我們正要結(jié)束的靜態(tài)工廠或者仍何使用真正的工廠模式的技術(shù)(like the Factory Method or Abstract Factory patterns).

              在我們繼續(xù)之前,我要指出靜態(tài)工廠(Static Factory是一種經(jīng)常被使用的超過真正的設(shè)計(jì)模式的慣用方法,但是象這樣使用的人常常用單詞“工廠(Factory)”來應(yīng)用這個(gè)創(chuàng)建對(duì)象的方法. 無論如何,你能使用我們正要結(jié)束的靜態(tài)工廠或者仍何使用真正的工廠模式的技術(shù)(like the Factory Method or Abstract Factory patterns).

          Let's Break that Last Dependency

          (讓我們打破最后的依賴)

          我們解耦了應(yīng)用主要代碼和具體類的依賴,但是Static Factory, ActorFactory仍然牢牢地綁定著具體的類,加之丑陋的if-then-else語句仍然存在。我們?nèi)绾尾拍芨纳七@些移除最后的依賴呢?

              有一種技術(shù)是使用javaClass.forName()forName()方法允許你用指定的包路徑下的類名動(dòng)態(tài)的裝入類。一旦你要取得類,你只需要用實(shí)例化一個(gè)它的新實(shí)例,并且返回它。 讓我們看他怎樣工作:

          class ActorFactory {

              static public Actor createActor(String type) {

                   Actor actor = null;

                  Class actorClass = null;

                  try {

                      actorClass = Class.forName(type);

                  } catch (ClassNotFoundException e) {

                      System.out.println("Error: class " + type + " not found.");

                      e.printStackTrace();

                  } catch (Exception e) {

                      e.printStackTrace();

                  }

                  if (actorClass != null) {

                      try {

                          actor = (Actor) actorClass.newInstance();

                      } catch (Exception e) {

                          e.printStackTrace();

                      }

                  }

                  return actor;

              }

          }

           

              這個(gè)代碼更加解耦了你的應(yīng)用和具體類的依賴,因?yàn)楝F(xiàn)在你可以通過傳遞類名(或至少一些實(shí)現(xiàn)了Actor接口的類)給工廠,你就可以取得類的實(shí)例。我們?yōu)榇烁冻龅拇鷥r(jià)就是我們不得不檢測(cè)所有可能的途徑:首先,確信我們傳遞的類名字串的類事實(shí)存在,并且確信你能夠?qū)嵗@個(gè)類,我們可以在這偷個(gè)懶,我們可以在不能裝入或?qū)嵗粋€(gè)類而發(fā)生異常時(shí),打印出異常的stacktrace在實(shí)際應(yīng)用中,顯然你不得不做的更多。我們也用靈活性換取了少許對(duì)靜態(tài)類型檢測(cè)的控制。你將要通過稍微的思考,對(duì)于實(shí)例,它能夠完美的合法的為我們裝入Actor類,但是我們不能實(shí)際上從Actor實(shí)例一個(gè)對(duì)象,因?yàn)樗且粋€(gè)接口。

             一旦我們修改了ActorFactory,我們需要在你的應(yīng)用代碼里做一些小的修改,我們需要傳遞由String描述的actor類。像這樣:

              simulator.addActor("headfirst.factory.simulator.Buffalo");

              simulator.addActor("headfirst.factory.simulator.Horse");

              simulator.addActor("headfirst.factory.simulator.Cowboy");

              simulator.addActor("headfirst.factory.simulator.Cowgirl");

          像這樣,我們能夠編譯和運(yùn)行這個(gè)代碼并且和先前得到相同的結(jié)果:每一個(gè)actor類型被實(shí)例化了。

             現(xiàn)在,當(dāng)我們想要改變stampede simulatoractors時(shí)(例如,我們要拍一個(gè)電影,用動(dòng)畫的演員替換真實(shí)的演員),所有要做的就是改變我們傳遞給addActor()方法的描述actor類型的String串即可。我們根本不需要改變ActorFactory or StampedeSimulator中的任何代碼。

          Taking It All the Way

          這是一個(gè)改進(jìn),但是代碼仍然和在actors的指定類型偶合,我們?nèi)匀恍枰付ㄔ诖a中和傳遞給addActor()方法的Actor 類型的名字,意思就是當(dāng)我們要改變演員的時(shí)候不得不重新編譯代碼,有什么其他的方法取得演員的類型,而沒有代碼依賴我們想要的演員的類型嗎?

          有一個(gè)辦法就是我們刪除所有依賴具體類型的代碼,指定我們想要的actors的類型在一個(gè)properties文件,在運(yùn)行時(shí)裝入他們。這樣我們就沒有依賴具體演員類型的代碼了。這樣做,我們改變指定的演員類型。替換硬編碼actor類型,用編碼載入類型從一個(gè)叫做actor.propertiesproperties文件。這個(gè)文件每行是你需要的一個(gè)演員類型,看起來像這樣:

          buffalo = headfirst.factory.simulator.Buffalo

          buffalo = headfirst.factory.simulator.Buffalo

          buffalo = headfirst.factory.simulator.Buffalo

          horse = headfirst.factory.simulator.Horse

          cowboy = headfirst.factory.simulator.Cowboy

          cowgirl = headfirst.factory.simulator.Cowgirl

          這是一個(gè)標(biāo)準(zhǔn)格式的java properties文件:等號(hào)兩邊分別是屬性名和屬性值。現(xiàn)在可以替換傳遞給createActor()方法的actor的類型的完整路徑名,我們只要傳遞一個(gè)描述類型的串給他(就象我們的第一個(gè)版本中代碼那樣),這個(gè)串將對(duì)應(yīng)于properties文件中的屬性名:

              simulator.addActor("buffalo");

              simulator.addActor("horse");

              simulator.addActor("cowboy");

              simulator.addActor("cowgirl");

           

          我們同樣需要修改ActorFactorycreateActor()方法,從properties文件中裝入所有的屬性到一個(gè)Properties實(shí)例中。然后傳遞類型給createActor()方法(例如:”buffalo”),取得屬性對(duì)應(yīng)的actor的完整類型名,并用它實(shí)例化成我們需要的actor對(duì)象。

            
            static public Actor createActor(String type) {

                  Class actorClass = null;

                  Actor actor = null;

                  String actorType = null;

                  Properties properties = new Properties();

           

                  try {

                      properties.load(new FileInputStream("simulator.properties"));

                  } catch (IOException e) {

                      System.out.println("Error: couldn't read from the simulator.properties file."

                              + e.getMessage());

                  }

                  actorType = properties.getProperty(type);

                  if (actorType == null || actorType.equals("")) {

                      System.out.println("Error loading actor type for type: " + type);

                  }

           

                  try {

                      actorClass = Class.forName(actorType);

                  } catch (ClassNotFoundException e) {

                      System.out.println("Error: class " + actorType + " not found!");

                      System.out.println(e.getMessage());

                  } catch (Exception e) {

                      e.printStackTrace();

                      System.out.println(e.getMessage());

                  }

           

                  if (actorClass != null) {

                      try {

                          actor = (Actor) actorClass.newInstance();

                      } catch (Exception e) {

                          e.printStackTrace();

                          System.out.println(e.getMessage());

                      }

                  }

                  return actor;

              }

          你當(dāng)然可以添加屬性來指定添加多少的類型到simulator,這樣最好了。

          現(xiàn)在你可以不需要指定任何的actor具體類在你代碼的任何地方,你已經(jīng)完全的解耦了。

           概要

          不同的工廠模式的目的是減少依賴具體的類。我們一步步進(jìn)展并明白了如何移除最后的依賴。首先,我們將具體實(shí)例對(duì)象的代碼移到我們的主要代碼之外,將它放到一個(gè)工廠里。然后我們?cè)谶@個(gè)基礎(chǔ)上改進(jìn)它,再將路徑名和類名傳遞給工廠的基礎(chǔ)上,使它動(dòng)態(tài)地裝入具體的類和實(shí)例化他們,這僅僅是必須確保每一個(gè)傳遞來得類都實(shí)現(xiàn)了工廠的返回接口。最后,我們打破了最后的依賴,從properties文件裝入我們想要的類型到simulator這使我們完全的消除了與具體類的依賴。

          記住,當(dāng)你減少依賴的時(shí)候,你不需保證你的代碼的健壯性、可維護(hù)性、擴(kuò)展性。

            

          完整的代碼

          如果你想試一下這個(gè)程序,你可以拷貝下面的代碼到一個(gè)文件,StampedeSimulatorTestDrive.java:

          package headfirst.factory.simulator;

          import java.io.FileInputStream;

          import java.io.IOException;

          import java.util.Properties;

           

          public class StampedeSimulatorTestDrive {

              public static void main(String[] args) {

                  System.out.println("Stampede Test Drive");

                  StampedeSimulator simulator = new StampedeSimulator();

                  simulator.addActor("buffalo");

                  simulator.addActor("horse");

                  simulator.addActor("cowboy");

                  simulator.addActor("cowgirl");

              }

          }

           

          class StampedeSimulator {

           

              public void addActor(String type) {

                  Actor actor = null;

                  actor = ActorFactory.createActor(type);

                  actor.display();

                  // rest of stampede simulator here

              }

          }

           

          class ActorFactory {

           

              static public Actor createActor(String type) {

                  Class actorClass = null;

                  Actor actor = null;

                  String actorType = null;

                  Properties properties = new Properties();

             try {

                      properties.load(new FileInputStream("simulator.properties"));

                  } catch (IOException e) {

                      System.out.println("Error: couldn't read from the simulator.properties file."

                                          + e.getMessage());

                  }

                  actorType = properties.getProperty(type);

                  if (actorType == null || actorType.equals("")) {

                      System.out.println("Error loading actor type for type: " + type);

                  }

           

                  try {

                      actorClass = Class.forName(actorType);

                  } catch (ClassNotFoundException e) {

                      System.out.println("Error: class " + actorType + " not found!");

                      System.out.println(e.getMessage());

                  } catch (Exception e) {

                      e.printStackTrace();

                      System.out.println(e.getMessage());

                  }

           

                  if (actorClass != null) {

                      try {

                          actor = (Actor) actorClass.newInstance();

                      } catch (Exception e) {

                          e.printStackTrace();

                          System.out.println(e.getMessage());

                      }

                  }

                  return actor;

              }

          }

           

          interface Actor { 

              public void display();

          }

           

          class Buffalo implements Actor { 

              public void display() {

                  System.out.println("I'm a Buffalo");

              }

          }

           

          class Horse implements Actor { 

              public void display() {

                  System.out.println("I'm a Horse");

              }

          }

           

          class Cowboy implements Actor { 

              public void display() {

                  System.out.println("I'm a Cowboy");

              }

          }

           

          class Cowgirl implements Actor { 

              public void display() {

                  System.out.println("I'm a Cowgirl");

              }

          }

          確認(rèn)保存這個(gè)文件到目錄src/headfirst/factory/simulator.如果你已經(jīng)下載了Head First Design Patterns中的代碼code你就已經(jīng)有了src/headfirst/factory目錄,只要新建一個(gè)simulator目錄在factory目錄就可以了創(chuàng)建一個(gè)class目錄保存你的class文件。

          確認(rèn)保存這個(gè)文件到目錄src/headfirst/factory/simulator.如果你已經(jīng)下載了Head First Design Patterns中的代碼code你就已經(jīng)有了src/headfirst/factory目錄,只要新建一個(gè)simulator目錄在factory目錄就可以了創(chuàng)建一個(gè)class目錄保存你的class文件。

              不要忘了創(chuàng)建一個(gè)simulator.properties文件,包括你的屬性項(xiàng)(這個(gè)文件是最重要的):

           

          
          

          
          

          buffalo = headfirst.factory.simulator.Buffalo

          horse = headfirst.factory.simulator.Horse

          cowboy = headfirst.factory.simulator.Cowboy

          cowgirl = headfirst.factory.simulator.Cowgirl

          現(xiàn)在可以編譯、運(yùn)行代碼象下面:

           

          javac -d ./classes ./src/headfirst/factory/simulator/StampedeSimulatorTestDrive.java

          java -cp ./classes headfirst.factory.simulator.StampedeSimulatorTestDrive

          你可以看到下面的輸出:

           

          Stampede Test Drive

          I'm a Buffalo

          I'm a Horse

          I'm a Cowboy

          I'm a Cowgirl
          posted on 2005-05-26 09:20 zming's blog 閱讀(2124) 評(píng)論(2)  編輯  收藏 所屬分類: Java Tech

          評(píng)論

          # re: 打破最后的依賴-Head First Design Patterns對(duì)工廠的解釋 2005-06-22 08:14 sniper
          請(qǐng)問你是從哪里找到Head First Design Patterns的 ?  回復(fù)  更多評(píng)論
            

          # re: 打破最后的依賴-Head First Design Patterns對(duì)工廠的解釋 2006-02-06 12:49 triper
          >記住,當(dāng)你減少依賴的時(shí)候,你不需保證你的代碼的健壯性、可維護(hù)性、擴(kuò)展性.

          Remember, when you reduce dependencies, you make your code more flexible and easier to maintain and extend.   回復(fù)  更多評(píng)論
            

          主站蜘蛛池模板: 延寿县| 长岭县| 安仁县| 安徽省| 连江县| 巩留县| 寿宁县| 镇宁| 治多县| 乐东| 深水埗区| 东山县| 漳浦县| 建德市| 买车| 扶风县| 宁陵县| 耿马| 井研县| 海城市| 阿克苏市| 郴州市| 罗城| 休宁县| 师宗县| 南靖县| 汉川市| 柞水县| 克什克腾旗| 康乐县| 综艺| 兰西县| 乐安县| 海南省| 当雄县| 巴彦县| 克山县| 元谋县| 青神县| 准格尔旗| 鸡西市|