3

As I am learning OOP principles, I know that it is always good practice to hide the inner workings of classes so that the end user can't access or break them. I understand why this is important. The human mind cannot comprehend overly complex code. I also hear the term "Black box" quite often. A common example I hear is a remote control. It makes sense to me why end users don't need to understand how exactly the remote works.

However, when it comes to learning programming, explaining how things work "under the hood" happens all the time. In Java for example, it is important to know the difference between a LinkedList and an ArrayList even though they do essentially the same thing. Understanding how programming languages work in general would be another example. So my question is wouldn't this violate the encapsulation and abstraction principles?

9
  • 2
    Experience is knowing what level of abstraction is appropriate for the situation at hand. The same person will sometimes ignore the details and at other times need to go beyond the abstraction. Jul 10, 2022 at 0:19
  • 1
  • 1
    please don't cross-post: stackoverflow.com/questions/72925028/… "Cross-posting is frowned upon as it leads to fragmented answers splattered all over the network..."
    – gnat
    Jul 10, 2022 at 5:24
  • 1
    Who is "we"? Because I sure don't. Jul 10, 2022 at 9:44
  • 1
    You don't necessarily need to know how LinkedList and ArrayList are implemented, but you do need to know their different performance characteristics, because that is part of observable behavior and not abstracted away. It is just a lot easier to remember by broadly understanding the implementation.
    – JacquesB
    Jul 10, 2022 at 14:24

4 Answers 4

3

To use the LinkedList and ArrayList example, a discussion about how these classes work "under the hood" happens when deciding if your current use case would benefit more from one kind of list over the other.

For example, if needing to optimize for insertion or deletion speed, a LinkedList would be appropriate. If iteration speed is of greater importance, then ArrayList would be better. This is a bigger architectural discussion about the code with respect to a particular use case in the application.

After deciding which kind of list is best to use, the focus of the conversation and design switches to abstractions. Say you decided to use a LinkedList. After initializing the LinkedList, you pass that object to another method that adds or removes items from that list. Methods to add and remove items are certainly defined in the LinkedList class, but more importantly, they are defined in the List interface — an interface that LinkedList implements. A higher level of abstraction is available. Using the LinkedList through the List interface means any code following the initialization of the LinkedList no longer cares about the implementation details of the list. This makes code easier to refactor later.

As curiousdanni commented, you must choose the right level of abstraction. The level of abstraction can change from one line of code to the next. Implementation details tend to be more important during initialization of an object than when using that object for its intended purpose.

Discussions involving implementation details and abstractions are both important. One kind of discussion does not negate the need for the other. Instead, both concrete and abstract discussions are necessary, depending on the use case. The challenge is deciding at which point your code should be tightly coupled to implementation details, and when it should be decoupled by using an abstraction.

None of this breaks encapsulation because knowing how something works "under the hood" doesn't mean you can open the hood and change whatever you want. A properly encapsulated class will not allow you to change its internal state directly.

Coming back to the LinkedList example, just because you know a LinkedList is a doubly-linked list data structure does not mean your code can arbitrarily change pointers to the next or previous items in the collection. You are forced to use the add or remove methods to do that. Encapsulation is preserved, even if the class name communicates some implementation details.

1

When driving a vehicle, it is for the most part irrelevant whether the engine is an internal combustion engine (and if so whether it is natural gas, diesel or petrol), a steam engine, an electric motor, or something else. You have the steering wheel, break and gas pedals and other controls, and that’s enough to get it moving.

When you are racing, knowing exactly how the engine you are using works can be the difference between a win and a loss, but isn’t really that important when driving the vehicle to the starting line.

If you are designing or repairing the vehicle, knowing which you are working on can be essentially. You don’t want to spend a lot of design effort on where to put the gas tank in an all electric vehicle, and you are never going fix it by replacing the spark plugs.

So, the answer to whether you need to know how it works under the hood is “it depends”. The OOP approach to programming is to say “probably not” and then provide tooling to help make that true.

As for programmers knowing how things work under the hood violating encapsulation…not really, for starters for the vast majority of programmers in any given situation the “under the hood” knowledge is rather generic, to say the least. Take for instance the garbage collector in Java,.net, JavaScript, and Lisp as well as other languages/systems — most programmers will (hopefully) have some idea of what is happening under the hood, and that can help them make their programs faster and more robust, but that’s a far cry from being ready or able to dive down and fix a memory leak in the garbage collector. The exact details are both implementation details that are subject to change AND not relevant to 99.99 percent of what they do everyday. What would violate it, is if you required someone to master the garbage collector before letting them write hello world.

Or to put it another way, any under the hood knowledge should be coincidental and irrelevant in everyday OOP. If I write two classes, one using the other, then I can’t help but know the details of both, but a good implementation and a good design won’t make that necessary in order to get things done.

0

When learning programming, you might need to look at how things work under the hood to get an understanding of some concepts that would be too abstract otherwise. A typical example is Java's object handle (reference), which allows to better understand how objects are used, and why a mutation done somewhere might impact elsewhere.

When chosing library classes, we should in principle not worry about the implementation, but only about the suitability of the class for our intended usage. In this regard, your example highlights practices in public API documentation related to human communication, rather than issues with encapsulation.

Indeed ArrayList vs LinkedList is an example of documentation where the internals are very developed. You could easily imagine an alternative that only provides the minimal elements needed to make decisions: "The size, isEmpty, get, set, iterator, and listIterator operations run in constant time. The add operation runs in amortized constant time, that is, adding n elements requires O(n) time. All of the other operations run in linear time (roughly speaking)" and that it's "not synchronized" when working with threads.

On the other side, shedding some light on the internals make developers feel comfortable, and helps to intuitively understand why the things are as they are without having to read all the source code. Openness helps to build trust.

0

Worth noting that when using LinkedList or ArrayList, it's not the internals that we're worried about. The outside specification of Collection/List leave some room for implementations. So those classes actually modify / complete / constrain the specification for some methods even further. Sure, the insides are different, but the outside specification is actually different and that is what we (usually) care about.

If I were to implement a List using some technique that would give you the same performance characteristics as LinkedList, would you care how it works internally?

Aside from that why do we care? Well, professional curiosity I guess.

Other than that, I would say that if you have to look at the implementation of some method you're not directly working on, it's definitely a smell.

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.