1

I'm stuck on this concept.

This is part of an explanation I saw on a site:

Hiding the Implementation

A primary consideration in object-oriented design is separating the things that change from the things that stay the same. This is particularly important for libraries. Users (client programmers) of that library must be able to rely on the part they use, and know that they won't need to rewrite code if a new version of the library comes out. On the flip side, the library creator must have the freedom to make modifications and improvements with the certainty that the client code won't be affected by those changes. This can be achieved through convention. For example, the library programmer must agree to not remove existing methods when modifying a class in the library, since that would break the client programmer's code. The reverse situation is thornier, however. In the case of a field, how can the library creator know which fields have been accessed by client programmers? This is also true with methods that are only part of the implementation of a class, and not meant to be used directly by the client programmer. But what if the library creator wants to rip out an old implementation and put in a new one? Changing any of those members might break a client programmer's code. Thus the library creator is in a strait jacket and can't change anything.

But I can't imagine a real situation where this can happen.

Can someone show me a practical example of this in real life?

This is how I imagine this:

public void print(String message)
{
     System.out.println(message);
}

What difference does it make if a library client knows this implementation or not?

Community
  • 1
  • 1
Eduardo M
  • 229
  • 5
  • 14
  • 1
    I think a better example would be if you wrote an interface, then wrote an implementation of that interface. – Powerlord Sep 10 '17 at 02:34
  • 1
    the point is that the client does not have to know about the implementation. it is a responsibility of the library creator. in your example over time a new functionality could be added, say to print into an additional file. Old programs using the library will not have to change (if the library developer is clever enough). – Serge Sep 10 '17 at 02:38
  • The example I always like to refer to is `String#length`. Depending on the version of the JVM you're using, this might return a field, a `char[]`'s length, or something else. But your code doesn't have to change every time you upgrade your JVM, and it's precisely because you didn't have to know or rely on the specific implementation of that method; all you cared about was that it returned, somehow, the String's length. – yshavit Sep 10 '17 at 03:05
  • [A recent post.](https://stackoverflow.com/questions/45912510/does-java-jit-cheat-when-running-jdk-code/45913867#45913867) – chrylis -cautiouslyoptimistic- Sep 10 '17 at 03:12
  • Your example doesn't even have a field. – shmosel Sep 10 '17 at 04:42

4 Answers4

2

The implementation is not really hidden from view. Especially since most libraries out there that you will use are open source. "Hidden", in this case, refers to anything that is not part of the public API. The public API of a library consists of the public methods and fields that a library exposes. Once a library releases a version, it should continue to support the public API on future versions. Anything else is considered hidden and users of that library cannot rely on that "hidden code" being there in future version of that library.

EDIT:

But how could the client rely on code if it was not hidden, for example?

So lets say that a library comes out with a method that looks like this

public void doSomething(CharSequence x);

This is considered not hidden because it is public. Me as a user of that method is expecting it to exist in future versions of that library. So I am expecting that the input to that method is a CharSequence. If the author of that library wants to change that to String then that would be wrong. They should not change that CharSequence to a String because the users of the previous version are expecting CharSequence and switching it to a String might have negative consequences when the previous users of the library upgrade to the new version. In this case, the code might not compile.

So basically, if it is not hidden (aka part of the public API) then the author should not make any changes to the public API that would make previous versions of the library to not work.

This comes up all the time and there are ways around it. For example, when log4j upgraded to version 2, their changes were so dramatic that their API had to break and hence created a whole new library called log4j 2. So its a completely different library with different package names. It is not a new version of the old library, its a new library with similar name.

As a contrast to that, take a look at the Java SE's HashTable class. This is old class that should not be used anymore. But Java has a strict rule that the old public API of previous versions must still be supported in new versions. So Java cannot do what log4j did.

Another example is Spring. Spring tries to follow the approach that Java did, in that you only need to update the version and your old code should work. But Spring does deprecate parts of its public API. It will remove old classes and methods that are public but that it really does not want people to use anymore. In this case, me as a user of Spring might find it hard to upgrade from version 1 to version 4, for example. Mainly because some of the public API might have been deprecated.

So here I have given three different ways library writers have tackled this situation.

1) Side steps the old version and create a whole new library. E.g. Log4j. 2) Be strict and always support old public AIP's. E.g. Java.
3) Slowly deprecate old public API methods or classes. E.g. Spring.

Me as a user of these libraries would prefer that old public API be supported. I have used all three of those examples. I have had to work with old HashTable classes on legacy code I was upgrading to Java 8 and was happy that it was still supported. I have felt the pain of upgrading from log4j 1 to log4j 2. I have also upgraded Spring on a complicated project and have had adverse affects that needed to be troubleshooted. I can tell you that being strict to your old public API's is easiest on the users of said library.

Jose Martinez
  • 11,452
  • 7
  • 53
  • 68
  • I think I got it in some level but not completely yet. So, the client can know how a library is implemented then. But how could the client rely on code if it was not hidden, for example? – Eduardo M Sep 10 '17 at 03:02
0

A simple example could be if a log method was used for logging errors. Maybe first all it did was print to the console. Then later you wanted to write errors to a file:

public void log(String message)
{
     // write to file
}

Then later you decided to write to a database or make a remote call:

public void log(String message)
{
     // make network call
}

As long as the implementation is hidden, clients can continue calling log() and nothing will break because you did not change the method signature.

You can also have multiple implementations of this method by extracting an interface, as shown by @tima.

Frank Modica
  • 10,238
  • 3
  • 23
  • 39
  • What I don't understand is how a client could be affected if he knew the implementation. What exactly he could do in his code which would broke if library programmer change something. – Eduardo M Sep 10 '17 at 02:48
  • If you didn't hide the implementation behind an abstraction, the client would be calling `System.out.println(message)` directly. Now you can't change how printing occurs, and you can't react to it occurring. – Frank Modica Sep 10 '17 at 02:50
  • Sorry but I can't understand why would the client call `System.out.println(message)` directly instead `print(message)` – Eduardo M Sep 10 '17 at 02:57
  • Right, now that you have provided a `print` method they don't have to call `System.out.println(message)`. Their code can just call `print` without worrying what's happening under the hood, because the implementation is effectively "hidden". By "hidden" we don't mean the client can't go see what's really happening in the method call. It just means the client only has to know that the `print` method takes a string, and in the future their code won't break if you change what you do with that string. – Frank Modica Sep 10 '17 at 03:01
  • If for example your method was initially called `printToConsole` instead of `print`, clients would have `printToConsole(...)` everywhere in their code. Later if you decide you want to print to a file instead, if you change the method name to `printToFile` the client's code no longer compiles. So by "hiding" the logic behind a generic method like `print` from the start, you can change the implementation and the client's code never breaks, because their code never made assumptions about the implementation in the first place. – Frank Modica Sep 10 '17 at 03:12
  • Hmm! I thought when the library programmer change something in his library it would 'refactor' in client's code too. But if I got this right, when the implementation is shown, the code will keep that old implementation even if library programmer changes something, right? – Eduardo M Sep 10 '17 at 03:25
  • If you change the code inside the method, but not the method's name or parameters (i.e. its "signature"), the client gets the updates if they have your updated library with the updated method. So their code still compiles, but under the hood your library is doing something different now. But if you had changed the method's signature the client's code would break. – Frank Modica Sep 10 '17 at 03:31
  • Don't get too caught up on the word "hidden". It doesn't mean the client can't see the code or know what it's doing, if they want to find out. It just means that they can write their own code as though they didn't know what your code is really doing. Now you are free to change what your code is doing over time, as long as the method signatures don't change. – Frank Modica Sep 10 '17 at 03:34
  • I feel like it's something very simple and I'm complicating this so hard. I couldn't get this yet. Can't get how they writing the code as though they know what the code is doing would broke their code. I think this discussion is taking too long for such an easy concept. I'll search more for pratical examples on Google in hope to understand it. Thanks for the patience. – Eduardo M Sep 10 '17 at 03:50
  • I'm probably not explaining well. Here's a real world analogy before I go. Your phone has a keypad. That is the generic interface - you don't know how the keypad is implemented. You just touch the numbers to make a call. Later a new version of the phone comes out which operates completely different under the hood. Maybe the phone used to be a land line, but now it's a cell phone. But to you, the client, it doesn't matter, because the same interface with a keypad is there. The "hidden" implementation has been swapped, but you have no trouble because the part you interacted with stayed the same. – Frank Modica Sep 10 '17 at 04:10
0

Say for example, you're working with a LinkedList of names, and you make a utility method to print all the names in the list:

public void print(LinkedList<String> names)
{
    for (String name : names)
        System.out.println(name);
}

This works, but it limits you to only using a LinkedList. What if for some reason, some other code that you have runs slowly with a LinkedList, so you change it to an ArrayList? Well, now you have to go and change this method too.

public void print(ArrayList<String> names)
{
    for (String name : names)
        System.out.println(name);
}

But within this method, it doesn't matter if it is a LinkedList or an ArrayList, as long as you can iterate over it. This is where you want to hide the implementation details from your code, by using an interface:

public void print(Iterable<String> names)
{
    for (String name : names)
        System.out.println(name);
}

Now, you don't even have to pass in a List - you could pass in a HashSet, and it would still work. So, from your original qoute:

A primary consideration in object-oriented design is separating the things that change from the things that stay the same

The things that change, in this case, would be how you iterate over the collection, represented by the ArrayList and LinkedList implementations.

The thing that stays the same is the fact that you can iterate over the collection, which is what the Iterable interface represents.

Andrew Williamson
  • 8,299
  • 3
  • 34
  • 62
  • While what you say is true, valid and important, I think that it is unrelated to the question. (But maybe I'm wrong.) – Marco13 Sep 10 '17 at 02:48
0

There are many examples in the real world for this. For instance, if you consider buying a grocery previously v/s buying a grocery now. Or building a house previously v/s building a house now.

As time goes on the implementation changes of anything, but still the end result is same like bought grocery or built house. So, the library must have methods like buyGrocery() or buildHouse(), the implementation of which keeps on changing, but the user of the library still calls the same methods to attain the end results. Hope this answered your query.

Cheers!

Iqbal S
  • 1,156
  • 10
  • 16