views:

1240

answers:

11

Object-oriented design (OOD) combines data and its methods. This, as far as I can see, achieves two great things: it provides encapsulation (so I don't care what data there is, only how I get values I want) and semantics (it relates the data together with names, and its methods consistently use the data as originally intended).

So where does OOD's strength lie? In constrast, functional programming attributes the richness to the verbs rather than the nouns, and so both encapsulation and semantics are provided by the methods rather than the data structures.

I work with a system that is on the functional end of the spectrum, and continually long for the semantics and encapsulation of OO. But I can see that OO's encapsulation can be a barrier to flexible extension of an object. So at the moment, I can see the semantics as a greater strength.

Or is encapsulation the key to all worthwhile code?

Edit: I mean specifically the type of encapsulation OO provides here. changeColor(door,blue) becomes door.changeColor(blue).

+9  A: 

Encapsulation and the resulting abstraction are clearly the main strengths of OO. The "things" predicate what "actions" can be invoked on them, so nouns take on a higher semantic importance than verbs.

Ultimately, it's hard to envision designing a complex system in a consistent and maintainable form without some level of encapsulation.

Jen
Absolutely. The point here is the type of encapsulation (data with algorithm/code) that OO provides. Procedural: changeColor(door,blue); OO: door.changeColor(blue).
Phil H
+4  A: 

Isolating Complexity is IMO the main goal of any design: encapsulating functionality behind an interface that is simpler to use than the functionality itself.

OO provides various mechanisms for that - the two oyu mention:

Encapsulation allows to design a custom surface that is independent of the actual implementation. (to paraphrase, "Simpler means different").

Semantics allows to model entities that represent elements of the problem domain, so they are easier to understand.


Any project reaching a certain size becomes an exercise in managing complexity. I'd wager a claim that over the years, programming has skimmed along the limits of complexity we#ve learned to manage.

I haven't dabbled in functional programming for years, but in my understanding it can best be described by a mathematician's meaning of the words powerful, elgant, amd beautiful. "Beautiful" and "elegant", in this context, try to describe a brilliant insight into the true or the relevant structure of a complex system, looking at it from a point of view where it is surprisingly simple. it accepts the complexity as a given, and tries to navigate it.

The flexibility you mention is in my understanding the ability to change the POV according to your needs - but that runs contrary to encapsulation: what is a pointless detail from one position may be the only relevant in another.

OO, OTOH, is the reductionists approach: we change POV by going to a higher level. In "old OO", there is an a single hierarchy of POVs, interfaces are - in this model - a way to model different POVs.

If I may say so, the strength of OO is being better suited to "normal people".

peterchen
+4  A: 

Some form of modularity is the key to any scalable design. The limitations of human beings prevents people from "grokking" too much information at once, so we have to subdivide problems into manageable, cohesive chunks, both to provide a basis for understanding a large project, as well as a way to subdivide the work assignments of a large project among many people.

How to choose the most effective "division"/"partitioning" of a large project to meet the goals above? Experience has shown that OO is the big winner here, and I think many people would agree that two key attributes of OO that make it good at this are:

  • Encapsulated: each class encapsulates a "secret" - a set of implementation-specific assumptions-that-are-likely-to-have-to-change-over-time about its implementation - and exposes an interface that is agnostic to these assumptions; layering such encapsulated abstractions makes it possible to architect a robust design where components/implementations can easily be swapped in the face of anticipated changes.
  • Noun-centric: in most domains, humans seem better at first decomposing a domain model by thinking about the nouns/data of a domain, followed by identifying the supportive verbs that are associated with each noun.

Regarding functional programming (FP) versus OO, I have blogged about this, but briefly I think that FP is more about implementation techniques whereas OO is more about program structure and design, and thus the two are complementary, with OO being more dominant on the 'large' end of the scale and FP being more dominant on the 'small' end. That is, on a large project, the high-level structure is best described by the OO class design, but many of the module-level details (implementations, and details of the shapes of the interfaces of the modules) are best shaped by FP influences.

Brian
Interesting, although I'm interested in your definition of large and small here - if I have a large number of things, I will probably use a functional approach, as the algorithm will need to perform at scale. If instead I have few, large items, then OO tends to be predominant as the differences between objects are too large for overarching functional approaches to be worthwhile. Viz the question, your answer seems to be encapsulation, not semantics.
Phil H
+6  A: 

As a Lisp programmer, whose object system arguably provides neither of these, I say: none of the above.

jwz: "the pseudo-Smalltalk object model loses and that generic functions (suitably constrained by the no-external-overrides rule) win".

I think the desirable attributes which you and others list here (encapsulation, modularity, etc.) are not as inherent in OO as you think. They're often provided alongside Java-style OO, but not purely the consequence of it.

Ken
could you expand on this answer? I think there are very interesting observations in there
UncleZeiv
OO is the only paradigm to wrap data and algorithm up in one package. Procedural code (including functional) requires passing data structures to subroutines. OO makes the algorithm part of the structure, thus both hiding the underlying data structure and implementation details.
Phil H
+24  A: 

You appear to be using a rather narrow definition of, ”Encapsulation.” Would I be right in presuming that you define encapsulation to be, “Combining data with methods?”

If I’m wrong then please ignore the rest of this post.

Encapsulation is not a loose term; in fact, it’s defined by the International Organisation for Standardization. The ISO’s Reference Model of Open Distributed Processing - defines the following five concepts:

Entity: Any concrete or abstract thing of interest.

Object: A model of an entity. An object is characterised by its behaviour and, dually, by its state.

Behaviour (of an object): A collection of actions with a set of constraints on when they may occur.

Interface: An abstraction of the behaviour of an object that consists of a subset of the interactions of that object together with a set of constraints on when they may occur.

Encapsulation: the property that the information contained in an object is accessible only through interactions at the interfaces supported by the object.

We can further make a self-evident proposal: as some information is accessible via these interfaces, some information must be hidden and inaccessible within the object. The property such information exhibits is called information hiding, which Parnas defined by arguing that modules should be designed to hide both difficult decisions and decisions that are likely to change, see one of the great computing papers:

http://www.cs.umd.edu/class/spring2003/cmsc838p/Design/criteria.pdf

It’s important to note that it is not only the data that is information-hidden: it is some subset of behaviour associated with the object that is difficult or likely to change.

In your post, you seem to be saying that the difference between encapsulation in OO and in functional programming stems from data management, but at least according to the ISO and Parnas, data management is not the key to encapsulation. So I don’t see why encapsulation in functional programming need be any different from that in OO.

You mention, furthermore, in your post that functional programming provides encapsulation, “… by the methods rather than the data structures.” This, I think, is a difference of scale rather than of absolute. If I use the word, “Object,” rather than, “Data structure,” (again, please let me know if I’m misinterpreting), then you seem to find significance in OO’s encapsulation by object and functional programming’s encapsulation by method.

Yet by the ISO definition above, an object is anything I wish to model. Thus classes may be encapsulated within a package, so long as some of those classes contribute to the package’s interface (i.e., the public classes of the package) and some are information-hidden (the private classes in the package).

By the same token, methods are encapsulated within a class – some methods being public and some private. You can even take this a notch lower and say that McCabian sequential sequences of code are encapsulated within methods. Each forms a graph of nodes encapsulated within encapsulated regions; and all these graphs form a graph stack. Thus functional programming may well encapsulated at the function/file level, but this is no different from the method/class graph of OO, and essentially no difference from the class/package graph of OO either.

Also, note that word Parnas uses above: change. Information hiding concerns potential events, such as the changing of difficult design decisions in the future. You ask where OO’s strength’s lie; well, encapsulation is certainly a strength of OO, but the question then becomes, “Where does encapsulation’s strength lie?” and the answer is one of resounding clarity: change management. Particularly, encapsulation reduces the maximum potential burden of change.

The concept of, “Potential coupling,” is useful here.

“Coupling,” itself is defined as, “A measure of the strength of association established by a connection from one module to another,” in another of computing’s great papers:

http://www.research.ibm.com/journal/sj/382/stevens.pdf

And as the paper says, in words never since bettered, “Minimizing connections between modules also minimizes the paths along which changes and errors propagate into other parts of the system, thus eliminating disastrous, “Ripple,” effects, where changes in one part cause errors in another, necessitating additional changes elsewhere, giving rise to new errors, etc.”

As defined here, however, there are two limitations which can easily be lifted. Firstly, coupling does not measure intra-module connections, and these intra-module connections can give rise to just as many, “Ripple,” effects as inter-module connections (the paper does define, “Cohesion,” to relate intra-module elements, but this is not defined in terms of connections between elements (i.e., references to labels or addresses) with which coupling was defined). Secondly, the coupling of any computer program is a given, in that modules are connected or; there is little scope within the definition of coupling to manage the potential changes of which Parnas speaks.

Both these issues are resolved, to some degree, with the concept of potential coupling: the maximum possible number of connections formable among all elements of a program. In Java, for example, a class that is package-private (the default accessor) within a package cannot have connections formed on it (i.e., no outside classes can depend on it, reflection notwithstanding), but a public class within a package can have dependencies on it. This public class would contribute to the potential coupling even if no other classes depend on it at the moment – classes might depend on it in future, when the design changes.

To see encapsulation’s strength, consider the Principle of Burden. The Principle of Burden takes two forms.

The strong form states that the burden of transforming a collection of entities is a function of the number of entities transformed. The weak form states that the maximum potential burden of transforming a collection of entities is a function of the maximum potential number of entities transformed.

The burden of creating or modifying any software system is a function of the number of classes created or modified (here we use, “Classes,” presuming an OO system, and are concerned with encapsulation at the class/package level; we could equally have concerned ourselves with the function/file level of functional programming). (Note that the, “Burden,” is modern software development is usually cost, or time, or both.) Classes that depend on a particular, modified class have a higher probability of being impacted than classes that do not depend on the modified class.

The maximum potential burden a modified class can impose is the impacting of all classes that depend on it.

Reducing the dependencies on a modified class therefore reduces the probability that its update will impact other classes and so reduces the maximum potential burden that that class can impose. (This is little more than a re-statement of the, “Structured design,” paper.)

Reducing the maximum potential number of dependencies between all classes in a system therefore reduces the probability that an impact to a particular class will cause updates to other classes, and thus reduces the maximum potential burden of all updates.

Encapsulation, by reducing the maximum potential number of dependencies between all classes, therefore mitigates the weak form of the Principle of Burden. This is all covered by, “Encapsulation theory,” which attempts to mathematically prove such assertions, using potential coupling as the logical means of structuring a program.

Note, however, that when you ask, “Is encapsulation the key to all worthwhile code?” the answer must surely be: no. There is no single key to all worthwhile code. Encapsulation is, in certain circumstances, merely a tool to help improve the quality of code so that it may become, “Worthwhile.”

You also write that, “ … encapsulation can be a barrier to flexible extension of an object.” Yes it most certainly can: it is indeed designed to be a barrier against extending the design decisions of an object that are difficult or likely to change. This is not, however, thought to be a bad thing. An alternative approach would be to have all classes public and have a program express its maximum potential coupling; but then the weak form of the Principle of Burden states that updates will become increasingly costly; these are the costs against which barriers to extension are to be measured.

Finally, you make the interesting comparison between encapsulation and semantics, and that, in your opinion, OO’s semantics are its greater strength. I’m no semanticist either (I didn’t even know such a word existed before the good Mister Ramsey alluded to it in his comment) but I presume you mean, “Semantics,” in the sense of, “the meaning, or an interpretation of the meaning, of a word,” and very basically that a class with a, woof() method should be called a Dog.

There is great strength indeed in this semantics.

What is curious to me is that you pit semantics against encapsulation and look for a winner; I doubt you’ll find one.

In my opinion, there are two forces that motivate encapsulation: the semantic and the logical.

Semantic encapsulation merely means encapsulation based on the meaning of the nodes (to use the general term) encapsulated. So if I tell you that I have two packages, one called, 'animal,' and one called 'mineral,' and then give you three classes Dog, Cat and Goat and ask into which packages these classes should be encapsulated, then, given no other information, you would be perfectly right to claim that the semantics of the system would suggest that the three classes be encapsulated within the, 'animal,' package, rather than the, 'mineral.'

The other motivation for encapsulation, however, is logic, and particularly the study of potential coupling, mentioned above. Encapsulation theory actually provides equations for the number of packages that should be used to encapsulate a number of classes in order to minimise the potential coupling.

For me, encapsulation as a whole is the trade-off between this semantic and logical approach: I’ll allow the potential coupling of my programs to rise above the minimum if this makes the program semantically easier to understand; but enormous and wasteful levels of potential coupling will be a warning that my program needs to be re-structured no matter how semantically obvious it is.

(And if the good Mister Ramsey is still reading, could you or your semanticist friends give me a better word for the, “Semantics,” phase I’m using here? It would be good to use a more appropriate term.)

Regards, Ed.

Ed Kirwan
Excellent response! To answer your first query, I did mean encapsulation in terms of hiding complexity, and reducing coupling. Perhaps the difference is that OO encapsulates both semantics and functional complexity, while functional programming only encapsulates the latter.
Phil H
I have recently been re-reading Parnas (which influenced my answer), and it is indeed good stuff.
Brian
That post was very long.
Tim Matthews
+1: Good reply. -1: Way too long. It evens out ;-)
Adrian Grigore
Great response.. throughly enjoyed reading it
Surya
Ed, you're talking about 'natural-language semantics' (meanings of words) and you want names in the program to be consistent with it. All good. My crowd uses 'semantics' purely to talk about how programs work (e.g., what's supposed to happen when you run one).
Norman Ramsey
+3  A: 

Object-oriented design's strength is proportional to how much late binding occurs in the design. This is the Kay notion of OO, not the Nygaard notion. Alan Kay wrote:

OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP. There are possibly other systems in which this is possible, but I'm not aware of them.

Much of the literature ignores late binding in favor of the C++ idea of object orientation.

Steven Huwig
Indeed. Python, javascript et al manage arguably not to bind at all.
Phil H
+2  A: 

Let's take a step back and look at this from a higher level. The advantages of any language feature lie in the ability to succinctly express the problem/solution in a more natural way with respect to the problem domain.

The mechanics of OOP are easily implemented in plain C with structs and function pointers. You can even get a bit of OOP feel doing it that way. However, OOP idioms are not nearly as forthcoming in such an environment. When there's actual language support for OOP then the expressiveness of the paradigm comes out, and the way a language implements an idea has a very real impact on what is "said" and how. For example, see differences in code using closures/lambdas in lisp, python, ruby, etc.

So in the end it's not about the components and underlying concepts, but rather how they're put together and used that make OO in C++ what it is.

dwc
+1  A: 

Encapsulation in conjunction with Polymorphism. The ability of classes in most OOP languages to implement one or more interfaces has had the biggest impact on the development of my software. This feature allows me to precisely define the interaction between two object.

Not only define the interactions but document it so that years later I can return to that section of code and see what is happening clearly.

This feature is the main reason why I prefer using OOP languages over functional languages. While very powerful I have found software written in functional languages to be a pain to maintain when the maintenance cycle is measured in decades. (AutoLisp software found in AutoCAD)

RS Conley
A: 

Your question reads like You want to derive the benefits of a house by analyzing a brick.

Having the ability to provide semantic context and encapsulation are just the basic capabilities of a class in OO. (Like a brick can withstand a certain force and claim a certain space.)

To continue the analogy: To get the maximum out of bricks, just put them together. The very same applies to classes and objects.

There are a lot of design patterns that can be used for OO programming. Most of them rely on the abilities "encapsulation" and "semantic", that You mentioned.

Some of those patterns are even an answer to the third paragraph of Your question:

  • If You want to extend the behaviour of an existing class, You can create a derived class.
  • If You want to extend or change the behaviour of an existing object, You might consider the decorator pattern.
Black
(Why do you capitalise You?) Well, given the analogy, is the value of a brick house in encapsulation (security, privacy) or weather protection? Arguably both, but in many countries weather protection is far less important. The construction of the house can determine the social constructs of the society of its inhabitants. Most houses are not made of bricks. Why?
Phil H
+1  A: 

IMHO, OO simply means objects interacting with other objects. Encapsulation simply means abstracting a concept. So, you create a Socket and .Connect() to something. How it connects, you don't really care (which is basically my definition of encapsulation).

And, pure functional programming can use object to communicate.. but those objects need to be immutable. So, again IMHO, FP can easily use OO concept; Imperative language such as C can still use the concept of OO.. for instance, a file for each "Class" with a private section that shouldn't be used.

A: 

The real power of OO lies in polymorphism rather than encapsulation. Encapsulation, to certain extent, is achievable and is used in functional languages, but polymorphism would be very awkward if implemented in functional language.

(Read "design pattern" by gang of four to understand the power of OO.)

@Phil, The difference you mentioned, if I understand you correctly, is between the way the program invokes data/method: in oo, there first is an object/instance, and then the data/method of the object is invoked through the object; in functional, method is directly invoked.

However, looking at the implementation of a functional program, we see that the data and the method are wrapped (in a file but not in a class). For example, a C program has the header file that declares the functions accessible by other file, and the data is a private data if is only accessible through these declared functions. As long as a programmer is careful enough, most of the encapsulation in OO can be implemented in functional programs. (Even inheritance is available by using certain pointer tricks.)

Paul
But the encapsulation that OO gives is to wrap the data and methods up together, which is the key change from previous approaches. Functional systems consider the data and transforms to be separate.
Phil H