0

コードに次の 3 つのレイヤーがあるとします。
1. データベース レイヤー (ORM)
2. ビジネス
ロジック 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のコメント: コンストラクター インジェクションは優れていますしかし、それに従えば、どうすれば抽象化を達成できるでしょうか? ここで Google Guice がどのように役立つのでしょうか?

4

2 に答える 2

1

Miskoが参照するテスト容易性のために、理想的には、などのインターフェイスを作成しコンストラクターに具象クラスではなくそれらのインターフェイスを取得させて、テストで実際にデータベースを使用しない偽の実装を簡単に渡すことができるようにすることに注意してください。 、など。MyDatabaseLayerMyBusinessLogic

Guiceを使用すると、インターフェースをaModuleまたはModulesの具象クラスにバインドします。Injector次に、それらを使用して作成し、Moduleからルートオブジェクト(アプリケーションオブジェクトなど)を取得します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サーブレットを使用してサーブレットにコンストラクタインジェクションを使用できますInjector

于 2011-11-18T13:17:52.537 に答える
1

inversion-of-control で欠けている部分は、アプリケーション レイヤーがコンストラクターを直接呼び出さないことです。ファクトリー (IoC コンテナー) を使用してコンストラクター パラメーターを設定します。

使用するツールが何であれ、guice / spring / picocontainer / singleton-factories の場合、アプリケーション コードは次のようになります。

@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

上記のシングルトン モデルにはスコープがないため、使用しないことを強くお勧めします。上記をコンテキスト内にラップできます-次のように

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、および spring は、上記の SOOO の複雑さをはるかにうまく解決できます。

さらに、Java 6 アノテーションを尊重するスプリングのようなものは、コンストラクター注入以外のことを実行できることを意味します。これは、同じ基本データ型 (文字列など) の構成アイテムが複数ある場合に非常に役立ちます。

于 2011-11-18T01:13:21.213 に答える