211

This is going to be a very non-technical, soft question and I am not sure if this is the right platform. But I am a beginning CS student so I hope you guys tolerate it.

In the first semester we were introduced to OOP concepts like encapsulation, data hiding, modularity, inheritance and so on through Java and UML. (Java is my first programming language)

The way I understand it, OOP is a way of managing software complexity. But its principles are not new or unique, they are in a sense universal to all engineering fields.

For example a car is a very complex structure whose complexity is managed by a hierarchy of modular and encapsulated components with well-defined behaviors and interfaces.

But I do not understand the reason behind introducing a new programming paradigm. I think all the principles used for managing complexity can be realized by procedural programming languages. For example, for modularity we can just divide the program into many small programs that perform well-defined tasks whose code is contained in separate files. These programs would interact with each other through their well-defined input and output. The files may be protected (encrypted?) to achieve encapsulation. For code re-use we can just call those files whenever they are needed in new programs. Doesn't this capture all what OOP is or am I missing something very obvious?

I am not asking for a proof that OOP manages complexity. In my opinion it certainly does. But I think all the principles used to manage complexity like modularity, encapsulation, data hiding and so on can be very easily implemented by procedural languages. So why really OOP if we can manage complexity without it?

  • 41
    You are missing the separation of interface and implementation. Being able to swap one implementation for another at runtime is a very important feature. Basically, this is achieved by the dynamic method dispatching of OO languages with inheritance. Procedural languages can kind-of do that too (read: void pointers), but without type safety. – marstato Mar 20 '17 at 10:23
  • 81
    To a large extent, the idea of Object-Oriented languages and design is precisely to make those universal, intuitive concepts as easy as possible to represent and recreate in code. If you had a scheme or a set of guidelines for how to achieve all those things without an inherently Object-Oriented language, then your proposals on how to do things would effectively be the Object-Oriented methodology used. Actual OO languages are just a way to formalize and simplify that. – Standback Mar 20 '17 at 12:08
  • 14
    @RobbieDee have you actually read my question ? It is about trying to understand OO on a more fundamental level by questioning weather the software complexity can be managed without OO. I am not trying to undermine OO, not trying to invent something new, I am just trying to understand it better and if it the question is so 'self-evident' why did it receive the excellent answer from Jorg? – steakexchange Mar 20 '17 at 13:10
  • 12
    Encrypting a file is not encapsulation. You may have obscured seeing the contents of the code from another developer, but you have not necessarily protected the inner workings of the code from other code. The original author could do this before or after encryption if he can recall how. – JeffO Mar 20 '17 at 18:59
  • 8
    You don't need anything beyond machine language -- let the programmer memorize the opcodes and write the ones and zeros himself. But having some sort of "symbolic" language is very useful in terms of reducing errors and increasing productivity, and, as Dijkstra observed, a language which imposes some "structure" (or at least makes maintaining "structure" easier) helps considerably. OO languages may not be the ideal technology, given the current level of language sophistication, but they're pretty good for many applications. The idea is to manage complexity without getting in your way. – Daniel R Hicks Mar 21 '17 at 12:36

16 Answers 16

176

Let me try with a really low theory answer :)

What you are really asking is: Why include support for Object Orientation (OO) directly in the language when procedural languages can be used to design and write OO code?

And the answer is: To have a standard for how OO is expressed in the source code so you don't end up with 22 different implementations for the same abstraction.

For example, lets say I create a MagicButton and a MagicSlider which can be used in an user interface system. I need a way to group the methods which can be used with the MagicButton, the methods which can only be used with the MagicSlider, and the methods which can be used by both. These objects share some methods because they are both Magic gui objects.

I can do the grouping by naming functions in a special way MagicSlider_DoSomething ..., by including the methods in specific files named in a special way MagicSliderMethods.XXX, or I could find some other special way to do the same thing. If there is no standard way in the language to do it I will do it different from you, and differently from anyone else. This makes sharing code much more difficult.

Yes, late dispatch – virtual methods in OO languages – can be implemented in procedural languages, but there are so many different ways to implement it. Depending on who wrote the code you will end up with different implementations of OO inside the same program.

Think about the poor maintenance developer. This person must manage different object abstractions and different ways to call virtual methods depending on who wrote the original code.

Also: Having the abstractions in the language allows advanced code editors such as Eclipse to do a lot of static analysis on the code. For example Eclipse can offer up a list of all methods which can be used on an object, as well as auto implementation of empty "TODO methods". Eclispe knows exactly which methods your class must implement based on which classes you extend and which interfaces you implement. This would be almost impossible if there were not a language standard to do OO.

  • 40
    Classic example: Lua. It's not natively OO, but can be made to, but this means there's about 5 different equally well-known OO libraries that aren't entirely interoperable. – Kroltan Mar 20 '17 at 16:17
  • 55
    @steakexchange You focus on absolutes too much. Very little has a "sole purpose". Languages all do lots of different things to differing degrees of quality. Choosing a language is choosing the set of trade-offs that make it most fitting for the purpose for which you need it. – Tim B Mar 20 '17 at 17:46
  • 42
    @nocomprende Standardizing abstractions is almost literally what programming languages are for. Even assembly language abstracts over the differences across something like ten hardware generations over a decade and a half. – David Moles Mar 20 '17 at 22:17
  • 56
    @DavidMoles Standardized abstractions are literally what programming languages are. Don't waste a perfect opportunity to use "literally" literally! – Clement Cherlin Mar 20 '17 at 23:01
  • 12
    It is possible to standardise this. When I was at uni back in the mid 90s, I did a fairly substantial amount of work in X-Windows (mostly based on Motif, for those who remember such things). X-Windows did indeed allow you to implement all features of object orientation in regular C. The mental gymnastics to do so were fairly substantial though, and relied heavily on people not looking inside the box (at which point Schroedinger's Widget Code generally ended up dead). OO languages conceal this from coders, in the same way that a regular compiler does for assembler, and life is easier. – Graham Mar 21 '17 at 16:49
210

In the first semester we were introduced to OOP concepts like encapsulation, data hiding, modularity, inheritance and so on through Java and UML. (Java is my first programming language)

None of those are OOP concepts. They all exist outside of OO, independent of OO and many even were invented before OO.

So, if you think that that is what OO is all about, then your conclusion is right: you can do all of those in procedural languages, because they have nothing to do with OO.

For example, one of the seminal papers on Modularity is On the Criteria To Be Used in Decomposing Systems into Modules. There is no mention of OO in there. (It was written in 1972, by then OO was still an obscure niche, despite already being more than a decade old.)

While Data Abstraction is important in OO, it is more a consequence of the primary feature of OO (Messaging) than it is a defining feature. Also, it is very important to remember that there are different kinds of data abstraction. The two most common kinds of data abstraction in use today (if we ignore "no abstraction whatsoever", which is probably still used more than the other two combined), are Abstract Data Types and Objects. So, just by saying "Information Hiding", "Encapsulation", and "Data Abstraction", you have said nothing about OO, since OO is only one form of Data Abstraction, and the two are in fact fundamentally different:

  • With Abstract Data Types, the mechanism for abstraction is the type system; it is the type system that hides the implementation. (The type system need not necessarily be static.) With Objects, the implementation is hidden behind a procedural interface, which doesn't require types. (For example, it can be implemented with closures, as is done in ECMAScript.)
  • With Abstract Data Types, instances of different ADTs are encapsulated from each other, but instances of the same ADT can inspect and access each other's representation and private implementation. Objects are always encapsulated from everything. Only the object itself can inspect its own representation and access its own private implementation. No other object, not even other objects of the same type, other instances of the same class, other objects having the same prototype, clones of the object, or whatever can do that. None.

What this means, by the way, is that in Java, classes are not object-oriented. Two instances of the same class can access each other's representation and private implementation. Therefore, instances of classes are not objects, they are in fact ADT instances. Java interfaces, however, do provide object-oriented data abstraction. So, in other words: only instances of interfaces are objects in Java, instances of classes are not.

Basically, for types, you can only use interfaces. This means parameter types of methods and constructors, return types of methods, types of instance fields, static fields, and local fields, the argument to an instanceof operator or a cast operator, and type arguments for a generic type constructor must always be interfaces. A class may be used only directly after the new operator, nowhere else.

For example, for modularity we can just divide the program into many small programs that perform well-defined tasks whose code is contained in separate files. These programs would interact with each other through their well-defined input and output. The files may be protected (encrypted?) to achieve encapsulation. For code re-use we can just call those files whenever they are needed in new programs. Doesn't this capture all what OOP is or am I missing something very obvious?

What you describe is OO.

That is indeed a good way to think about OO. In fact, that's pretty much exactly what the original inventors of OO had in mind. (Alan Kay went one step further: he envisioned lots of little computers sending messages to each other over the network.) What you call "program" is usually called an "object" and instead of "call" we usually say "send a message".

Object Orientation is all about Messaging (aka dynamic dispatch). The term "Object Oriented" was coined by Dr. Alan Kay, the principal designer of Smalltalk, and he defines it like this:

OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.

Let's break that down:

  • messaging ("virtual method dispatch", if you are not familiar with Smalltalk)
  • state-process should be
    • locally retained
    • protected
    • hidden
  • extreme late-binding of all things

Implementation-wise, messaging is a late-bound procedure call, and if procedure calls are late-bound, then you cannot know at design time what you are going to call, so you cannot make any assumptions about the concrete representation of state. So, really it is about messaging, late-binding is an implementation of messaging and encapsulation is a consequence of it.

He later on clarified that "The big idea is 'messaging'", and regrets having called it "object-oriented" instead of "message-oriented", because the term "object-oriented" puts the focus on the unimportant thing (objects) and distracts from what is really important (messaging):

Just a gentle reminder that I took some pains at the last OOPSLA to try to remind everyone that Smalltalk is not only NOT its syntax or the class library, it is not even about classes. I'm sorry that I long ago coined the term "objects" for this topic because it gets many people to focus on the lesser idea.

The big idea is "messaging" -- that is what the kernal of Smalltalk/Squeak is all about (and it's something that was never quite completed in our Xerox PARC phase). The Japanese have a small word -- ma -- for "that which is in between" -- perhaps the nearest English equivalent is "interstitial". The key in making great and growable systems is much more to design how its modules communicate rather than what their internal properties and behaviors should be. Think of the internet -- to live, it (a) has to allow many different kinds of ideas and realizations that are beyond any single standard and (b) to allow varying degrees of safe interoperability between these ideas.

(Of course, today, most people don't even focus on objects but on classes, which is even more wrong.)

Messaging is fundamental to OO, both as metaphor and as a mechanism.

If you send someone a message, you don't know what they do with it. The only thing you can observe, is their response. You don't know whether they processed the message themselves (i.e. if the object has a method), if they forwarded the message to someone else (delegation / proxying), if they even understood it. That's what encapsulation is all about, that's what OO is all about. You cannot even distinguish a proxy from the real thing, as long as it responds how you expect it to.

A more "modern" term for "messaging" is "dynamic method dispatch" or "virtual method call", but that loses the metaphor and focuses on the mechanism.

So, there are two ways to look at Alan Kay's definition: if you look at it standing on its own, you might observe that messaging is basically a late-bound procedure call and late-binding implies encapsulation, so we can conclude that #1 and #2 are actually redundant, and OO is all about late-binding.

However, he later clarified that the important thing is messaging, and so we can look at it from a different angle: messaging is late-bound. Now, if messaging were the only thing possible, then #3 would trivially be true: if there is only one thing, and that thing is late-bound, then all things are late-bound. And once again, encapsulation follows from messaging.

Similar points are also made in On Understanding Data Abstraction, Revisited by William R. Cook and also his Proposal for Simplified, Modern Definitions of "Object" and "Object Oriented":

Dynamic dispatch of operations is the essential characteristic of objects. It means that the operation to be invoked is a dynamic property of the object itself. Operations cannot be identified statically, and there is no way in general to [know] exactly what operation will executed in response to a given request, except by running it. This is exactly the same as with first-class functions, which are always dynamically dispatched.

In Smalltalk-72, there weren't even any objects! There were only message streams that got parsed, rewritten and rerouted. First came methods (standard ways to parse and reroute the message streams), later came objects (groupings of methods that share some private state). Inheritance came much later, and classes were only introduced as a way to support inheritance. Had Kay's research group already known about prototypes, they probably would have never introduced classes in the first place.

Benjamin Pierce in Types and Programming Languages argues that the defining feature of Object-Orientation is Open Recursion.

So: according to Alan Kay, OO is all about messaging. According to William Cook, OO is all about dynamic method dispatch (which is really the same thing). According to Benjamin Pierce, OO is all about Open Recursion, which basically means that self-references are dynamically resolved (or at least that's a way to think about), or, in other words, messaging.

As you can see, the person who coined the term "OO" has a rather metaphysical view on objects, Cook has a rather pragmatic view, and Pierce a very rigorous mathematical view. But the important thing is: the philosopher, the pragmatist and the theoretician all agree! Messaging is the one pillar of OO. Period.

Note that there is no mention of inheritance here! Inheritance is not essential for OO. In general, most OO languages have some way of implementation re-use but that doesn't necessarily have to be inheritance. It could also be some form of delegation, for example. In fact, The Treaty of Orlando discusses delegation as an alternative to inheritance and how different forms of delegation and inheritance lead to different design points within the design space of object-oiented languages. (Note that actually even in languages that support inheritance, like Java, people are actually taught to avoid it, again indicating that it is not necessary for OO.)

  • 16
    +100 - We put inherent blame on things because they're being used improperly. – JeffO Mar 20 '17 at 11:50
  • 55
    Sorry, but this is so incredibly wrong. Alan Kay may have come up with the term, but the principles were around before Smalltalk. Object-oriented programming derives from Simula, and its OO style had nothing to do with "messages". Pretty much every OO language that's been successful has run on the basic principles set forth in Simula--the same ones we see in Java--and Smalltalk-style OO has been a failure in the "marketplace of ideas" every time it's reintroduced, because it just doesn't work very well at all. The name was the only truly meaningful thing Kay contributed. – Mason Wheeler Mar 20 '17 at 12:57
  • 23
    @steakexchange No. "The essence of OO," the thing that makes it truly distinctive, is objects with virtual methods. There's a reason no one uses Smalltalk: the message-passing system works very poorly at individual-computer scales. Every time some well-intentioned but naive language designer tries to reimplement Smalltalk principles, it ends up failing. (The most recent example would be Objective-C, which no one would have ever used if Steve Jobs hadn't shoved it down the entire iOS community's throats. It's never found any traction outside the Apple ecosystem, and there's a reason for that.) – Mason Wheeler Mar 20 '17 at 13:46
  • 28
    @MasonWheeler Could you maybe elaborate on your comment in an answer since you have quite an opposite view to what Jorg is saying? – steakexchange Mar 20 '17 at 13:54
  • 20
    It's also worth to note that the concept of Object-Oriented languages evolved a lot. Those ancestral concepts may not be as true nowadays with a lot of languages ditching the old models and embracing multi-paradigms. Look at C#, for example - that language mixes up almost everything under the sun at once and, while mostly referred as a OO language, it is actually a mix of different paradigms. That enables it to be a really expressive tool for developers all around. Also, Class-Based OO is one of many equally valid flavors of OO programming. – T. Sar - Reinstate Monica Mar 20 '17 at 14:47
66

But I think all the principles used to manage complexity like modularity, encapsulation, data hiding and so on can be very easily implemented by procedural languages.

When you say, "very easily" you're making a very bold statement. The way I read it is: "I don't see the difficulty, so it must not be very large." When phrased that way, it becomes clear that you're not asking "why do we need OO", you're asking "why aren't the difficulties that other programming paradigms encountered that lead to the invention of OO immediately apparent to me?"

An answer to that question is that many of these difficulties do not exist in the kinds of programs you're working on. You are not being asked to update 40-year old spaghetti code. You are not attempting to write a new display manager for an operating system. You are not debugging multithreaded distributed applications.

For many of the kinds of toy programs we CS students are tasked with writing, we could just as well be writing them in BASIC or assembly as Java or Python. That is because the inherent complexity of the tasks are so low, there is only one developer, there are no legacy interoperability issues, performance doesn't matter, and the code will likely only ever be run a handful of times on one machine.

Imagine taking a student driver and asking them to merge onto a busy street in rush hour, on a manual transmission with no synchromesh, heading up a steep hill. Disaster. Why? They are unable to manage the level of complexity required to simultaneously follow all of the rules that the task requires.

Now imagine the same student, same vehicle, driving at a walking pace in an empty parking lot. They are OK, because their level of skill is adequate to the task. There is no pressure, little risk, and they can take the individual subtasks of starting, clutching, shifting, accelerating, steering one at a time.

That student might ask why we have automatic transmissions, if a skilled driver can do all of these things simultaneously? The answer is that a skilled enough driver doesn't, in optimal conditions, need an automatic. But we're not all professional drivers in peak condition, and we typically want the convenience of having the car's designers take care of all that complexity for us.

A skilled, disciplined enough programmer can indeed create a functioning high-complexity system in C, or assembly. But we're not all Linus Torvalds. Nor should we have to be, to create useful software.

I personally have no interest in having to reinvent all the features of a modern language before I can even address the problem at hand. If I can take advantage of a language that includes solutions to already solved problems, why wouldn't I?

So I will turn your question around, and ask you, if languages provide convenient features like encapsulation, and polymorphism, why shouldn't we use them?

  • 13
    So basically it is possible to do OO with a procedural language but that is doing things manually while using a standardized OO language automates and simplifies. – steakexchange Mar 20 '17 at 16:43
  • 6
    @steakexchange Pretty much exactly that. – Tim B Mar 20 '17 at 17:44
  • 3
    @steakexchange a good historical example of this was the object model for X Windows back in the day. It was written in C but was based on an object-oriented system. So, you had to follow certain conventions to interface with it so your classes would play nice with everyone else's. – Ukko Mar 20 '17 at 21:09
  • 7
    @nocomprende Certainly. But one might render one's computer unbootable by doing raw disk writes instead of relying on the filesystem, and there will be truly difficult to debug problems in an ad-hoc object system built by a novice. Encapsulation, when done correctly, keeps us from intentionally or unintentionally meddling with things we shouldn't. – Clement Cherlin Mar 20 '17 at 22:58
  • 3
    It's interesting to me that the examples you give of applications that would benefit from OO are often not written in OO languages. The 40 year old spaghetti is likely written in C, COBOL, FORTRAN or REXX. The display manager is likely written in C (albeit with OO-ish conventions), and many successful distributed multithreaded systems are written in Erlang, Go or C. – James_pic Mar 21 '17 at 12:18
22

What you are describing is not OOP, it's abstraction. Abstraction is present in all modern models of design, even ones that aren't OOP. And OOP is a very specific kind of abstraction.

First, it's worth noting that there is no single definition of OOP, so there may be people who disagree with what I'm characterizing as OOP.

Secondly, it's important to remember that OOP was inspired by traditional models of design, so the similarities to car design is no coincidence.

However, here's some ways OOP is more nuanced than what you've said:

  • Encapsulation: this isn't just about having a set interface for a module (i.e. abstraction), it's about forbidding access beyond this interface. In Java, accessing a private variable is a compile error, whereas in your car design, you can (in some cases) use things in a way that's different from the intended interface.

  • Inheritance: This is really the thing that makes OOP unique. Once you've defined an interface, you can make multiple things implementing that interface, and you can do this in a heirarchical way, altering specific parts of their implementation, while inheriting all the previous parts, massively reducing code duplication.

    If you think in terms of the encapsulated components of a car, there's not really an equivalent to this. There's not a way for me to make a gear by taking a different gear and changing a specific part of its implementation. (At least I don't think so, I don't know much about cars).

  • Polymorphism: Once you've defined an interface, anything using that interface should be indistinguishable, from the point of view of what operations are available, and you shouldn't need to know what implementation is being used to use an interface. This is where subtyping and the Liskov Substitution Principle become important.

  • Coupling: A key aspect of OOP is that we closely relate things with the same operations, and spread out the different forms that they can have. Data are bundled with operations on that data. This means that it is very easy to add a new form of data (a new implementation), but very difficult to add a new operation to an interface (since you'd have to update each class that implements the interface). This is in contrast to Algebraic Data Types in functional languages, where it's very easy to add a new operation (you just write a function that deals with all the cases), but hard to add a new variant (since you need to add a new case to all your functions).

  • 1
    Good answer! One part I don't agree with: the distinction you're drawing about encapsulation isn't valid. Encapsulation always means "forbidding access beyond this interface" -- that's true for OOP and non-OOP settings alike. So this part isn't really something that's unique to OOP. – D.W. Mar 20 '17 at 16:05
  • @D.W. I've tried to clarify this, saying not that it's unique to OOP but that it's the difference between encapsulation and abstraction. Thanks for the feedback! – jmite Mar 20 '17 at 17:47
  • 2
    OK. But I still have a different view about what is written here on that subject. You wrote that "here's some ways OOP is more nuanced than what you've said", but encapsulation isn't a way that OOP is more nuanced than what was written in the question. Encapsulation is what it is, in any paradigm. And where you wrote that " What you are describing is not OOP, it's abstraction", I thought the original question was trying to describe encapsulation (not just abstraction). I guess I'll just leave this comment as a different perspective. I do think the answer is very helpful! – D.W. Mar 20 '17 at 17:56
  • Inheritance is a common feature, but several important OO languages lack it. – Bradd Szonye Mar 23 '17 at 2:34
  • Good answer, but IMO you're overstating the car example. An engine for a given model has a well-defined interface (cam shaft, mounting bracket "receptacles", etc.). You can replace a plain old carburetor with a fuel-injected one, add a turbo-charger, etc. without affecting the transmission. (Although a diesel engine does require a modified fuel tank IIRC.) Conversely, you can replace a manual transmission with an automatic and AFAIK that doesn't affect the engine at all. – David Mar 23 '17 at 13:03
11

Do we really need OO languages to manage software complexity?

This depends on the meaning of the word, "need."

If "need" means requires, no we do not require it.

If "need" means "provides strong benefits", then I would say, "Yes," we desire it.

Big picture

OO languages bind functionality to data.

You can avoid this binding and write functions that pass around data values.

But then you'll probably wind up with constellations of data that go together, and you'll start passing around tuples, records, or dictionaries of data.

And really, that's all method calls are: partial functions on bound sets of data.

Feature by feature

Features of OOP:

  • Inheritance allows for code (mixins) and concept (Abstract Base Classes/interfaces) reuse - but you can get this by redecorating functions and variables in a sub-scope.
  • Encapsulation allows for information hiding so we can work at higher levels of abstraction - but you can do this with header-files, functions, and modules.
  • Polymorphism allows us to use arguments of different types so long as those arguments support the same interfaces - but we can do that with functions as well.

However, none of those things happens so easily as with an object oriented language with first class support of those features.

References

There are lots of critics of OOP.

However, studies seem to indicate that we get greater programmer productivity from code reuse through OOP. This is a controversial finding, and some researchers say they can't reproduce these productivity gains, given certain constraints. (source)

Conclusion

We don't "need" OOP. But in some cases the user wants OOP.

My understanding is that mature programmers can be quite productive in the object-oriented style. And when packages have core objects with simple interfaces that are easily understood, even new programmers can become quite productive quickly.

10

I will try to be brief.

The core principle of OO is the combination of data and behavior in a single organizational unit (an object).

This is what allows us to control complexity and it was a pretty innovative concept when it emerged. Compare that to files on the one hand (pure data), programs that read and process those files on the other hand (pure logic) and output (pure data again).

Only once you have that package of data and logic together, modeling some real-world entity, you can start exchanging messages, create child classes, separate private from public data and behavior, implement polymorphic behavior, do all this OO-specific magic.

So, yes, OO is a big deal. And no, it is not just a bunch of old stuff with a fancy name.

Taking it all apart, looking at the elements and then saying "oh, well, there's nothing here I haven't seen before" is not recognizing the assembly that holds the innovation. The result is more than the sum of its parts.

8

There is no "official" definition of object oriented programming, and reasonable people disagree on what actually is defining quality of OO. Some say messaging, some say subtyping, some say inheritance, some say the bundling of data and behavior. That does not mean the term is meaningless, just that you shouldn't get too caught up in bickering about what real OO is.

Encapsulation and modularity are more fundamental principles of design, and should be applied in all programming paradigms. Proponents of OO does not claim that these properties can only be achieved with OO - only that OO is particularly well suited to achieving this. Of course the proponents of other paradigms like say functional programming claim the same for their paradigm. In practice many successful languages are multi-paradigm and the OO, functional etc. should be seen as tools rather than the "one true way".

I think all the principles used for managing complexity can be realized by procedural programming languages.

True, because in the end you can do anything in any programming language. It might just be easier in some languages than in others, since all languages have different strengths and weaknesses.

7

Something the other answers haven't mentioned: state.

You talk about OO as a tool for managing complexity. What's complexity? That's a fuzzy term. We all have this gestalt sense of what it means, but it's harder to pin it down. We could measure cyclomatic complexity, i.e. the number of run-time paths through the code, but I don't know that's what we're talking about when we use OO to manage complexity.

What I think we're talking about is state-related complexity.

There are two main ideas behind encapsulation. One of them, the hiding of implementation details, is pretty well-covered in the other answers. But another is hiding its run-time state. We don't muck around with objects' internal data; we pass messages (or call methods if you prefer implementation detail over concept as Jörg Mittag pointed out). Why?

People have already mentioned it's because you can't change the internal structure your data without changing the code the accesses it, and you want to do that in one place (the accessor method) instead of 300 places.

But it's also because it makes the code hard to reason about: procedural code (whether in a language that is procedural in nature or is simply written in that style) offers little help for imposing restrictions on the mutation of state. Anything can change at anytime from anywhere. Calling functions/methods may have spooky action-at-a-distance. Automated testing is more difficult, since the success of the tests is determined by the value of non-local variables that are widely accessed/accessible.

The other two large programming paradigms (OO and functional) offer interesting, but almost diametrically opposed solutions to the problem of state related complexity. In functional programming one tries to avoid it entirely: functions are generally pure, operations on data structures return copies rather than updating the original in place, etc.

OO on the other hand offers tools to deal with managing state (instead of tools for avoiding it). In addition to the language-level tools like access modifiers (protected/public/private), getters and setters, etc. there are also a number of related conventions like the Law of Demeter which advises against reaching through objects to get at other objects data.

Note that you don't need objects to do really any of this: you could have a closure that has inaccessible data and returns a data structure of functions to manipulate it. But isn't that an object? Doesn't that fit our conception of what an object is, intuitively? And if we have this concept, isn't it better to re-ify it in the language rather than (as other answers have said) relying on a combinatorial explosion of competing ad-hoc implementations?

5

Do we really need OO languages to manage software complexity?

No. But they can help in many situations.

I have used primarily a single OO language for decades, but most of my code is actually strictly pre-OO-style procedural. However, for anything involving a GUI, I use the language's vast OO library of built-in methods and objects, because it tremendously simplifies my code.

For example, a Windows application using the original low level Windows API to display a form, a button, and an edit field requires a lot of code, while instead using the libraries of objects that come with Visual Basic or C# or Delphi make the same program tiny and trivial. So, my OO code is usually relatively small and for the GUI, while my code that those objects invoke is typically much larger and usually not concerned with being OO (though it can vary depending on the problem I'm trying to solve).

I have seen OO programs that were overly complicated, relied on complicated esoteric rules about how the objects were implemented, and could have been much simpler if written without OO concepts. I have also seen the opposite: complex systems crying out to be reimplemented and simplified by using objects.

As you gain experience, you'll find different situations require different tools and solutions, and one size does not fit all.

3

As somebody who is involved in a very large project entirely written in C, I can definitely say that the answer is a clear "no".

Modularity is important. But modularity can be implemented in practically any decent language. For example, C supports modular compilation, header files and structure types. This is enough for 99% of the cases. Define a module for each new abstract data type you need, and define the functions for operating on the data type. Sometimes, you want performance and those functions are in the header file as inline functions, other times you will use standard functions. It is all invisible to the user which way is chosen.

Structures support composition. For example, you can have a locked hash table that consists of a mutex lock and a regular hash table. This is not object oriented programming; no subclassing is done. Composition is a tool that is much older than the idea of object oriented programming.

For the 1% of the cases where compilation level modularity is not enough, and you need runtime modularity, there is a thing called function pointers. They allow having individual implementations of a well-defined interface. Note that this is not object oriented programming in a non-object-oriented language. This is defining an interface and then implementing it. For example, subclassing is not used here.

Consider perhaps the most complex open source project there is. Namely, the Linux kernel. It is written entirely in the C language. It is done mainly by using the standard compilation level modularity tools including composition, and then occasionally whenever runtime modularity is needed, function pointers are used to define and implement an interface.

If you try to find an example of object oriented programming in the Linux kernel, I'm sure finding such example is very hard, unless you extend object oriented programming to include such standard tasks such as "defining an interface and then implementing it".

Note that even the C programming language supports object oriented programming if you really need it. For example, consider the GTK graphical user interface toolkit. It is actually object oriented, albeit written in a non-object-oriented language. So, this shows that the idea that you need an "object oriented language" is deeply flawed. There is nothing an object oriented language can do that another kind of language cannot do. Furthermore, if you're an expert programmer, you know how to write object oriented code in any language very easily. It is not a burden to use C, for example.

So, the conclusions are that object oriented languages are probably useful only for novice programmers who fail to understand how the concept is actually implemented. However, I wouldn't want to be near any project where the programmers are such novice programmers.

  • 1
    "[...] the conclusions are that object oriented languages are probably useful only for novice programmers who fail to understand how the concept is actually implemented." Interesting. What languages do you have in mind? Do you have examples of open source projects written in these languages which failed or did not fail? – Vincent Savard Mar 22 '17 at 15:52
  • 3
    You raise some good points, but your main idea is flawed. Yes, it is possible to implement OO concepts such as encapsulation, virtual dispatch, inheritance, and even garbage collection in a language like C. It is also possible to do that in assembly. It does not make programming easier. Programming and especially designing in a language like C is definitely more difficult than it is in an OO language. In C you need to map the concepts to their implementation, in an OO language you don't need to do that step (at least not for the OO concepts). – fishinear Mar 22 '17 at 17:23
  • 1
    "[function pointers] allow having individual implementations of a well-defined interface. Note that this is not object oriented programming in a non-object-oriented language. This is defining an interface and then implementing it." Sorry, but that's completely wrong, because this is exactly what OOP is. " For example, subclassing is not used here" Subclassing is not a required feature of OOP. Note, for example, that JavaScript is an object oriented language that does not feature subclassing (or, for that matter, classes at all... Just objects that contain function references). – Jules Mar 23 '17 at 10:06
  • 1
    To clarify my last comment, my point is that the b main distinguishing factor between OOP (not necessarily any specific OO language) and other methods is that in OOP we define interfaces that operate abstractly on data without needing to know the format of that data by binding the implementation of the interfaces to the data itself. That is what OOP is. The method of implementation is irrelevant, whether it's a Java style class, a JavaScript object (effectively a map of name to attribute, which may be either data or code) or a structure containing function pointers and a void * for the data. – Jules Mar 23 '17 at 10:34
  • 1
    " So, the conclusions are that object oriented languages are probably useful only for novice programmers who fail to understand how the concept is actually implemented. " .... And this, frankly, is plain insulting. I know quite well enough how these concepts are implemented. I've done enough work using then to be proficient, even. I've even implemented both an interpreter and a compiler for OO languages in the past. But because I prefer to work in higher level languages that have first class objects I must be a novice programmer who you'd rather not work with?! – Jules Mar 23 '17 at 10:47
2

The reason for introducing programming paradigms, including object-oriented methods, is to make it easier to make more sophisticated and powerful programs. In the August 1981 issue of Byte Magazine, Daniel Ingalls, one of the key creators of Smalltalk, defined "object oriented" as involving the following capabilities:

  • automatic storage management
  • ability to exchange messages
  • a uniform metaphor that applies to all operations of the language
  • no component depends on the internals of another component (modularity)
  • program defines only behavior of objects, not their representation (polymorphism)
  • each component should appear in only one place (factoring)
  • the use of a virtual machine which is hardware independent
  • every user-accessible component should be available for observation and control (reactive principle)
  • there should be no overall controller (no operating system)

These were the principles Ingalls identified as being the driving design considerations for SmallTalk-80 as developed by Xerox Parc Research. In the aforementioned magazine article you can read a detailed description of each of the these principles and how they contribute to the object-oriented paradigm according to Ingalls.

All of these principles can be applied using any Turing complete language, whether it be procedural, assembly language or whatever. These are design principles, not a language specification. An object-oriented language is intended to make it easier to use these principles when creating software.

For example, to take the first of Ingall's principles (automatic storage management), anyone can write their own automatic storage management system in a procedural language, but it would be a lot of work to do so. When using a language such as SmallTalk or Java that has automatic storage management built in, the programmer does not have to do as much work managing memory. The tradeoff is that the programmer gets less control over the way memory is used. So, there is a benefit and a drawback. The idea of a design paradigm like object-oriented programming is that the benefits of the paradigm will outweigh the disadvantages for at least some programmers.

  • I think the same rules would apply in the case of Domain Specific Languages. Same advantages and disadvantages... The one difference is that DSLs can be made simple enough for end-users to work with, because the 'language' corresponds to their understanding of the problem space, and nothing else is included. – user251748 Mar 23 '17 at 15:33
0

One way of managing software complexity is to separate the framework from the desired actions entirely using a Domain Specific Language. This means that the level of programming code is distinct from the level at which desired results are configured - a completely different language or system. When this is done properly, the conventional code essentially becomes a library, and the user or other person creating the desired results plugs things together with a scripting language or visual design tool, such as a report generator.

To work, this requires drawing a strict boundary around what operations will be possible and how they link up (script language or visual design, like a form construction tool). Metadata is an important way of abstracting out the run-time configuration from the coding details, making it possible for a system to support a wide range of desired results. If the boundaries are laid out and held to (not accepting every request for extension that comes along) you can have a long-lasting and robust system that works for people, without them needing to be programmers to accomplish what they want.

Martin Fowler has written a book about this, and the technique is almost as old as programming itself. You could almost say that all programming languages are Domain Specific Languages, and so the idea is endemic, overlooked because it is so obvious. But you can still create your own scripting or visual design tools to make your life easier. Sometimes generalizing a problem makes it far easier to solve!

0

This is a very good question and I feel the answers given here have not done justice, so I will go ahead and add my thoughts.

The aim is - Manage Software Complexity. The aim is not "use OO language".

The is no 'reason' behind introducing a new paradigm. It is something that happened naturally as coding became more mature. It makes more sense to write code where we add a coach at the end of the train (the train being modeled using a linked list) rather than adding a new node to the end of the linked list.


Coding in terms of real world entities is simply the obvious and correct way to code when we are coding about the real world entities.


A computer can work with adding a node to the end of linked list as easily as it can work with adding an extra coach to the end of the train. But for humans it is easier to work with the train and coach than with linked list and nodes even though when we go a level deep, we find the train being modeled by means of a linked list.

Protecting or encrypting the files cannot achieve encapsulation. The opposite of encryption is decryption. The opposite of encapsulation is Decapsulation which means Decomposition of structures and classes in programming languages to achieve better performance. The performance gained from reducing memory traffic and avoiding OOP rules checks.

Hence you can write code that is both encrypted and well encapsulated because these two are different concepts.

Encapsulation helps in managing the complexity by virtue of it being close to the reality.

Therefore, program in objects because, it is easier for you to code and it is faster for you and everyone else to understand.

0

The one thing to remember is this:
OOP is not about language features; it is about the way you structure your code.

OOP is a way of thinking, and of designing the architecture of your code, and it can be done in just about any language. This especially includes those low-level, non-OO languages, which are called assembler and C. You can do perfectly object-oriented programming in assembler, and the Linux kernel, which is written in C, is quite object-oriented in many aspects.

That said, OO features in a language greatly reduce the amount of boilerplate code that you need to write to achieve the desired results. Where you need to explicitly define a virtual function table and fill it with the appropriate function pointers in C, you just do nothing in Java, and you are done. OO languages simply remove all that enabling cruft from the source code, hiding it behind nice, language-level abstractions (like classes, methods, members, base classes, implicit constructor/destructor calls, etc.).

So, no, we don't need OO languages to do OOP. It's just that OOP is so much easier to do with a decent OO language.

-1

Object oriented programming is more than just modules + encapsulation. As you say, it is possible to use modules + encapsulation in a non-object-oriented (procedural) language. OOP involves more than just that: it involves objects and methods. So, no, that doesn't capture OOP. See, e.g., https://en.wikipedia.org/wiki/Object-oriented_programming or a good textbook's introduction to OOP.

  • Thank you for the Answer. Could you recommend one ? – steakexchange Mar 20 '17 at 9:53
-2

The biggest reason is that, as a program gets more complex, you need to make some parts of it invisible from other parts, or the complexity of the app and the number of functions will make your brain dribble out of your ears.

Let's imagine a system of 100 classes, each with about 20 operations that can be performed on them; that's 2,000 functions. However, of those, maybe only 500 are complete operations like 'Save' and 'Delete', while 1500 are internal functions which perform a little bit of maintenance, or have some utility role. Consider;

// intentionally in a non-specific language!

setName(person, name) {
    nameParts = splitPersonName(name);
    person.firstName = nameParts[0];
    person.lastName = nameParts[1];
    person.modified = true;
}

splitPersonName(name) {
    var result = [];
    result.add(name.substring(0, name.indexOf(" ")));
    result.add(name.substring(name.indexOf(" ") + 1));
    return result;
}

So SetName is a function people should be doing to a person, but SplitPersonName is a utility function used by the person.

Straight procedural programming makes no distinction between these two operations. That means your 2,000 functional all vie for your attention. However, if we could mark these functions as 'available to everyone who has a person record' and 'used only as a utility function inside the person record' then our attention is now 500 'available to everyone' functions, and 15 'utility' functions for the class you're editing.

That's what public and private do;

public class Person {
    public void setName(...) {...}
    private string[] splitPersonName(...) { ...}
}
  • 1
    I am not sure if you understood my question correctly. I am already aware of encapsulation and data hiding as a way to manage complexity. I just think that this can be easily done in procedural languages aswell by dividing the program into modules that perform well-defined simple tasks whose inner workings are specified in separate protected sources. So why OOP when we can do those things in procedural languages? That is my question. – steakexchange Mar 20 '17 at 10:47
  • 2
    I guess my answer is that if you want to do this sort of thing, you're going to have to write special tooling and language constructs to do so (for example, a special 'include' call to a include one file that may not be included elsewhere). Once you've started down that path, you've actually started implementing an object oriented language in your own way. For example, C++ was originally a preprocessor which produced plain C, and I suspect once you're implementing your system it looks a lot like C++ on top of C. – user62575 Mar 20 '17 at 10:58
  • 3
    @steakexchange Do note that OOP was developed a time where many procedural languages didn't have modules like that. Some wouldn't even call such languages procedural. Indeed, there's nothing that says a procedural language must not be OO or vice versa. Be careful of labels - they can easily mislead you. If your procedural language supports modules that have public and private fields and procedures, good for you :) The main difference between "traditional procedural" and "OOP" is that the call dispatch is more flexible in OOP - indeed, in strict OOP, you never know what code you call. – Luaan Mar 20 '17 at 13:59
  • 2
    @steakexchange In ML-family languages, modules work great, and in combination with lambdas, they give you all the power of any OO language (after all, a function is an interface with a single method - isn't that almost what's recommended by "good code" guys in OO? :P). For various reasons, they're still less used than the more procedural languages like C++ or Java, but they do have their appeal, and many people are trying to educate people about how they can simplify their life (with more or less success). – Luaan Mar 20 '17 at 14:01
  • C effectively has these things. It has modules (.c files) with interfaces (.h files) and it can have public (extern) and non-public (extern) methods (functions). You can even have poor man's polymorphism with arrays of function pointers, I'm not saying OO is easy in C (or perhaps, sane) but encapsulation is pretty easy, – Nick Keighley Mar 21 '17 at 15:46

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