Abstracting related functionality using interfaces vs tight coupling

decoupling, oop, php

Solution

Classes are said to be tightly coupled when one class has a dependency on another or relies on the internals or specifics of the other.

In the case of the first example you gave OrderGenerator and OrderFormatter are not tightly coupled because neither one is dependent on the other: they both depend on the iFormatter. Another way of putting it is OrderFormatter knows nothing about OrderGenerator and OrderGenerator knows nothing about OrderFormatter, it just knows that the object that is passed to it implements the function 'format'. You have highlighted this when you pass either CarOrderFormatter or VanOrderFormatter to OrderGenerator without adverse result.

In the case of the second example where CarOrderFormatter is passed to CarOrderGenerator you mentioned that surely errors would occur of you passed say VanOrderFormatter to CarOrderGenerator. If the code in CarOrderGenerator is relying on the implementation details of CarOrderFormatter then the classes are tightly coupled even though the iFormatter interface is what the CarOrderGenerator sees. This code would be confusing because CarOrderGenerator is saying with its 'contract' that it doesn't care what formatter is passed but clearly it does. It would therefore be better in terms of the clarity of the code if the CarOrderGenerator explicitly said that you could only pass a CarOrderFormatter to its constructor. The abstraction (that is, the use of the interface) is not a bad thing however and thought needs to be put into how exactly the interface should be defined. Say for example instead of having just an iFormatter interface instead you had iCarOrderFormatter and iVanOrderFormatter both of which are defined the same but the CarOrderGenerator required iCarOrderFormatter and the VanOrderGenerator required iVanOrderFormatter. The your example could become:

$example = new CarOrderGenerator(new CarOrderFormatter
$example->generate();
$example = new CarOrderGenerator(new SportsCarOrderFormatter)
$example->generate();

or

$example = new VanOrderGenerator(new PassengerVanOrderFormatter
$example->generate();
$example = new VanOrderGenerator(new CargoVanOrderFormatter)
$example->generate();

The key thing to realize is that the interface is introduced to create flexibility in what can be passed to the generator without it causing problems.

Problem

I am trying to understand what really defines tight coupling. I have read a number of posts on the subject but one thing still doesn't sit right with me. I understand that classes should be injected into other classes using their interfaces rather their concrete implementations. I also understand that if a class adheres to an interface then any class which uses the injected interface can call the public functions defined in the interface and expect similar functionality. ``` interface iFormatter() { public function format(array $order): array } public class OrderFormatter implements iFormatter { public function format(array $order): array { // ... return $formattedArray; } } public class OrderGenerator implements iGenerator { private $formatter; public function __construct(iFormatter $formatter) { $this->formatter = $formatter; } public function generate() { // ... return $this->formatter->format($order); } } ``` So I think in the case where only the formatter changes, this would be defined as loosely coupled; ``` $example = new OrderGenerator(new CarOrderFormatter); $example->generate(); $example = new OrderGenerator(new VanOrderFormatter); $example->generate(); ``` What I don't quite get though is when you abstract responsibilities away from each other but those classes are still closely tied to each other. Something like ``` $example = new CarOrderGenerator(new CarOrderFormatter); $example->generate(); $example = new VanOrderGenerator(new VanOrderFormatter); $example->generate(); ``` Yes you could pass in a different formatter to these generators, but surely more often that not some error would occur within the `format` function if it is expecting certain data from the `generate` function in the `XXXOrderGenerator` concrete class? So I believe I have abstracted the responsibilities to their own classes in the last example above and although interfaces have been used i'm not sure if this is still tightly coupled or is technically loosely coupled? Or if I have missed the point entirely...

Original source

Related problems