3

I don't seem to grasp the concept of how interfaces will implement loose coupling? You might find this question to be a duplicate of some other question but I've read many answers related to this topic and I haven't found a satisfactory explanation.

Below is an example of how many developers implement loose coupling.

interface shape {
   public function sayName();
}

class Circle implements shape {

     public function sayName(){
          echo 'Hello my name is circle';
      }
}

class Square implements shape {

     public function sayName(){
          echo 'Hello my name is square';
      }
}

class Creator {
       public $shape = null;
       public function __construct(Shape $shape){
          $this->shape = $shape;
       }
}

$circle = new Creator(new Circle());
$circle->sayName();

$square = new Creator(new Square());
$square->sayName();

In the above example we are using polymorphism of interface to achieve loose coupling. But I don't see that this code is loosely coupled. In the above example the calling code (client) is directly referring to "Circle" and "Square" classes using "new" operator hence creating tight coupling.

To solve this problem we can do something like this.

interface shape { public function sayName(); }

class Circle implements shape {

     public function sayName(){
          echo 'Hello my name is circle';
      }
}

class Square implements shape {

     public function sayName(){
          echo 'Hello my name is square';
      }
}

class Creator {
       public $shape = null;
       public function __construct($type){
          if(strtolower($type) == 'circle'){
             $this->shape = new Circle();
          } else if(strtolower($type) == 'square'){
             $this->shape = new Square();
          } else {
             throw new Exception('Sorry we don\'t have '.$type.' shape');
          }
       }
}

$circle = new Creator('Circle');
$circle->sayName();

$square = new Creator('Square');
$square->sayName();

This example fixes the problems of previous example but we don't use interface at all.

My question is why should I use interface if I can implement loose coupling without it? what benefits would the interface offer in the above scenario? or what problems would I face if I don't use interface in the above example?

Thanks for your help.

Jay Bhatt
  • 5,601
  • 5
  • 40
  • 62
  • 3
    And by your method you have to rewrite the Creator constructor() method code every time you want a new shape like a triangle, or a trapezoid, or a pentagon.... loose coupling means __not__ having to rewrite the Creator code, simple create a new class that implements Shape – Mark Baker Jul 29 '13 at 16:30
  • @MarkBaker You are wrong. I will have one creator and I can have thousands of clients. What's better rewrite one creator or thousand clients? – Jay Bhatt Jul 29 '13 at 16:33
  • @JayBhatt that makes no sense. – Orangepill Jul 29 '13 at 16:34
  • @MarkBaker I.e. If my class name changes from Circle to CircleV1? I can easily manage this inside the creator rather then updating thousand clients – Jay Bhatt Jul 29 '13 at 16:34
  • @Orangepill which part? – Jay Bhatt Jul 29 '13 at 16:34
  • And when you have 1000 shapes for your creator, how many lines of code will the constructor require.... and there's no need to rewrite 1000 shapes, simply create new shapes as and when you need them without ever needing to change the creator class in any way... and if your class name changes from Circle to CircleV1, you don't need to change the Creator, only the Circle class name and the caller – Mark Baker Jul 29 '13 at 16:36
  • @MarkBaker fair enough. – Jay Bhatt Jul 29 '13 at 16:38
  • @MarkBaker The second approach will have complications. Cheers... – Jay Bhatt Jul 29 '13 at 16:40
  • 1
    Interfaces don't have anything to do with what your are trying to accomplish in your Creator class. You are trying to develop a system to map string values to an class implementation. The only thing interfaces have to do with that is that the resultant class should implement the interface. – Orangepill Jul 29 '13 at 16:41
  • Look into Service Location and Dependency Injection for a cleaner way to handle this type of problem. – Orangepill Jul 29 '13 at 16:42
  • @Orangepill Its true that the interface doesn't play much role in the second example but still I have seen lots of such examples. One is this http://sourcemaking.com/design_patterns/strategy/php would you care to explain why is this so? Is this a good design practice or just to handle future changes? – Jay Bhatt Jul 29 '13 at 16:48
  • 1
    This is a simplified service location strategy, but imho I don't think it is scaleable enough to handle 1000's of types without creating a nightmare to maintain. Other implementations use some sort of config file to map strings to class implementations. The key point is what comes out of the Creator should implements the required interface. – Orangepill Jul 29 '13 at 16:54
  • @MarkBaker sincere apologies for using harsh language I was too much frustrated with this whole thing. – Jay Bhatt Jul 29 '13 at 17:08
  • @Jay - no problem, I didn't consider your language particularly harsh; though the example isn't really doing much in and of itself – Mark Baker Jul 29 '13 at 17:21

1 Answers1

5

As others have noted, what you're doing is more along the lines of dependency injection, which is a related, but separate topic from loose coupling. I'll try to give some insight into the simple application of loose coupling via interfaces.

I tend to think of interfaces as a handy way of defining/enforcing contracts. Instead of the simplistic example in which every shape can say hello, consider a case where you need to render each shape to an image.

This pseudo-code shows how you might deal with Squares and Circles with no interface.

class Circle {
  function renderCircle() { ... }
}

class Square {
  function renderSquare() { ... }
}

// then when we use them
Circle[] circlesArray = loadCircles
Square[] squaresArray = loadSquares

foreach(circle in circlesArray){
  circle.renderCircle
}

foreach(square in squaresArray){
  square.renderSquare
}

If instead, we say that we don't so much care about what type of shape it is, but only that you can render it, we end up with the following interface:

interface renderableShape {
  function render()
}

And so on cases where you are only concerned about the ability to render the shape, you program against that interface.

You could have something like this:

function renderShapes(Array[renderableShape] shapes){
  foreach(shape in shapes){
    shape.render()
  }
}

You're now ensuring that your renderShapes function has no visibility into any of the specific details of the Circle or Square implementations. All it needs to know is that you can call render.

Larsenal
  • 49,878
  • 43
  • 152
  • 220