2

I want to build a .jar that encapsulates a vendor api, so we can use our own objects as parameters to communicate with the api.

I've read some articles and topics here in SO, but I still am a bit confusing if I'm getting dependency injection right. For examples of House objects with Doors and Windows, it's easy to understand, but it seems it gets difficult with real code.

I didn't want to use any framework / locator pattern. The code below is just a simplification of how the objects are indeed. The Cake object is a big object and so on :)

Is this code fine? Should I change it in order to be easier to test?

//These are the third-party classes
class VendorService {
    public VendorService(String wsdlPath) {/*vendor code*/}
    ICakeApi getApi();
}

interface ICakeApi {
    void authenticate(String username, String password);
    VendorSpecificCake cookCake(VendorSpecificIngredients ingredients);
}

//This is the code I'm trying to use DI
class MyCakeService {
    ICakeApi cakeApi;

    public MyCakeService(ICakeApi cakeApi) {
        this.cakeApi = cakeApi;
    }

    public void authenticate(MyUserPasswordBean bean) {
        cakeApi.authenticate(bean.getUsername(), bean.getPassword());
    }

    MySpecificCake cookCake(MySpecificIngredients ingredients, 
                            VendorObjectFactory vendorFactory, 
                            InternalObjectFactory internalFactory) {

        VendorSpecificIngredients objs =
            vendorFactory.createVendorSpecificIngredients(ingredients);

        VendorSpecificCake vCake = cakeApi.cookCake(objs);
        MySpecificCake myCake = internalFactory.createMySpecificCake(vCake);
        return myCake;
    }
}

class MyCakeServiceFactory {

    MyCakeService build(String wsdlPath) {
        VendorService vendorService = new VendorService(wsdlPath);
        ICakeApi cakeApi = vendorService.getApi();
        MyCakeService service = new MyCakeService(cakeApi);
    }
}

class UsageTest {
    public void testMyCode() {
        MyCakeServiceFactory factory = new MyCakeServiceFactory();

        //should I add this as dependency on the constructor?
        VendorObjectFactory vendorFactory = new VendorObjectFactory();

        InternalObjectFactory internalFactory = new InternalObjectFactor();
        MyCakeService service = factory.build("/tmp");
        service.authenticate(new MyUserPasswordBean("john", "snow"));

        MySpecificIngredients ingr = new MySpecificIngredients(...);

        //ideally, I'd like to avoid having the user to instantiate 
        //VendorObjectFactory and InternalObjectFactory
        service.cookCake(ingr, vendorFactory, internalFactory); 
    }
}
1

I didn't want to use any framework / locator pattern

I understand this, in fact I respect it. But it means your main will be a busy place unless you make good use of creational patterns. Good defaults can allow convention over configuration to simplify things. Surprisingly, this boils down to presenting humans with easy ways to use classes. That doesn't just happen. That's work.

Is this code fine? Should I change it in order to be easier to test?

I see nothing difficult to test here. cookCake() boils down to functional composition and I could inject my own functions (well methods really) at any point. I could certainly inject mocks if needed.

//should I add this as dependency on the constructor?
VendorObjectFactory vendorFactory = new VendorObjectFactory();

Is there ever a good reason for this to change from call to call? Can that reason be satisfied by just building two different MyCakeService instances? What's more readable?

//ideally, I'd like to avoid having the user to instantiate 
//VendorObjectFactory and InternalObjectFactory
service.cookCake(ingr, vendorFactory, internalFactory);

Are there good defaults for these? If most of the time yours uses the same ones, make them the default values.

When you start following the ideas of dependency injection defaults can seem out of place. As long as I can inject something to replace the default this isn't critical.

Some people like to completely forbid new in behavior objects. You can follow this and still put convention over configuration. You just move your defaults into your builder.

The idea is to make your builders very easy for humans to use. Now you're behavior objects can have long painful constructors because only the builders deal with them. If humans spend most of their time using the builders who cares?

The Josh builder mashes these two ideas together to avoid ever having a long constructor. But you don't have to do it that way. Rather than do it with an inner class and an outer class you can just use two full blown classes.

The big thing the Josh builder does isn't actually ridding us of long constructors. It gives us named parameters in languages that don't have them. Named parameters make picking and choosing what defaults to override very easy. That makes putting convention over configuration easy.

And that means no, users don't have to instantiate VendorObjectFactory and InternalObjectFactory by hand. You can offer a builder that builds them as defaults yet makes it easy to inject replacements for them. And the behavior object produced can even be immutable.

You'll have to tune this to your tastes but this has worked well for me. It's a fair bit of boilerplate but the result is flexibility without being overwhelmed with choices.

  • Thanks for the real nice comments!! They helped me to finally get the idea of DI! – Felipe S. Jul 21 '16 at 12:10
  • 1
    C# provides a much better pattern than the builder for replacing long, telescoping constructors: optional method parameters with assignable defaults. – Robert Harvey Jul 22 '16 at 3:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.