3

Suppose I have 2 classes (which don't show methods to look simpler):

public interface Fruit{
}

public class Orange implements Fruit{
}

, and assume I can use 2 ways to initialize Orange without actual difference (means both can compile and work at the same way in my code):

1.Abstract one:

Fruit obj=new Orange();

2.Concrete one:

Orange obj=new Orange();

,according to Understanding "programming to an interface", as I understand, I should use the abstract one, because it fits more "programming to interface" and have less coupling.

But I don't quite understand why the abstract one has less coupling ,because I think the abstract one has 2 keywords of classes : Fruit and Orange, while the concrete one has 1 keyword only : Orange, which means the abstract one depend on more classes. While I agree the abstract one may allow me to switch from Orange to other type (eg:Grape) with erasing and typing less characters, it still contains 2 keywords after switching(Fruit and Grape).

Also I found the other disadvantage of the abstract one is , if one day, Orange needs to remove the interface, or change to other interface:

public class Orange{
}

or public interface CircleShape{ }

public class Orange implements CircleShape{
}

, the abstract one needs to modify, while the concrete one doesn't need to do so. So I think the concrete one has less coupling than the abstract one. Is that true? If not, what is the misconception here?

  • 1
    Does this answer your question? Does it make sense to declare private fields using an interface as their type? – gnat Dec 24 '19 at 7:29
  • 4
    I believe you are struggling with the word "abstract". To abstract, in this context, means to remove details that interfere with the understanding or usage of something. If obj only needs to behave as a Fruit, then you have abstracted the details of its "orange-ness". If Orange needs to become no longer a Fruit later on, then you will have to change the definition and usage everywhere, and abstraction no longer plays a role. – BobDalgleish Dec 24 '19 at 15:05
  • You can't create a design that supports every kind of change imaginable; it's not about that. Rather, you design for most likely kinds of changes, and as you learn more about the problem you are trying to solve, you adjust the design along the way until it somewhat stabilizes. That's how you decide what to uncouple, and that gives you context to be able to notice certain patterns and understand how to codify them into abstractions (interfaces, base classes, whatever), decoupling parts of your system. "Programming to an interface" means writing client code in terms of those abstractions. – Filip Milovanović Dec 26 '19 at 17:39
  • 2
    Does this answer your question? Why are interfaces useful? – Jesse C. Slicer Dec 26 '19 at 19:51
3

The coupling here happens between Orange and the rest of your program. If you retain an instance of Orange, then you could conceivably call method specific to Orange and not of Fruit. This is bad for two reasons: A) using methods specific to Orange means you could not act on that instance simply as a Fruit instance (violates Liskov Substitution Principle), and B) it implies that "Orange" is not a "Fruit" in that it behaves differently.

Using a more concrete example, suppose you need a new list and so you do:

ArrayList<String> list = new ArrayList<String>();

You decide you want to pass this to a method, and so of course your method becomes:

public void handleList(ArrayList<String> list) {
    // ...
}

If you think about it, handleList method doesn't particularly care that it is an instance of ArrayList or even List most likely. It need only deal with every object in that collection at least once. So you could safely rewrite it as the following and it would allow you to potentially pass other implementations (your own included!):

public void handleList(Collection<String> list) {
    // ...
}

If you wanted to take this a step further, you would use a wildcard (?) in the place of String, and call toString() on every item in the collection. The point is that we're not being obliged to use an instance of ArrayList. We could use LinkedList or HashSet or TreeSet instead.

Returning back to your example, ideally in your program, you should care about the fact that it uses Orange as opposed to Grape or Apple classes exactly in one place in your program. After this point, you should only use these instances as Fruit, or to put it another way, you no longer care how it is implemented, you only care how it is used.

That said, it can be convenient to have an abstract common class with common functionality between implementations. This is not a substitute for an interface. An interface defines how it will be used. An abstract class is meant to reduce and simplify your implementations. Conceivably you could have multiple abstract classes with themselves each with several concrete implementations, all of which deriving from a single interface that you use throughout your program.

For more reading, take a look at this article explaining a bit about Liskov Substitution Principle, specifically the part regarding the Open closed principle. You should have a clear understanding in your program where you're defining objects and where you're using them. If they're not in the same location, you should most definitely be using interfaces to define how they're used.

  • 2
    Being able to use a method specific to Orange is of course good if that is what you actually wanted - if you want to do things with the object that you can't do for arbitrary Fruit objects. – gnasher729 Dec 24 '19 at 14:21
2

As a general principle, you should use the smallest amount of information necessary.

In your example, if Orange implements the interface Fruit, and if your code works fine without the knowledge that the object is an Orange object, then it should be declared as a Fruit interface. This means there is no need to change anything if you want to have a Strawberry object, except changing the first line to "Fruit obj = new Strawberry();". Of course if parts of the following code expect an Orange object and use methods that are not present in the Fruit interface, then you start with "Orange obj = new Orange();" And you know that your code won't just work with a Strawberry object. That's tough, it requires code changes, but these code changes are necessary.

If you change the interface that Orange implements - well, you can't just change that interface. You will implement both interfaces. Or you will remove the methods that are needed for Fruit and add methods for the new interface. Your code creating the Orange object isn't going to work anymore. Declaring Orange obj = new Orange(); wouldn't have helped you because most likely you were calling interface methods that are not there anymore. Changing the interface is a major change with major consequences. Your FruitSalad class is unlikely to be able to work with an Orange object if it doesn't implement the Fruit interface.

0

This comes down to the question of

Why do we abstract anyways?

The point of abstraction is to do more with less. When you see code as rules like don't talk to men you don't know who wear dark suits and black hats and don't talk to strangers you have two rules, the latter being more abstract than the former.

So you end up with an abstract rule having less words, being more general and so dealing with more cases.

But what about talking to doctors which may be a) strangers and b) men?

Then the rule doesn't make sense and you have to deal with exceptions to the rule.

What has this to do with your problem?

Having an interface of Fruit you made an abstraction. And every instruction dealing with $things which behave like fruits is very general and could be applied to many fruity things.

You do not want to write code for every single Fruit you know of.

But as the example with above, this falls apart when you need Orange behavior, which may be richer than only fruity behavior.

So "implement to an interface" is a rule of thumb which means:

It is a good idea to group objects sharing a common behavior together with an interface and write instructions for these objects to write less code than for each individual object. But sometimes this rule doesn't hold.

P.S.: "Coupling" means in this "depending on specialization" or being less general. So when you make general rules, you avoid specialization = coupling.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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