Musings on Encapsulation vs. (De)composition

The standard dogma of OO-design is, that you should certainly not allow access to the internals of an object to the outside world. Instead you are to expose operations on that type. Generally there seems to be a consent in my current environment that getters and setters are evil, as they do indeed expose internal state. However everyone is using them. So what is going on?

Recently functional programming got a lot of interest (rightfully so), e.g: F#, Scala and a modest come back of Haskell (Curry on!). While in OO we always strive to hide information (which might be a bit of a misunderstanding anyways), while in Haskell it is quite natural to decompose a bit of data and to define functions in terms of functions of the components.

Scala for example provides two alternatives for defining operations on datatypes. There is traditional polymorphism and case classes that allow pattern matching style operations. Odersky sees both approaches as complementary argueing that the traditional OO solution makes it easy to add a new type, by just implementing operations, while pattern matching allows for easy addition of new operations on given types.

My current thinking is that there is certain areas, essentially whenever we externalize state of objects (e.g.: persistence, serialisation, or UI), decomposing an object into it’s components and have generic operations performed on those seems to be quite beneficial. I am not sure, how to allow for such legitimate uses and to prevent all kinds of logic to access the state. Getters and setters are alright, if you use them responsibly. However one could also imagine a more generic access mechanism, perhaps passing in a callback, that takes the components, of the original object as parameters.


Posted

in

by

Tags:

Comments

One response to “Musings on Encapsulation vs. (De)composition”

  1. James Iry Avatar

    Functional programmers make a distinction between state and representation that OO programmers generally don’t. Representation is the internal details of how something is built. State, on the other hand, is that which makes something respond differently over time. By that definition instances of java.lang.String have no state but certainly have hidden representation. Also, by that definition, an mutable object can’t hide state so much as it can carefully control the valid state transitions. That’s why your friends are opposed to simple getter/setter pairs – they make any state transition a valid one whether it should be or not.

    The reason I make that distinction is so I can make a distinction about what case classes usually expose. The normal use of case classes is as immutable classes, so what they tend to expose is representation rather than state. So, unlike getters and setters on mutable objects, there’s no concern about putting an object in an inconsistent state just because details have been exposed. You can make case classes mutable, it’s just not done as often.

    Still, exposing details of representation has downsides for modularity even if you aren’t opening up illegal state manipulation. F#, Scala, and GHC Haskell v 6.10 all have a way to separate pattern matching decomposition from the internal details of representation. F# calls it “active patterns”, Scala calls it “extractors”, and GHC Haskell calls it “views.” There are differences in the mechanisms but all are geared towards the same goal: separate decomposition from internal details that are nobody’s business.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.