2011-11-18 48 views
0

可測性創建的類正確的設計說我有我的代碼這3個層次:
1.數據庫層(ORM)
2. BusinessLogic
3.應用的使用構造器注入

現在,我寫的我的代碼如下:

  1. 數據庫層:
    這主要對數據庫有CURD操作。

    class MyDatabaseLayer { 
        public int findValue(int k) { 
         // find v 
        } 
    
        public void insertValue(int k, int v) { 
         // Insert v 
        } 
    } 
    
  2. BusinessLogic:
    這個擁有實際的邏輯來調用數據庫層和做的東西。

    class MyBusinessLogic { 
        private MyDatabaseLayer dbLayer; 
        public MyBusinessLogic(MyDatabaseLayer dbLayer) { 
         this.dbLayer = dbLayer; 
        } 
    
        public int manipulateValue(int k) { 
         dbLayer.findValue(k); 
         //do stuff with value 
        } 
    } 
    
  3. 應用層:
    這將調用業務邏輯和顯示數據

    MyBusinessLogic logic = new MyBusinessLogic(new MyDatabaseLayer()); //The problem 
    logic.manipulateValue(5); 
    

現在,如果你在應用層看,它知道數據庫層,這是錯的。它知道太多。

Misko Hevery says:構造函數注入是好的。但如果我遵循這一點,我將如何實現抽象? Google Guice如何幫助我呢?

回答

1

注意,對於可測試性MISKO指的是,你最好要進行接口MyDatabaseLayerMyBusinessLogic等。並有構造函數把這些接口,而不是具體的類,以便在測試時,可以很容易地在不實際使用數據庫假冒實現通等

隨着吉斯,你會綁定接口的具體類的ModuleModule s。然後,您將使用這些Module創建一個Injector,並從Injector獲取一些根對象(例如,您的應用程序對象)。

Injector injector = Guice.createInjector(new AbstractModule() { 
    @Override protected void configure() { 
    bind(MyDatabaseLayer.class).to(MyDatabaseLayerImplementation.class); 
    // etc. 
}); 
MyApplicationLayer applicationLayer = injector.getInstance(MyApplicationLayer.class); 

MyApplicationLayer,你會注入業務邏輯:

@Inject 
public MyApplicationLayer(MyBusinessLogic logic) { 
    this.logic = logic; 
} 

這當然是一個很簡單的例子,還有更復雜的事情可以做。例如,在Web應用程序中,可以使用Guice Servlet在Servlet上使用構造函數注入,而不是在創建對象後直接從Injector中獲取對象。

+0

是的,我有接口。我只是用這個例子來簡化事情。你能指點我一些複雜例子的參考嗎? – zengr

+0

我會看看Guice [用戶指南](http://code.google.com/p/google-guice/wiki/Motivation),包括綁定,範圍,擴展等方面的東西。 – ColinD

+0

Btw,我正在處理一些遺留代碼,我無法爲MyDatabaseLayer提供接口。從Guice的角度來看這會是一個問題嗎? – zengr

1

控制反轉的缺失部分是應用程序層不直接調用構造函數。它使用工廠(IoC容器)來填充構造函數參數。

無論使用何種工具,吉斯/春/ PicoContainer的/單,工廠,你的應用程序代碼應該是這個樣子:

@Controller 
class MyController { 
    @Resource // Some container knows about this annotation and wires you in 
    MyBusinessLogic myBusinessLogic; 

    @RequestMethod("/foo/bar.*") 
    public MyWebResponse doService(Response resp, long id, String val) { 
    boolean worked = myBusinessLogic.manipulatevalue(id, val); 
    return new MyWebResponse(worked); 
    } 
} 

注意,myBusinessLogic可以以多種方式註冊 - Java的@Resource,MyBusinessLogicFactory .getMyBusinessLogic(),guice.get(MyBusinessLogic.class)等

甲差芒的解決辦法是:

package foo; 
class MyBusinessLogicFactory { 

    static volatile MyBusinessLogic instance; // package-scoped so unit tests can override 
    public static MyBusinessLogic getInstance() { 
     if (instance == null) { 
      synchronized(MyBusinessLogicFactory.class) { 
       instance = new MyBusinessLogic(MyDatabaseLayerFactory.getInstance()); 
      } 
     } 
     return instance; 
    } 
} 

// repeat with MyDatabaseLayerFactory 

注第因爲它沒有範圍,所以以上單例模型是非常不鼓勵的。你可以換上述上下文裏 - soething像

class Context { 
    Map<Class,Object> class2Instance = new ConcurrentHashMap<>(); 
    public <T> T getInstance(Class<T> clazz) { 
     Object o = class2Instance.get(clazz); 
     if (o == null) { 
     synchronized(this) { 
      o = class2Instance.get(clazz); 
      if (o != null) return (T)o; 
      o = transitivelyLoadInstance(clazz); // details not shown 
      for (Class c : loadClassTree(clazz)) { // details not shown 
      class2Instance.put(c, o); 
      } 
     } 
     } 
     return (T)o; 
    } 
    ... 
} 

但在這一點上,PicoContainer的,Guice和彈簧可以解決上述的複雜SOOO好得多。另外,像java這樣的符合java 6註解的spring這樣的東西,意味着除了構造函數注入之外,你可以做其他事情,如果你有多個相同基本數據類型的配置項(例如字符串),這非常有用。

+0

「但是在那個時候,picocontainer,guice和spring可以更好地解決上述SOOO的複雜性。」你能詳細說明那一部分嗎? – zengr