C# Design Patterns – Using Interfaces for Loosely Coupled Code

cdependency-managementdesign-patternsinterfacesobject-oriented

Background

I have a project that depends on the usage of a certain type of hardware device, while it doesn't really matter who makes that hardware device as long as it does what I need it to do. With that being said, even two devices that are supposed to do the same thing will have differences when they are not made by the same manufacturer. So I am thinking to use an interface to decouple the application from the particular make/model of device involved, and instead have the interface just cover the highest-level functionality. Here is what I am thinking my architecture will look like:

  1. Define an interface in one C# project IDevice.
  2. Have a concrete in a library defined in another C# project, that will be used to represent the device.
  3. Have the concrete device implement the IDevice interface.
  4. The IDevice interface might have methods like GetMeasurement or SetRange.
  5. Make the application have knowledge about the concrete, and pass the concrete to the application code that utilizes (not implements) the IDevice device.

I am pretty sure that this is the right way to go about it, because then I will be able to change out what device is being used without affecting the application (which seems to happen sometimes). In other words, it won't matter how the implementations of GetMeasurement or SetRange actually work through the concrete (as may differ among manufacturers of the device).

The only doubt in my mind, is that now both the application, and the device's concrete class both depend on the library that contains the IDevice interface. But, is that a bad thing?

I also don't see how the application won't need to know about the device, unless the device and IDevice are in the same namespace.

Question

Does this seem like the right approach for implementing an interface to decouple the dependency between my application and the device that it uses?

Best Answer

I think you've got a pretty good understanding of how decoupled software works :)

The only doubt in my mind, is that now both the application, and the device's concrete class both depend on the library that contains the IDevice interface. But, is that a bad thing?

It doesn't need to be!

I also don't see how the application won't need to know about the device, unless the device and IDevice are in the same namespace.

You can deal with all these concerns with Project Structure.

The way I typically do it:

  • Put all my abstract stuff in a Common project. Something like MyBiz.Project.Common. Other projects are free to reference it, but it may not reference other projects.
  • When I create a concrete implementation of a an abstraction I put it in a separate project. Something like MyBiz.Project.Devices.TemperatureSensors. This project will reference the Common project.
  • I then have my Client project which is the entrypoint to my application (something like MyBiz.Project.Desktop). At startup, the application goes through a Bootstrapping process where I configure abstraction/concrete-implementation mappings. I can instantiate my concrete IDevices like WaterTemperatureSensor and IRCameraTemperatureSensor here, or I can configure services like Factories or an IoC container to instantiate the right concrete types for me later on.

The key thing here is that only your Client project needs to be aware of both the abstract Common project and all the concrete implementation projects. By confining the abstract->concrete mapping to your Bootstrap code you're making it possible for the rest of your application to be blissfully unaware of the concrete types.

Loosely coupled code ftw :)