“Is Evil..” titles are evil
Too excellent articles on Object Oriented Design: Why extends is evil and Why getter and setter methods are evil. Ignore the inflammatory titles: the subheading of the second article, “Make your code more maintainable by avoiding accessors”, is a much better indication of their content. I picked up some great tips on proper use of OOP from reading them. In particular, the section on CRC cards made something click which hadn’t clicked when I looked at them earlier this year for my ill fated University software project.
I have to say that I disagree with the opinion that accessors are evil. In fact, I really have to raise an issue with the entire article, but then you have to understand my experiences.
For a while, I worked on a version of a Smalltalk development environment. By it's very nature and definition, I had no idea how elements would be used because I was building various core elements. Accessors were not only a good idea there, but damn near required.
Accessors often do more than simply get or set a variable. They can also do checks and balances, convert value, set instance counts and locks and all sorts of other behaviors that absolutely should be hidden from the user. Relying on the end user to perform these tasks is suicidal and a burden to them.
Plus a design like the one advocated is highly tuned to a specific use. Reuse would require retooling to add Accessor like features anyway.
Programming frameworks should never interfere with the ultimage goal of any programmer, to solve a problem. To that end, a good framework is mindful that it should accept any value, return explicit values, and contain conversion methods for base types.
Absolutely none of the above should come into play if you're simply designing a program to add a bunch of numbers together, but then, why would you use OO for that? It's overkill.
Please, if you're going to do development, add a few more thoughts into your process:
Only experience will teach you that the last two points are universally underestimated, but the answers to those questions will help you determine what approach to take, and that approach may be a simple, uncommented, ten line Perl script.
jr conlin - 8th September 2003 22:04 - #
What the author writes seems to be more about the drawbacks of static typing and how to overcome them, than the problems with certain OO ways of programming.
Yet another reason to use dynamically typed languages ;-)
Peter Bowyer - 9th September 2003 09:53 - #
I suppose I have to treat this as all the "...considered Harmful" articles and assume that while it appears he is making a broad general statement, he is in fact just doing so for effect. He is essentially saying not to use the members of the class to read its state, perform some process from that state and then update the state, not when you could use a method of the class to do the same. Yeah, that's great, and should be obvious. But he makes no distinction between that case, and the case when you just want to know the value of a member so that you can do something totally unrelated with it.
He also seems to saying that a principal of OO is that you can change the Implementation of a class and not affect any of the consumers of that class. But aren't the properties/members of a class part of its Interface?
gilmae - 10th September 2003 06:57 - #
cdn - 10th September 2003 21:53 - #
jr:
Then you've probably got a good idea of OO axioms.
Not under the author's definition. He's arguing against accessors that reveal implementation.
I'm pretty sure that when he writes "user" in the context of code, he means class client. That is, the "user" of the class.
Precisely. The author writes, in the first article, about the issue where frameworks have to be very general, and OO newbies (which would be most IT devs, in my experience) take such framework designs to be ideals, and apply them where they could make a simpler and more specific design.
Yes. Good design is a complicated beastie. That must be why we argue over it so much. ;-)
Peter:Yes, I agree. Smalltalk was typeless, and this makes things much simpler. However, sometimes performance is a main design criteria, and in that case, perhaps a strongly-typed OO language is not so bad, even if the benefits of OO are greatly dimenished.
I've thought previously that it'd be darned nice to be able to declare that some concrete class CClass should intrinsicly define an interface IClass, which would always be a 1 to 1 match for the concrete, but would be available for use in implementing mock objects, or for using when implementaiton inheritance is not desired, but polymorphism with existing concrete classes (perhaps provided in a framework) was desired. No language I know of provides such a facility.
In the end, even this sophisticated approach is a workaround for the burden of strong typing on the OO paradigm. We'd be arguing about something else, then. ;-)
gilmae:When would you want to ask about some state, and do something completely unrelated to that state?
Yes, they certainly are. But minimized coupling is useful.
I think his point is that implementation inheritance is not always the best design (quick and sexy though it is), when you hold loose coupling to be a design goal. I think the title is very poorly chosen, given the moderate and useful advice contained.
All in all, I was quite happy with the articles. They illuminated some things that I'd already been slowly discovering on my own. Besides the advice, I thought the tone of the writing was quite good. I'll look forward to his writing in the future.
For those to whom C++ or Java are the only languages they've attempted OO with, I suggest getting a taste of Smalltalk. Squeak is free, and if you can get past the cutesy UI and look at the classes that come with the library, I think you'll get a lot out of it. I know I am. :)
Jeremy Dunck - 10th September 2003 23:11 - #
Trivial example: I have a Person class, I am using it to generate form letters, I can use a method getAddress that hands me a formatted string containing all the addressing members, but I also need the Postcode member of that address seperately. What I need to do with that Postcode is totally unrelated to the Member class itself, I am using it to address a letter in the way demanded by the business rules of a third-party, non-consumer of the class. I think the solution in "Accessors are Evil" is for the Person class to hand off a struct representing the Address to avoid breaking consumers of the class when you change the typeof the members, but in the end, after you put in structs and get the consumers to access the data from these structs, the consumers are still accessing primitives from that struct. If the consumer is expecting an int16 for the Id field, and you have changed it to an int32 and a char[], the consumer is going to break no matter if it is getting the members from the class itself, or from an abstract data type.
I seemed to missing some vital experience, I think. Until I get that experience, enlightenment will not be mine
gilmae - 10th September 2003 23:51 - #
How about a Person having an Address object, which can also be used by a Letter object? A PersonMailer object would take a collection of People objects (or perhaps even take an iterator over a group of People), and call a MakeLetterLabel method on the Address object for that Person. It'd then call Mail on the Letter object, passing in the LetterLabel.
In Letter.Mail, it might call a Print method on the LetterLabel object. At that point, a printer puts ink on paper. No object knows anything about how addresses were stored by people, nor what how exactly a label needs to be printed, and so on.
The Law Of Demeter, and Single Responsibility Principle are key.
What business does a person have formatting an address? What business does a letter have asking a person for an address? What business does a letter have knowing the addressing schemes for proper labelling? And yet, you can get the job done by telling an object to do something, instead of asking for information.
CRCs really will bring this sort of thing to light.
Of course, a nice bunch of unit tests and refactoring tools will go a long way to supporting flexibility, even if these principles are not solidly followed.
And, of course, functional programming avoids a lot of this complexity. If only we weren't on Von Neumann. ;-)
Jeremy Dunck - 11th September 2003 05:27 - #
gilmae - 11th September 2003 08:03 - #