9 ms·
Your essay is arguing against a strawman of what "good" OOP is supposed to be. Your examples... >Objects just aren't supposed to be reaching into each other w
by jasode 11y ago
Your essay is arguing against a strawman of what "good" OOP is supposed to be. Your examples...
>Objects just aren't supposed to be reaching into each other with 'getters' and 'setters' and messing with information.
The getX() and setX() is an anti-pattern of good OOP.
>Instead of using objects for compartmentalizing functionality, they were just used as a holding pen for loosely related functions.
No, good OOP has class/object with both private state and associated functions combined to provide a public interface for clients to use.
(Although Java & C# (unlike C++) may have muddled this aspect because their language specification does not allow "free" functions to live in a plain namespace. Therefore, programmers are forced to use the "class{}" keyword with static functions as a workaround. Maybe that's where your "holding pen" characterization came from?)
>A traditional OOP approach uses shadowy architecture to remove the responsibility of an object,
No, good OOP tries to push responsibility into classes/objects so they can act as independent agents with minimal coupling to the outer context they sit in.
I'm guessing your comments are based on seeing a lot of poor practices masquerading as OOP which affects what you think OOP actually is. Unfortunately, it's like trying to argue against Javascript because of overuse of "eval()" or arguing the flaws of Haskell because variable names are often 1 character long.
There are definitely problems with OOP but they are not the bullet points you mentioned.
- smt88 11y agoI think your comment highlights my biggest problem with OOP: no one agrees on what it is or how to organize it. The amount of mental energy I used to spend on exactly how things should be separated into objects and how those objects interacted was astronomical.
- rlt3 11y ago>I'm guessing your comments are based on seeing a lot of poor practices masquerading as OOP which affects what you think OOP actually is I personally feel that the current OOP model eventually devolves into the bullet points you listed given enough complexity. One reason why I think this occurs is that there is no concept of a 'message' in OOP, only methods or functions of a class. There's explicit coupling even when calling a method with no arguments because you know the method is valid for that object. Contrast this with the example of the 'listener' in the room. A speaker broadcasts his message but the independent agents ultimately decide what to do with that message. The OOP approach calls "object->listen" on each listener. My approach simply broadcasts the message and lets the objects determine how to handle it themselves.
- pmontra 11y agoNot all actor based languages can broadcast messages to e every actor. Example: Erlang sends messages to a given process. To broadcast to many processes you must know the pids of all of them. Or send to a "bus" process that knows the pids of every one else. Performance bonus (maybe) if the bus knows which process will be interested in a given message. Not much different than having a central registry of objects acting as a bus and dispatching method calls. Furthermore you can have tight coupling with message passing too.
- jasode 11y ago>One reason why I think this occurs is that there is no concept of a 'message' in OOP, only methods or functions of a class. If you mean there is no formal/explicit OOP syntax in C++/C#/Java etc for a "message bus" or "queue" for a decoupled publish/subscribe type thing, you're right. Yes, Golang/Erlang have that concept a little more "baked" into the language. But that's still orthogonal to the supposed OOP flaws you brought up. The C++/C#/Java approach to the missing "message" functionality would be either to create a class with a managed buffer to "hold messages" for other classes to write or read from... or use a library that interfaces with an external messaging bus.
- rlt3 11y ago>The approach to the missing "message" functionality would be either to create a class Yeah, I explicitly covered this. I don't feel like piling some 'controller' object on top of other objects necessarily accomplishes what I'm after. But it does work and I've used the pattern plenty.
- vidarh 11y ago> One reason why I think this occurs is that there is no concept of a 'message' in OOP, only methods or functions of a class. There's explicit coupling even when calling a method with no arguments because you know the method is valid for that object. This is specific to early-binding languages like C++, Java etc. Look at Smalltalk or Ruby, and this is not true in the general case. E.g. Ruby ORM's tend to dynamically create classes and methods at runtime based on analyzing the database schema of the database you connect to. You literally won't know if a given method exists until you've connected to the database. Even then, there are no guarantees - depending on framework it may e.g. let your call hit "method_missing" first time (or every time) and optionally then define a method (or it may continue to handle it via method_missing, but defining a method can be much faster, depending on situation) > My approach simply broadcasts the message and lets the objects determine how to handle it themselves. So OOP the way Alan Kay describes it, in other words.
- patrickmay 11y ago> The getX() and setX() is an anti-pattern of good OOP. This needs to be tattooed in reverse on many developers foreheads so it's the first thing they read when brushing their teeth in the morning.
- UK-AL 11y agoProblem is developers take that as an excuse to make state public, which is even worse.
- kasey_junk 11y agoIt doesn't matter whether you make the state public via a getter or via variables (modulo some compiler specific reasons to use accessors). If your design is allowing some external actor to work with multiple parts of the state of a behavior driven object (ie not a record) accessors are not going to help you, you've still exposed the state.
- UK-AL 11y agoCompletely agree. But at least with getters/setters you can intercept calls before they modify state. But you should design so you don't need this in the first place.
- atonparker 11y agoIt seems to me like you and the author agree about what good OOP should be (encapsulated state, message passing), except they argue that "traditional" OOP is not useful, and you see Traditional OOP as Good OOP. In fact, "good" is not used a single time in the article so I don't see how they could be constructing a strawman against it. I'm guessing the author is using "traditional" in the sense of what is traditionally taught, which in my experience was closer to Java beans and FactoryFactory classes. In other words, a terrible misinterpretation of Alan Kay's ideas. If you actually look at the framework they are presenting, it pushes the developer towards many of the "good OOP" points you made. So I'm not sure exactly what you're arguing here.
- jasode 11y ago>, and you see Traditional OOP as Good OOP. No, my comments don't agree with that. In fact, I tried to point out that his "traditional OOP" examples are incorrect OOP and therefore, a straw man to be arguing against.
- abiox 11y agothe swell thing about "OOP" is how ill-defined and vague the notion can be. just like "correct OOP" and "incorrect OOP". as a consequence, discussions can go back and forth, frequently resulting in little but gymnastic displays of equivocation and red-herrings. when the dust settles, surprisingly little communication has occurred.
- mpweiher 11y ago"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." http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay_oop_en http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay...
- kazagistar 11y ago
- kazinator 11y agoMoreover, getters and setters can be readily modeled with messages. That messages and (at least single dispatch) methods are identical was settled decades ago. (Has someone worked out a multiple dispatch OOP based on message passing?)
- vidarh 11y agoThe conceptual difference between messages and methods to me is that messages implies that it is the object itself that is the arbiter of whether or not it can handle a specific call, and the decision may depend on factors not knowable before the call. While "method" implies to me that it is something that may be applied to the object by an external actor. E.g. if I do "foo.bar()" in C++, the compiler will simply not compile the code if "bar" is not already defined on the class "foo" is declared as. The object at runtime has no influence on the dispatch. In Ruby, on the other hand (as well as Smalltalk and other languages with late binding and message passing), regardless of implementation details, whether or not "bar" is not defined when the code is parsed is irrelevant. You can in the general case not know before reaching the call-site whether or not "bar" is defined, and even if it is not, it is not up to the interpreter to throw an error if e.g. a method_missing is defined, and you can't in the general case determine in advance whether or not there will be a method_missing defined when you get to the call site. The object can decide to process different messages depending on the time of day if it pleases. See also the Alan Kay quote elsewhere in this thread - especially the part about late binding. You can achieve this with e.g. vtable based dispatch (fill in "missing" slots with thunks that loads a symbol and calls method_missing, and fill in the "method_missing" slot with a generic one that raises an exception), so this can still lead to similar implementations. It's a different way of looking at things, coupled with the dynamism of very late binding.
- kazinator 11y ago> if I do "foo.bar()" in C++, the compiler will simply not compile the code if "bar" is not already defined on the class "foo" is declared as In a more dynamic language, in fact "foo.bar()" may be equivalent of asking foo whether it understands how to bar, and if so, then please do it. If foo doesn't understand, there could be some mechanism for handling that, like a catch-all function that can be defined that gets invoked. Likewise, we could also have a static language with message passing OOP in which the same check is implemented. The classes declare statically which messages they understand and the compiler checks that. Quite simply, "late binding" != "message passing". There can be late binding of messages, and late binding of function calls. There is no real conceptual difference between single dispatch and (synchronous) message passing. What is Unix ioctl? Message passing or function call?
- incepted 11y agoGood points except saying that getters and setters are an anti-pattern of OOP. Actually, you contradict this a few lines blow: > No, good OOP has class/object with both private state If a class has state, you have to be able to read that state or it's useless. It's a good practice overall to tell classes what to do but eventually, you need to get information from them. That's where getters come into play.
- jasode 11y ago>If a class has state, you have to be able to read that state or it's useless. ... That's where getters come into play. No, having a bunch of getABC(), getXYZ(), getEtc() is a code smell. If the class has many getters()+setters() or has the equivalent of many public data members, it means that related actions requiring those variables are happening outside of the class/object instead of inside the class. The more getters()/setters() made available means the more the programmer is treating the class as a "dumb struct{}" with exposed members instead of a "smart agent" with knobs & levers to direct a hidden machine. The "knobs & levers" should be higher-level public methods that are not gets()+sets(). For example, let's say we have a TextBuffer object: With get()/set() mentality: TextBuffer.setLinecount(0); // reset counter to 0 for() { TextBuffer.getNextLine(); n=n+1; } TextBuffer.setLinecount(n); With a public method to make the object smarter about itself: TextBuffer.CountLines(); The method CountLines() replaces gets()/sets() and makes the object more "black box". The "for(){}" loop would be inside the object. Making objects "smarter about themselves" is a hallmark of good OOP. Less exposed getters() is less coupling and less pollution of classes' internals to the outside world. Refactoring/reducing gets()/sets() means unifying those outside actions acting on object's internals into a higher-level method interface. I'm not saying all gets() can be eliminated. But the Java practice of having 20 private member variables mirrored by 20 public gets()/sets() is not what OOP is about. It's actually about as opposite from OOP as you can get!
- matwood 11y ago> I'm not saying all gets() can be eliminated. But the Java practice of having 20 private member variables mirrored by 20 public gets()/sets() is not what OOP is about. It's actually about as opposite to OOP is you can get! Maybe people are just confusing different sides of the same concept. There are smart objects as you mention and dumb data holders that are used to pass 'messages' around. The classic anemic data model, with objects that operate on said data.
- anexprogrammer 11y agoYou indirectly put your finger on the real problem with OOP, and that's that a lot of people using it in the wild are frankly not very good at it. Bad object design, not seeing what should be objects in the first place. I always felt that people who had never used C before C++ or Java, nor any other procedural language, aside from some scripting tended to some of the messiest OO. Seemed they'd want to create a sea of objects and patterns even when something simple was staring them in the face. OO is harder to teach, and clearly harder to understand. That's the top and bottom of most issues with it.
- kazagistar 11y ago> OO is harder to teach, and clearly harder to understand That is a pretty damning judgement of OOP. It seems to me the whole point of a paradigm should be to improve understandability. If, in practice, it makes it worse, then it is a failed paradigm.
- smadge 11y agoIf you give someone a gun, they might shoot themselves. If you give someone a table saw, they might cut off their hand. If you teach someone objected oriented programming, they might write a unmaintainable ball of mud.
- spriggan3 11y ago> The getX() and setX() is an anti-pattern of good OOP. No it isn't, it provides encapsulation, which the most important aspect of OOP. I agree with the rest.
- jandrese 11y agoIt can very easily break the encapsulation though. Now external processes are dependent on direct access to the internal state of your object, and if the object is refactored in the future you need to reprogram those getters and setters to return something that is still correct but may not represent the internal state of the object at all and may in fact be complex now. An example of where this can all go wrong is if you have a TCP object that opens a connection to a remote server and then lets you read and write the socket. Someone decides that they need to find out what IP address the object used when it connected and create a get_ipaddr() method that returns a s_addr type. But then the TCP object is updated to support IPv6 and now the author needs to figure out how to return a V6 IP address to external methods that only expect V4. The proper way to implement it might have been to move whatever logic was examining the IP address into the TCP object itself. Of course this is how you end up with horrendously complex objects with hundreds of methods all used exactly once somewhere in the code or exactly once in the code of some other project.
- spriggan3 11y ago> An example of where this can all go wrong is if you have a TCP object that opens a connection to a remote server and then lets you read and write the socket. Someone decides that they need to find out what IP address the object used when it connected and create a get_ipaddr() method that returns a s_addr type. But then the TCP object is updated to support IPv6 and now the author needs to figure out how to return a V6 IP address to external methods that only expect V4 In that case you should use polymorphism. There is no reason why the consumer of the address returning the IP should know whether get_ipaddr returns an IPV4 or an IPV6 directly.
- jasode 11y ago>No it isn't, it provides encapsulation, The extensive use of getters()/setters() is a common misunderstanding of "encapsulation". It's an example of following the "letter of the law but not the spirit of the law". Let's separate the idea of "encapsulation" into 2 categories: (1) syntax encapsulation: private int x; public getX(); public SetX(); // letter of the law (2) conceptual/semantic encapsulation: Object.DoSomething() that works on x internally // spirit of the law It's the idea presented in (2) that follows the ideals of OOP design and helps reduce cognitive load. The common coding practice of (1) doesn't really provide "encapsulation" that helps make large scale programs more understandable. I also think (1) was exacerbated by codegen tools such as ORMs and GUI data binding frameworks. Therefore, inexperienced programmers thought that having a ton of gets()/sets() in their own manually handcrafted classes was "correct OOP".
- Tloewald 11y agoIn a sense the OP is making a point about which paths are paved in most OOP languages. That said, one of the problems I often see in user interfaces is "runaway metaphors". The "objects" and "messages" in question are _metaphors_ and it's important not to let a concept be taken hostage by the metaphors used to describe it. The main reason controllers exist isn't (or shouldn't be) because of a failure of OOP as a concept, but because of efficiency or complexity. Going back to the baseball example, while it might make intuitive sense for the player to send a "running to first" message, simulating a team's reaction to that event by having player objects communicate purely by messages is horribly complex, whereas a fairly decent simulation can be quickly created by "cheating" with a team controller object (or game controller or whatever). Even in a "pure" implementation, objects will want to examine each other ("how fast can that player run? Where is she right now? Now? Now? Where's the ball? Who has it? How good an arm do they have?") If anything, the baseball metaphor shows how complex things can get, and quickly reveals cases where almost any programmer would choose to violate encapsulation to make things work.
- Retric 11y agohow fast can that player run? Is asking about internal state. When can you get to X is not. The internal vs. external separation can often greatly simplify things. A player does not care where another players feet are they care where the best place to toss the ball is. PS: In an actual game there is a lot of communication going on. You don't want to throw a ball at someone looking in the wrong direction. So, at some level you're stuck dealing with innate complexity.
- Tloewald 11y agoOf course there's innate complexity, but pursuing encapsulation for its own sake adds complexity you don't need (just like using functional programming techniques for intrinsically non-functional use-cases adds complexity for no good reason). Again, "messages", "objects", etc. -- these are metaphors. They should be abandoned as soon as they stop being helpful.
- 11y ago
- Tloewald 11y agoIn a sense the OP is making a point about which paths are paved in most OOP languages. That said, one of the problems I often see in user interfaces is "runaway metaphors". The "objects" and "message Saying" in question are _metaphors_ and it's important not to let a concept be taken hostage by the metaphors used to describe it. The main reason controllers exist isn't (or shouldn't be) because of a failure of OOP as a concept, but because of efficiency or complexity. Going back to the baseball example, while it might make intuitive sense for the player to send a "running to first" message, simulating a team's reaction to that event by having player objects communicate by pure message is horrible complex, whereas a fairly decent simulation can be quickly created by "cheating" with a team controller object (or game controller or whatever). Even in a "pure" implementation, objects will want to examine each other ("how fast can that player run? Where is she right now? Now? Now? Where's the ball? Who has it? How good an arm do they have?") If anything, the baseball metaphor shows how complex things can get, and quickly reveals cases where almost any programmer would choose to violate encapsulation to make things work.
- jack9 11y ago> No, good OOP has class/object with both private state and associated functions combined to provide a public interface for clients to use. I don't believe there's evidence of this. Inteface scoping (e.g. "private/protected/public") is a relatively recent invention to protect against programmers who "don't know what they are doing". Adequate documentation is a superior tactic since that's a cursory best practice.
- KirinDave 11y ago"Good" OOP and "bad" OOP. There's just no difference. And quite frankly, if the only difference between their "getX" and a symmetrical message passing pattern that retrieves a copy of an attribute is the implementation details then... what? So much of "Good OO" seems to be prescribing people's mental models and not their actual practices. Seems somewhat odd to me, since most of the models are indistinguishable from one another in practice.
- collyw 11y agoI am glad I work in a mixed metaphor language. Choose the best bits when appropriate instead of arguing what is proper OOP or not.
- jasode 11y ago> instead of arguing what is proper OOP or not. Every programming paradigm and every programming language suffers the distinction of what is "proper" and "improper". There's proper functional programming and improper functional programming. For example, it's possible to write bad code of passing GodUniverseState records to "functions" that the Haskell/Ocaml compiles without error. However, that's not "proper" functional programming. It's just messy global state masquerading as a functional program. There's proper SQL usage and improper usage. For example, writing a stored procedure to loop through one table with a cursor, and then looking up a column value in another target table is "improper SQL". SQL has built in "joins" to do it correctly (and also performs faster as a bonus). Even Python has a category of "not Pythonic".