2009-01-27 28 views
112

Tôi vừa mới bắt đầu chơi với Guice và một trường hợp sử dụng tôi có thể nghĩ là trong một bài kiểm tra tôi chỉ muốn ghi đè một ràng buộc duy nhất. Tôi nghĩ rằng tôi muốn sử dụng phần còn lại của các ràng buộc mức sản xuất để đảm bảo mọi thứ được thiết lập đúng và để tránh trùng lặp.Overriding Binding in Guice

Vì vậy, tưởng tượng tôi có Module sau

public class ProductionModule implements Module { 
    public void configure(Binder binder) { 
     binder.bind(InterfaceA.class).to(ConcreteA.class); 
     binder.bind(InterfaceB.class).to(ConcreteB.class); 
     binder.bind(InterfaceC.class).to(ConcreteC.class); 
    } 
} 

Và trong thử nghiệm của tôi, tôi chỉ muốn ghi đè InterfaceC, trong khi vẫn giữ InterfaceA và InterfaceB trong sự khéo léo, vì vậy tôi muốn một cái gì đó như:

Module testModule = new Module() { 
    public void configure(Binder binder) { 
     binder.bind(InterfaceC.class).to(MockC.class); 
    } 
}; 
Guice.createInjector(new ProductionModule(), testModule); 

tôi cũng đã thử những điều sau đây, không có may mắn:

Module testModule = new ProductionModule() { 
    public void configure(Binder binder) { 
     super.configure(binder); 
     binder.bind(InterfaceC.class).to(MockC.class); 
    } 
}; 
Guice.createInjector(testModule); 

có ai biết nếu nó có thể làm những gì tôi muốn hoặc tôi hoàn toàn sủa cây sai ??

--- Theo dõi: Có vẻ như tôi có thể đạt được những gì tôi muốn nếu tôi tận dụng các thẻ @ImplementedBy trên giao diện và sau đó chỉ cần cung cấp một liên kết trong trường hợp kiểm tra, trong đó hoạt động độc đáo khi có một 1-1 ánh xạ giữa giao diện và triển khai.

Ngoài ra, sau khi thảo luận điều này với một đồng nghiệp dường như chúng ta muốn đi xuống con đường của trọng toàn bộ mô-đun và đảm bảo chúng tôi có các module của chúng tôi được xác định một cách chính xác. Điều này có vẻ như nó có thể gây ra một vấn đề mặc dù một ràng buộc bị thất lạc trong một mô-đun và cần phải được di chuyển, do đó có thể phá vỡ một tải các bài kiểm tra như ràng buộc có thể không còn có sẵn để được overriden.

+7

Giống như cụm từ "sủa cây sai": D –

Trả lời

124

Điều này có thể không phải là câu trả lời bạn đang tìm kiếm, nhưng nếu bạn đang viết bài kiểm tra đơn vị, có lẽ bạn không nên sử dụng một vòi phun và thay vì được tiêm các đối tượng giả hoặc giả mạo bằng tay.

Mặt khác, nếu bạn thực sự muốn thay thế một ràng buộc duy nhất, bạn có thể sử dụng Modules.override(..):

public class ProductionModule implements Module { 
    public void configure(Binder binder) { 
     binder.bind(InterfaceA.class).to(ConcreteA.class); 
     binder.bind(InterfaceB.class).to(ConcreteB.class); 
     binder.bind(InterfaceC.class).to(ConcreteC.class); 
    } 
} 
public class TestModule implements Module { 
    public void configure(Binder binder) { 
     binder.bind(InterfaceC.class).to(MockC.class); 
    } 
} 
Guice.createInjector(Modules.override(new ProductionModule()).with(new TestModule())); 

Xem chi tiết here.

Nhưng như javadoc cho Modules.overrides(..) đề xuất, bạn nên thiết kế mô-đun của mình theo cách mà bạn không cần ghi đè lên các ràng buộc. Trong ví dụ bạn đã cung cấp, bạn có thể thực hiện điều đó bằng cách di chuyển liên kết InterfaceC thành một mô-đun riêng biệt.

+7

Cảm ơn Albert, điều đó khiến tôi gần như xuống đường để làm những gì tôi muốn. Đó là một bản phát hành sản phẩm nhưng tho! Và đây là bài kiểm tra tích hợp, không phải kiểm tra đơn vị, mà tôi lý do tại sao tôi muốn đảm bảo mọi thứ khác đang được xây dựng chính xác – tddmonkey

+1

Tôi đã thêm một ví dụ cụ thể vào mã. Nó có giúp bạn tiến xa hơn không? – albertb

+1

Trừ khi tôi bị nhầm lẫn, 'ovveride' mất' Giai đoạn' thích hợp trong khi làm như vậy (tức là PHÁT TRIỂN được sử dụng một cách có hệ thống). – pdeschen

9

Tại sao không sử dụng quyền thừa kế? Bạn có thể ghi đè lên các ràng buộc cụ thể của mình trong phương thức overrideMe, để lại các triển khai được chia sẻ theo phương thức configure.

public class DevModule implements Module { 
    public void configure(Binder binder) { 
     binder.bind(InterfaceA.class).to(TestDevImplA.class); 
     overrideMe(binder); 
    } 

    protected void overrideMe(Binder binder){ 
     binder.bind(InterfaceC.class).to(ConcreteC.class); 
    } 
}; 

public class TestModule extends DevModule { 
    @Override 
    public void overrideMe(Binder binder) { 
     binder.bind(InterfaceC.class).to(MockC.class); 
    } 
} 

Và cuối cùng tạo phun của bạn theo cách này:

Guice.createInjector(new TestModule()); 
+1

Chữ '@ Override' dường như không hoạt động. Đặc biệt là nếu nó được thực hiện trên một phương thức '@ Provides' một cái gì đó. –

2

Bạn muốn sử dụng Juckito nơi bạn có thể khai báo các cấu hình tùy chỉnh của bạn cho mỗi lớp thử nghiệm.

@RunWith(JukitoRunner.class) 
class LogicTest { 
    public static class Module extends JukitoModule { 

     @Override 
     protected void configureTest() { 
      bind(InterfaceC.class).to(MockC.class); 
     } 
    } 

    @Inject 
    private InterfaceC logic; 

    @Test 
    public testLogicUsingMock() { 
     logic.foo(); 
    } 
} 
3

Nếu bạn không muốn thay đổi mô-đun sản xuất của bạn và nếu bạn có một cấu trúc dự án mặc định maven-like như

src/test/java/... 
src/main/java/... 

Bạn chỉ có thể tạo ra một lớp mới ConcreteC trong thư mục thử nghiệm của bạn sử dụng cùng một gói như cho lớp học ban đầu của bạn. Sau đó, Guice sẽ liên kết InterfaceC với ConcreteC từ thư mục kiểm tra của bạn trong khi tất cả các giao diện khác sẽ bị ràng buộc vào các lớp sản xuất của bạn.

1

Trong một thiết lập khác, chúng tôi có nhiều hoạt động được xác định trong các mô-đun riêng biệt. Hoạt động đang được tiêm vào trong Mô-đun thư viện Android, với định nghĩa mô-đun RoboGuice của riêng nó trong tệp AndroidManifest.xml.

Thiết lập trông như thế này. Trong Module Thư viện có những định nghĩa:

AndroidManifest.xml:

<application android:allowBackup="true"> 
    <activity android:name="com.example.SomeActivity/> 
    <meta-data 
     android:name="roboguice.modules" 
     android:value="com.example.MainModule" /> 
</application> 

Sau đó, chúng ta có một loại được tiêm:

interface Foo { } 

Một số cài đặt mặc định của Foo:

class FooThing implements Foo { } 

MainModule định cấu hình triển khai FooThing cho Foo:

public class MainModule extends AbstractModule { 
    @Override 
    protected void configure() { 
     bind(Foo.class).to(FooThing.class); 
    } 
} 

Và cuối cùng, một Hoạt động tiêu thụ Foo:

public class SomeActivity extends RoboActivity { 
    @Inject 
    private Foo foo; 
} 

Trong tiêu thụ Ứng dụng mô-đun Android, chúng tôi muốn sử dụng SomeActivity nhưng, đối với mục đích thử nghiệm, tiêm riêng Foo của chúng tôi.

public class SomeOtherActivity extends Activity { 
    @Override 
    protected void onResume() { 
     super.onResume(); 

     Intent intent = new Intent(this, SomeActivity.class); 
     startActivity(intent); 
    } 
} 

Người ta có thể tranh luận để lộ các xử lý cho các ứng dụng client module, tuy nhiên, chúng ta cần phải chủ yếu là giấu các thành phần được tiêm vì Module thư viện là một SDK, và phơi bày phần có ý nghĩa lớn hơn.

(Hãy nhớ rằng, đây là để thử nghiệm, vì vậy chúng tôi biết nội bộ của SomeActivity, và biết nó tiêu thụ một (gói có thể nhìn thấy) Foo).

Cách tôi thấy hoạt động có ý nghĩa; sử dụng ghi đè gợi ý cho thử nghiệm:

public class SomeOtherActivity extends Activity { 
    private class OverrideModule 
      extends AbstractModule { 

     @Override 
     protected void configure() { 
      bind(Foo.class).to(OtherFooThing.class); 
     } 
    } 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 
     RoboGuice.overrideApplicationInjector(
       getApplication(), 
       RoboGuice.newDefaultRoboModule(getApplication()), 
       Modules 
         .override(new MainModule()) 
         .with(new OverrideModule())); 
    } 

    @Override 
    protected void onResume() { 
     super.onResume(); 

     Intent intent = new Intent(this, SomeActivity.class); 
     startActivity(intent); 
    } 
} 

Bây giờ, khi SomeActivity được khởi động, nó sẽ nhận được OtherFooThing cho tiêm Foo cá thể của nó.

Đó là một tình huống rất cụ thể, trong trường hợp của chúng tôi, OtherFooThing được sử dụng nội bộ để ghi lại các tình huống thử nghiệm, trong khi FooThing được sử dụng, theo mặc định, cho tất cả các mục đích sử dụng khác.

Hãy nhớ, chúng tôi sử dụng #newDefaultRoboModule trong các bài kiểm tra đơn vị của chúng tôi và hoạt động hoàn hảo.