3

In the following code sample, I have a client that works through a controller. In my specific case the controller establishes a session to an address on a GPIB bus. An instance of this is then injected into the client class, which in my case is a device. The client class knows what to say to the device and uses the controller to pass those messages to the device.

Where I get into trouble is there are various ways to talk to the device. I have created so far ITextReader, ITextWriter, IBinaryReader, IBinaryWriter, and there may be more in the future. The implementations of these interfaces is done in the adapter assembly because that is the only assembly that knows how to talk to the adaptee. The controller and client only have references on the controller.api assembly.

How then does the client ask for an instance of the adapter's TextReader, for which it has no reference to? I figure there is some combination of using a factory pattern and an IoC container (using Unity), but I can't come up with a design where the client can get an instance of any of those reader\writer implementations.

Adapter Assembly:

public class Adapter : IAdapter
{
    public class Adapter(IAdaptee adaptee){..}
}

public class TextReader : TextReaderBase, ITextReader
{
    TextReader(IController controller)
    : base(controller)
    {..}

}

Controller.Api Assembly:

public interface IAdapter{...}
public interface ITextReader{...}
public interface ITextWriter{...}

Controller Assembly:

public class Controller : IController 
{
    Controller (IAdapter adapter){...}
}

public abstract class TextReaderBase : ITextReader
{
    TextReaderBase(IController controller){..}
}

Client Assembly:

public class Client : IClient
{
    Client (IController controller){...}
}

Edited 6/6/2015:

I should make it clearer what I am trying to do. The 3rd party library (adaptee) has low level functions for reading, writing, opening connections, etc. The high level classes for establishing connections, reading, and writing is what I was referring to as the controller. The adapter adapts the 3rd party library to this controller. I made additional interfaces to represent the low level reader\writer like IAdapterReader and IAdapterWriter.

The clients work exclusively through the controller, knowing nothing of the low level adapter stuff. In my current design, I was planning to have a service that reads a config file to find which adapter to instantiate and the address of the device. It creates an instance of this adapter, injects this into the controller class and then injects the controller object into the client. I am calling the controller class here a "session" to represent a connection to a specific address on the bus. The idea is that the client knows what to say to the device (commands and such) and the controller knows how to say it (has access to the physical layer).

For the client to actually talk to the device, I was thinking they would ask for a reader and a writer instance, but clearly this is not then dependency injection. For the client to ask for a reader\writer it would have to know about the adapter as my high level reader\writers wrap the low level ones implemented by the adapter.

I guess the question then is how does the client get a reader\writer? I can think of a crude way by injecting into the client ISession, ITextReader, ITextWriter, IBinaryReader, IBinaryWriter and any other interfaces it may need? The other way is to inject only ISession and make ISession have a T Resolve<T>(Type type) method. The implementation of Resolve could be through an IoC container. I could change the design around so that when the adapter object is being created it also registers all of its implementations with interfaces with an IoC container passed in from the service layer.

  • 1
    In dependency injection, you hand the dependency to the class constructor, not the other way around. – Robert Harvey Jun 5 '15 at 18:14
  • So ITextReader, ITextWriter, IBinaryReader, IBinaryWriter should be injected into either IController or IClient? If injected into IController then IController needs methods to return these instances? – LGreenberger Jun 5 '15 at 18:27
  • If an adapter is designed to convert from one specific interface to another specific interface, do you even need DI on the adapter? Can the adapter provide the necessary DI to the other objects? – Robert Harvey Jun 5 '15 at 18:30
  • Yes it absolutely can. The adapter in fact is the only class\assembly that knows which implementations of those interfaces to instantiate. I think the adapter has to provide the DI to the Controller? – LGreenberger Jun 5 '15 at 18:32
  • But the since the client as i have now only gets injected with IController, does that mean I need to change IController to have methods that return the interfaces that the adapter injected into it? – LGreenberger Jun 5 '15 at 18:36
1

Right now what I see is the following chain of dependencies:

client->controller->adapter->reader->controller

That does create a circular dependency.

You use Dependency Injection to achieve Dependency Inversion. That is, that higher level code does not dependent on the implementation of lower level code.

Looking at the chain, we can attest that there is a piece of code, either the controller or the text reader, that is both high and low level.

If the client does need to use directly the text reader then it cannot be that the adapter uses it as well, as you would need to inject it further up. Rather you will be injecting the adapter into the text reader. And then injecting the text reader into the client.

But if we do that, and we keep the injection of the controller both on the client and the text reader, are we not, then, exposing incorrectly functionality? Because you are saying both that the text reader is of the same level than the controller, and that the controller is at a lower level than the text reader (as it is injected)

Finally, why does the client need to access the text reader used by the adapter? If there is really something that is needed you can provide a pass through method that doesn't expose the fact that you have the text reader somewhere down there.

Check which functionality is going to be lower level (which one actually does the writing/reading) and which one does the high level stuff. Order them higher to level and inject lower into higher.

Without looking at the code,I am imagining that the actual order would be

client->controller->adapter->text read/write 

And that in fact the text reader/writer doesn't need to know about the controller because it is the one reading/writing directly from/to the device. Any information that the controller has that the text reader/writer needs will be passed through parameters.

  • Thank you very much for your post. I edited my original question to add some additional details based on what you wrote. What do you think? – LGreenberger Jun 7 '15 at 1:55
  • I think still doesn't change that much what I said. Rather than exposing the reader/writers, use the controller as a pass through. So you would have: public interface IController { void Write(string value); string Read(); ...} And then do transformations inside the controller to use the right reader/writer. That way, your client doesn't know about the type of reader/writer that you are using, and therefore you keep a clean separation – Miyamoto Akira Jun 8 '15 at 6:26

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.