3

I have two camera devices that are able to find a specific hardware illuminated point in an image, as well as measuring the physical distance to the surface (and some more stuff I chose to leave out, as it doesn't affect this problem right now), and the application uses the point and distance information independently of each other. Aha - same functionality with different implementations? I should use an interface! I started to define some, but as you'll see I'm probably making some mistakes in my design.

interface IPointFinder {
    // The point where the spot is 
    Point FindPoint(Image image);
}

interface IDistanceMeasurement { // Distance in mm int MeasureDistance(); }

interface IDevice { string DeviceName { get; } IPointFinder { get; } IDistanceMeasurement { get; } event CameraImage; }

The reason for the device owning the interface ISpotFinder is that the spot will be illuminated on the surface in different ways depending on the hardware (trying to keep things that will change for the same reason together).

There are two implementations of the IDevice interface. One has an external hardware device for measuring the distance and calculates where the spot is from the distance value (hence ignoring the image parameter), and the other one calculates the distance value from the spot found by SpotFinder. I thought, "Well, I'll just make this dependency invisible for the application, and hide it in the implementation of IDevice (for example by letting the distance measurement observe the point finder)", but that was not really optimal as:

  1. If the distance is dependent on that the point has been found, then it requires an image to have been taken, and processed. Calling this before that method would not return any valuable information.
  2. If the point is dependent on that the distance has been measured, then it requires that the client code already called DistanceMeasurement.MeasureDistance(), otherwise it would not be able to get the point.

The "sequence of execution"-dependency (lack of better term) (edit: I learned from the comments that this is called Temporal Coupling) is hence reversed depending on the implementation, which makes the interface rather useless, as implementation changes would change the way the interface is used.

I'm suspecting my view on the problem is too narrow, and think I could rework this. I would appreciate some feedback. What I thought of so far:

  1. Make the two interfaces into one MeasureDistanceAndPoint which would return both in one go. This feels appropriate as they are clearly related, but a bit weird since the values are used independently in the application, as mentioned.
  2. Define arguments for IDistanceMeasurement so that it would require a point as input. Then the IPointFinder could trigger the external distance measurement silently and calculate the point when external distance measurement is used.
  3. Rethink this altogether, which is probably the way to go, but I spent two full days on it and am getting a bit crazy.
Mattias
  • 173

1 Answers1

1

Constructors are your friend. They don't use inheritance so you can have extra dependencies inserted into one class without affecting another.

To simplify your case lets have the two devices as:

DeviceA
    GetDistance(spot)
    GetSpot()

DeviceB GetDistance() GetSpot(distance)

To make a single interface we need to remove the parameters from the methods

IDevice
    GetDistance()
    GetSpot()

We can then add a constructor with the required dependency

DeviceA :IDevice
    Constructor(spot)
    GetDistance()
    GetSpot()

DeviceB :IDevice Constructor(distance) GetDistance() GetSpot()

Now you have compile time checking of your dependency order, and the order is different for each device.

It also helps you to decide what the object should be called, a Device that you can only use once per spot or distance measure doesn't sound like a device, perhaps its called Finder or something.

Also, having both methods on a single class is now problematic. You could add some static methods but a neater method would be to separate them (also helping with the naming

DistanceMeasurerA :IDistanceMeasurer
    Constructor(spot)
    GetDistance()

DistanceMeasurerB :IDistanceMeasurer Constructor() GetDistance()

Now we can have your IDevice composite class with internal spot and distance finders which will need to be created

DeviceA(DistanceMeasurerAFactory ,SpotFinderA)
    GetDistance()
    {
       spot = this.spotFinder.findSpot();
       dist = DistanceMeasurerAFactory.Create(spot).GetDistance();
       return dist;
    }

The classes enforce the order of the two calls at compile time, everything has shared interfaces and your naming and class division is solved!

Ewan
  • 83,178