As a practical example, imagine a Gripper class which represents a robotic gripper in a simulation.
Gripper has a TryGrip method, which checks if there's a GrippableItem in the correct position (within the gripper's rectangle). If there is, the gripper takes control of the item until it is dropped:
class GripperItemLocator //Struggling to find a good name
{
public:
virtual std::unique_ptr<GrippableItem>
FindItemInRange(Rect rect) = 0;
};
class Gripper
{
public:
bool
TryGrip(GripperItemLocator& itemLocator)
{
auto item = itemLocator.FindItemInRange(CalcCurrentRect());
if (item)
{
TakeControlOfItem(std::move(item));
}
}
private:
Rect
CalcCurrentRect() const;
void
TakeControlOfItem(std::unique_ptr<GrippableItem> item);
};
In accordance with the dependency inversion principle, we have a GripperItemLocator abstraction in order to decouple the gripper from the rest of the world. If the where and how GrippableItem's are located in our program changes, this does not concern the gripper, only the class(es) that implement GripperItemLocator must change.
I'm wondering if we really need interface classes for this? Say we just have a regular class doing the same job:
class GripperItemLocator
{
public:
std::unique_ptr<GrippableItem>
FindItemInRange(Rect rect)
{
//Immediate implementation
}
};
I realize this does no longer have the ability to select different implementations at run-time, but say we're 100% sure there won't ever be a need for this, we're only interested in the decoupling. Do we lose anything else that I'm overlooking, or is this just as good ?
interfaceor abstract classes. Indeed, the DIP applies equally to programming languages which have no concept of aninterface. It's a (deliberately) language-agnostic bit of advice, since there are many different possible ways to build abstractions which have nothing whatsoever to do with classes -- DIP is about reducing coupling rather than about use of any particular patterns or language features.GrippableItemdata or theFindItemInRange()behaviour to theGripper::TryGrip()method?GrippableItems are stored could require change over time if the world around the gripper changes. A second conveyor might be installed, each conveyor now holding it's own collection ofGrippableItems. We want to decouple theGripperfrom such changes, onlyGripperItemLocatorwould require an update to match the new situation. But we do not require run-time polymorphism, and such changes would be rare. The program would only contain a singleGripperItemLocatorimplementation at a time.