15

(For the purpose of this question, when I say 'interface' I mean the language construct interface, and not an 'interface' in the other sense of the word, i.e. the public methods a class offers the outside world in order to communicate with and manipulate it.)

Loose coupling can be achieved by having an object depend on an abstraction instead of a concrete type.

This allows for loose coupling for two main reasons: 1- abstractions are less likely to change than concrete types, which means the dependent code is less likely to break. 2- different concrete types can be used at runtime, because they all fit the abstraction. New concrete types can also be added later with no need to alter the existing dependent code.

For example, consider a class Car and two subclasses Volvo and Mazda.

If your code depends on a Car, it can use either a Volvo or a Mazda during runtime. Also later on additional subclasses could be added with no need to change the dependent code.

Also, Car - which is an abstraction - is less likely to change than Volvo or Mazda. Cars have been generally the same for quite some time, but Volvos and Mazdas are far more likely to change. I.e. abstractions are more stable than concrete types.

All of this was to show that I understand what loose coupling is and how it is achieved by depending on abstractions and not on concretions. (If I wrote something inaccurate please say so).

What I don't understand is this:

Abstractions can be superclasses or interfaces.

If so, why are interfaces specifically praised for their ability to allow loose coupling? I don't see how it's different than using a superclass.

The only differences I see are: 1- Interfaces aren't limited by single inheritance, but that doesn't have much to do with the topic of loose coupling. 2- Interfaces are more 'abstract' since they have no implementation logic at all. But still, I don't see why that makes such a big difference.

Please explain to me why interfaces are said to be great in allowing loose coupling, while simple superclasses are not.

5
  • 3
    Most languages (e.g. Java, C#) that have “interfaces” only support single inheritance. As each class can only have one immediate superclass, (abstract) superclasses are too limited in order for one object to support multiple abstractions. Check out traits (e.g. Scala or Perl's Roles) for a modern alternative which also avoids the “diamond problem” with multiple inheritance.
    – amon
    Commented Apr 26, 2014 at 18:42
  • @amon So you're saying that the advantage of interfaces over abstract classes when trying to achieve loose coupling is them not being limited by single inheritance?
    – Aviv Cohn
    Commented Apr 26, 2014 at 18:48
  • No, i meant costly in terms of the compiler has more to do when it handles an abstract class, but this could be probably neglected. Commented Apr 26, 2014 at 18:54
  • 2
    It looks like @amon is on the right track, i found this post where it is said that: interfaces are essential for single-inheritance languages like Java and C# because that's the only way in which you can aggregate different behaviors into a single class (which leeds me to the comparison with C++, where interfaces are just classes with pure virtual functions). Commented Apr 26, 2014 at 19:02
  • Please tell who says superclasses are bad. Commented Apr 30, 2014 at 18:47

3 Answers 3

11

Terminology: I'll refer to the language construct interface as interface, and to the interface of a type or object as surface (for a lack of a better term).

Loose coupling can be achieved by having an object depend on an abstraction instead of a concrete type.

Correct.

This allows for loose coupling for two main reasons: 1- abstractions are less likely to change than concrete types, which means the dependent code is less likely to break. 2- different concrete types can be used at runtime, because they all fit the abstraction. New concrete types can also be added later with no need to alter the existing dependent code.

Not quite correct. Current languages do not generally anticipate that an abstraction will change (although there are some design patterns to handle that). Separating specifics from general things is abstraction. This is usually done by some layer of abstraction. This layer can be changed to some other specifics without breaking code that builds upon this abstraction – loose coupling is achieved. Non-OOP example: A sort routine might be changed from Quicksort in version 1 to Tim Sort in version 2. Code that only depends on the result being sorted (i.e. builds upon the sort abstraction) is therefore decoupled from the actual sorting implementation.

What I termed surface above is the general part of an abstraction. It now happens in OOP that one object must sometimes support multiple abstractions. A not-quite optimal example: Java's java.util.LinkedList supports both the List interface which is about the “ordered, indexable collection” abstraction, and supports the Queue interface which (in rough terms) is about the “FIFO” abstraction.

How can an object support multiple abstractions?

C++ doesn't have interfaces, but it has multiple inheritance, virtual methods, and abstract classes. An abstraction can then be defined as an abstract class (i.e. a class that cannot be immediately instantiated) that declares, but not defines virtual methods. Classes that implement the specifics of an abstraction can then inherit from that abstract class and implement the required virtual methods.

The problem here is that multiple inheritance can lead to the diamond problem, where the order in which classes are searched for a method implementation (MRO: method resolution order) can lead to “contradictions”. There are two responses to this:

  1. Define a sane order and reject those orders that can't be sensibly linearized. The C3 MRO is fairly sensible and works well. It was published 1996.

  2. Take the easy route and reject multiple inheritance throughout.

Java took the latter option and chose single behavioral inheritance. However, we still need the ability of an object to support multiple abstractions. Therefore, interfaces have to be used which do not support method definitions, only declarations.

The result is that the MRO is obvious (just look at each superclass in order), and that our object can have multiple surfaces for any number of abstractions.

This turns out to be rather unsatisfactory, because quite often a bit of behavior is part of the surface. Consider an Comparable interface:

interface Comparable<T> {
    public int cmp(T that);
    public boolean lt(T that);  // less than
    public boolean le(T that);  // less than or equal
    public boolean eq(T that);  // equal
    public boolean ne(T that);  // not equal
    public boolean ge(T that);  // greater than or equal
    public boolean gt(T that);  // greater than
}

This is very user-friendly (a nice API with many convenient methods), but tedious to implement. We would like the interface to only include cmp, and implement the other methods automatically in terms of that one required method. Mixins, but more importantly Traits [1],[ 2] solve this problem without falling into the traps of multiple inheritance.

This is done by defining a trait composition so that the traits don't actually end up taking part in the MRO – instead the defined methods are composed into the implementing class.

The Comparable interface could be expressed in Scala as

trait Comparable[T] {
    def cmp(that: T): Int
    def lt(that: T): Boolean = this.cmp(that) <  0
    def le(that: T): Boolean = this.cmp(that) <= 0
    ...
}

When a class then uses that trait, the other methods get added to the class definition:

// "extends" isn't different from Java's "implements" in this case
case class Inty(val x: Int) extends Comparable[Inty] {
    override def cmp(that: Inty) = this.x - that.x
    // lt etc. get added automatically
}

So Inty(4) cmp Inty(6) would be -2 and Inty(4) lt Inty(6) would be true.

Many languages have some support for traits, and any language that has a “Metaobject Protocol (MOP)” can have traits added to it. The recent Java 8 update added default methods which are similar to traits (methods in interfaces can have fallback implementations so that it's optional for implementing classes to implement these methods).

Unfortunately, traits are a fairly recent invention (2002), and are thus fairly rare in the larger mainstream languages.

1
  • Good answer, but I would add that single-inheritance languages can fudge multiple inheritance using interfaces with composition.
    – user22815
    Commented Apr 26, 2014 at 23:41
4

What I don't understand is this:

Abstractions can be superclasses or interfaces.

If so, why are interfaces specifically praised for their ability to allow loose coupling? I don't see how it's different than using a superclass.

First, subtyping and abstraction are two different things. Subtyping merely means that I can substitute values of one type for values of another type - neither type needs to be abstract.

More importantly, subclasses have a direct dependence on the implementation details of their superclass. That's the strongest kind of coupling there is. In fact, if the base class isn't designed with inheritance in mind, changes to the base class that don't change its behaviour can still break subclasses, and there's no way to know a priori if breakage will occur. This is known as the fragile base class problem.

Implementing an interface doesn't couple you to anything except the interface itself, which contains no behavior.

6
  • Thanks for answering. To see if I understand: when you want an object named A to depend on an abstraction named B instead of a concrete implementation of that abstraction named C, it's often better for B to be an interface implemented by C, instead of a superclass extended by C. This is because: C subclassing B tightly couples C to B. If B changes - C changes. However C implementing B (B being an interface) doesn't couple B to C: B is only a list of methods C must implement, thus no tight coupling. However regarding object A (the dependent), it doesn't matter whether B is a class or interface.
    – Aviv Cohn
    Commented Apr 26, 2014 at 23:22
  • Correct? ..... .... .
    – Aviv Cohn
    Commented Apr 26, 2014 at 23:23
  • Why would you consider an interface to be coupled to anything? Commented Apr 26, 2014 at 23:37
  • I think this answer nails it on the head. I use C++ quite a bit, and as was stated in one of the other answers, C++ doesn't quite have interfaces but you fake it by using superclasses with all the methods left as "pure virtual" (i.e. implemented by children). The point is, it's easy to make base classes that DO something along with delegated functionality. In many, many, many cases, I and my coworkers find that by doing that a new use case comes along and invalidates that shared bit of functionality. If there is shared functionality needed, it's easy enough to make a helper class.
    – J Trana
    Commented Apr 27, 2014 at 3:24
  • @Prog Your line of thought is mostly correct, but again, abstraction and subtyping are two separate things. When you say you want an object named A to depend on an abstraction named B instead of a concrete implementation of that abstraction named C you're assuming that classes are somehow not abstract. An abstraction is anything that hides implementation details, so a class with private fields is just as abstract as an interface with the same public methods.
    – Doval
    Commented Apr 28, 2014 at 11:21
1

There is coupling between parent and child classes, since the child depends on the parent.

Say we have a class A, and class B inherits from it. If we go into class A and change things, class B gets changed too.

Say we have an interface I, and class B implements it. If we change interface I, then although class B might not implement it anymore, class B is unchanged.

4
  • I'm curious whether the downvoters had a reason, or were just having a bad day. Commented Apr 26, 2014 at 21:11
  • 1
    I did not downvote, but I think it may have to do with the first sentence. Child classes are coupled to parent classes, not the other way around. The parent need not know anything about the child, but the child needs intimate knowledge about the parent.
    – user22815
    Commented Apr 26, 2014 at 23:43
  • @JohnGaughan: Thanks for the feedback. Edited for clarity. Commented Apr 26, 2014 at 23:56
  • Afaik there's no real difference between the degree of coupling from a child class to a parent class or to an interface, at least in a language like Java, PHP or C# with nominal typing.
    – bdsl
    Commented Dec 14, 2022 at 14:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.