Yenya's World

Mon, 24 Sep 2007

Objects Considered Harmful

From time to time I feel a strong urge to write an article describing how object-oriented programming is bad. Well, this post is not a full analysis, just a simple example. I have tried to read some Java code (maybe I am becoming a masochist :-), and found this:

    /**
     * Get X position.
     * Shortcut for getPosition().getX();
     *
     * @return X position
     */
    public int getX() {
        return x;
    }

It is nine lines (and nine more for Y) so that they can write something like this:

    myobj.getX() + myobj.getY();

instead of the following:

    myobj.x + myobj.y;

I wonder why some programming languages tend to attract people who like to write mid-layers. Java is probably the worst one, but also Python with its Zope and Plone mammoths, and Ruby with RoR are similar. Wrapping wrapped wrappers into the wrapper code. I have yet to see something like this in Perl (yet OOP is widely used in Perl). I think accessors and mutators are amongst the worst habits in OOP. Most often they are just like the above code - dummy wrappers for the class member variable. Maybe Java people have some sort of mighty IDE, which generates such a write-only garbage for them.

In a related news (related to the mid-layer problem), see this post about trying to use Ruby on Rails (another midlayer-happy framework).

Comming soon on your favourite flamebait channel: Wrapping SQL Database to Objects Considered Harmful. Stay tuned.

Section: /computers (RSS feed) | Permanent link | 15 writebacks

15 replies for this story:

Martiner wrote:

Why is setter/getter better than simple public field? Because when you decide that variable must be e.g. greater than zero, you insert single line into the setter instead of changing every assignment into this variable. Yes, Java IDEs have generators for getters/setters garbage.

Yenya wrote:

It _might_ make sense if the code in question was some public library. But in a closed project in which this particular class has exactly zero subclasses, this simply does not make sense. If you really want type checks or whatever, just declare your own type "postitive_integer", and embed this check to its operator= (or whatever it is named in Java). But this kind of trivial mutators/accessors does not make any sense.

Martiner wrote:

It makes no sense for me to have tons of types like positive_integer, integer_bigger_than_100,... You dont believe in encapsulation, but I dont believe that people who write ugly code in "closed project" will write simple, clean and maintainable public API.

Yenya wrote:

The point is that there is no enapsulation in that project: I would tend to agree that having accessor and mutator functions have _some_ value if the accessor or mutator had at least some chance of being non-trivial. But this is not the case. Also, rather than "positive_integer" in this case the type would be something like "coordinate_t". As for the second part of your comment - "but I dont believe ..." - I fail to understand how it is related to this case: there is no public API in this project.

petr_p wrote:

It's about ambitions of your code. If you believe you are the only user and maintainer of the code, you could afford to keep the code as simple as possible (no intermediate layers, no access protection, no type checking, no comments). But if you think somebody could use your code somehow sometimes, it will be better to build solid interface. And OOP provides straight way how to do it. I agree with you that misusing of OOP bloats your code, eats computer resources etc. But that are pros and cons of every middle ware.

thingie wrote:

When you suddenly decide that variable must be greater than zero (something you didn't say before), it's still change of your API. If someone used it with zero, his code will break and getters/setters won't help him much...

Milan Zamazal wrote:

IMHO classes should have no public slots (AKA attributes or fields) and data should be provided only through methods (I usually do it that way). It doesn't matter whether the code is public or private, one should try to write any not-one-time programs in an elegant way, to save his own work. Providing idiotic examples from idiotic languages is no argument. Java is no way anything close to a modern object oriented language, regardless what Sun's marketing claims. And the primary point of using accessors is not only to allow some checks or so, but to hide implementation details from the class. If you ask a class for data, e.g. a person's name, you shouldn't bother whether it's stored in the class instance directly as a slot or whether it is computed from other data, retrieved from the database, etc. You just call person-name function on the class and get the desired data. Nor you should presume the implementation by naming the method get-person-name or so. Civilized languages provide built-in means for using accessors, e.g. you can declare a slot in the following way: (name :accessor person-name). Other languages allow you at least to implement such things yourself, e.g. then you can write a slot definition by something like Slot('name', writable=True) in Python and the corresponding class attribute and accessors get defined automatically. In really stupid languages like Java you have no better choice than to generate a lot of lines of code which may make the whole thing counterproductive. As for amount of types: Of course it is useful to define a lot of custom types. And I guess you do it in OO all the time: Whenever you define new class, you define new type (unless you use the class just as a name space).

Yenya wrote: re: petr_p

You seem to miss my point. I am not against type control (I sometimes think Perl would be better with at least some compile-time type controls), nor comments, etc. What I object against are _trivial_ accessor/mutator pairs. They bring nothing when compared to the direct access to the member variables. They are slower, more expensive to both write and then understand. And they are even surrounded by heavy comments. Superfluous comments are evil as well: comments should be used for explaining hacks or non-trivial usage, not every method and variable. I think readability and maintainability of the code in question would be _way_ better without those trival accessor/mutator pairs and without those trivial comments. With current IDEs it is even trivial to refactorize - to change a direct member variable access to an accessor or mutator method call, _when_ (but not sooner than) you decide you want to do something nontrivial in the mutator or accessor. Interfaces are rarely set in the stone, and any project (not to mention such a small internal class) should be prepared to refactorize, when the need arises. As for building a "solid interface": it is an impossible task. [Almost] nobody is smart enough to see the future and design a perfect interface. But instead of including everything and a kitchen sink in your class, I think it is better to write _some_ interface, and be prepared to write more when it is needed. Java programmers seem to think that because their code is a class, it would be used as such - as a generic all-purpose object. But for many classes it is simply not true. Many classes have no subclass, and not all of the class interface is ever used in the project. And understanding such a project means scrolling through many and many pages, filled with dummy comments and trivial subroutines.

Vašek Stodůlka wrote:

I have been working with two ERP systems. One was object-oriented code on a traditional SQL database and it has a thick client. It is a very bad concept - you do not have access to code, you can manipulate only with database or through client and nobody gives you guarantee, that your interventions to database will not destroy the entire system and the client is usually stupid (our was :). And even when it was object-oriented code, the selling company cannot make changes to tables, because any user or partner code will not work with changed structures and some programs relied on a client too. So when somebody changed something, nobody knew what to do and whose fail is it. Today I work with another ERP system - it has something like object-oriented database and application server (with a text-based protocol, very well documented) and it has an interpreter with language, which is something between basic and assembler. The situation with database changes is similar to above - when a system producing company changes the database (objects), the programs will stop working. But I have error messages from an application server and with these I can repair my code very quickly. And the conclusion is - it does not matter if you use object-oriented or non-object-oriented language. Instead you have to have a good concept (or at least a layer of application server) and a good API / protocol. But maybe this holds only for ERP.

Yenya wrote: Re: Milan Zamazal

I disagree with two of your points: If I understand the first few sentences of your comment, you tend to write a _complete_ interface, instead of a minimal (or as-needed) one. This is hardly about "saving your own work" (and adds an unnecessary bloat the reader of your program has to walk through, just to discover parts of the code are not really used anywhere). The other point I disagree with is when you talk about the need to hide the implementation details: Unless you write a library for a public use (good luck with that anyway, it is _hard_), this is rarely a valid reason. But a local class inside a local project? No way. Usually you know in advance whether this class would have to hide something, for example to allow a different storage of the data. And even if you don't, it is still easier to access the member variable directly, and refactorize later, as needed. In many editors/IDEs it is trivial.

Yenya wrote: Re: Vašek Stodůlka

Agreed, but the situation is different for (even semi-)public interface of the ERP, and an _internal_ class of a single project.

Milan Zamazal wrote:

I'm not talking about complete interfaces, but about extensible interfaces, this is a very different thing. Hiding implementation is always useful, it's a general principle of building programs from well defined autonomous parts. When I change a function and the change forces me to change code that uses the function (I admit it happens to me often), I know I'm doing useless work caused by a mistake in the extensible design. And I can't see any problem in writing foo.bar() instead of foo.bar all the time, it's more consistent than writing sometimes foo.bar and sometimes foo.baz() anyway. As for comments, it's necessary to distinguish between code comments and documentation strings. Code comments should be rare, good code usually documents itself. Documentation strings should be attached to anything what is exported from a module.

Yenya wrote:

"Hiding implementation is always useful" - nope (unless the implementation is nontrivial, which is not the case here). If it adds a bunch of useless code, it is _not_ useful, especially when this is a purely internal thing and the refactorization using a modern IDE is easy. As for your "documentation strings". From the point of view of the reader of the code, this is _also_ a comment, which should the reader read (or skip). It lowers readability.

Lukas Lalinsky wrote:

I know this wasn't supposed to be about specific languages, but... "... but also Python with its Zope and Plone mammoths, and Ruby with RoR are similar." Seriously, Python is anything but not similar. Any Python programmer would in this case simply use the "x" attribute directly. The point why C++/Java programmers have to use getters/setters is that they can't switch from dumb to smart accessors without breaking the API. In Python you can switch then any time and still have the same API - "myobj.x". But this is a limitation of many languages that are often (incorrectly) presented as reference OO languages, not a problem with OOP itself.

Yenya wrote: Re: Lukas Lalinsky

Thanks for explanation. I was refering to Python in general (esp. Plone and Zope). Fortunately these days there are also Python programmers who focus on getting the job done instead of writing "everything and a kitchen sink" mammoths like the two mentioned.

Reply to this story:

 
Name:
URL/Email: [http://... or mailto:you@wherever] (optional)
Title: (optional)
Comments:
Key image: key image (valid for an hour only)
Key value: (to verify you are not a bot)

About:

Yenya's World: Linux and beyond - Yenya's blog.

Links:

RSS feed

Jan "Yenya" Kasprzak

The main page of this blog

Categories:

Archive:

Blog roll:

alphabetically :-)