Slashdot Mirror


Holub on Patterns

James Edward Gray II writes "Apress sent me a copy of Holub on Patterns for review, and for that I'm extremely grateful, because this is a gem of a book I would not have liked to miss. Odds are, most object-oriented programmers will feel the same, so allow me to share the highlights." Read on for the rest of Gray's review. Holub on Patterns: Learning Design Patterns by Looking at Code author Allen Holub pages 414 publisher Apress rating 9 reviewer James Edward Gray II ISBN 159059388X summary Design Patterns taught through Real World Programs.

If I can level any complaint against this book, it's probably that the title doesn't properly convey the goodness locked within. Holub on Patterns is short for Allen Holub on Design Patterns. Allen Holub is a long time expert on Design and Design Patterns, so he's the man you want to learn it from. Still, if I could name this book, it would be Object Oriented Design Voodoo. (Note: This is probably why I don't work for Apress or any other publisher.)

The book's subtitle is "Learning Design Patterns by Looking at Code." That probably conveys the work's focus a little better and it also gives away one of the book's best features: sensational examples. (These examples are in Java, another area where Holub is a well-known authority, but the concepts taught apply to Object Oriented Programming in any language.)

Titles aside, this book really is the best work I've read on design patterns. If you don't already know, design patterns are the recurring patterns of object-oriented software implementations. Luckily, you don't have to know anything about them to read this book. The author covers many patterns in rich detail from the beginning. Even if you do know your design patterns well, I'll wager Holub still has a trick or two to impress you with.

Holub discusses patterns in their ideal pure form, but much more importantly he shows them as they occur "in the wild," with multiple variations. He covers the downside of each pattern, weights the trade-offs of using them, and even gives a handful of cases where he felt they were impractical. He does all this right in the middle of complex real-world examples so you can see each point he's making. That's actual programming, folks. The good, the bad and the choices we programmers make are well presented, and that's rare in a programming text.

The book opens with two chapters that more or less cover why we need design patterns at all. Did you know getters/setters are bad? Did you know that subclassing is dangerous? If you said No to either question, you need this book and these two chapters in particular will get you up to speed on good OO practices. This section of the book is mostly theory, light on examples.

The next two chapters (covering over 250 pages) make up the heart of the book. Holub examines two examples in exhaustive detail. The first is his implementation of The Game of Life. You've probably implemented that on your TI calculator, but Holub sure didn't. He admits that his implementation is "Toy Code," but it's a robust example that involves eleven design patterns. The second example is production code, a mini database complete with SQL interpreter. This code is also swimming in pattern usage, and Holub gives you the guided tour.

I've already said these examples are great, but that claim begs some elaboration. First, we're talking about hundreds of lines of code in many of these listings. These aren't the usual contrived junk. What's more, one class may be participating in multiple patterns. Making any sense of these examples would be almost impossible if the author wasn't flawless in explaining the key points and always dropping hints about what you need to notice. This isn't light reading. It's work, but the rewards are there and it'll pay off if you really spend the effort to understand how the code works.

Finally, the book closes with an appendix that gives more typical recipe-card style listings of all the design patterns discussed throughout the text. This is a nice reference after you've finished the tricky stuff. If you're new to design patterns, you might start here, before the book throws you into the lion's den with its massive examples.

Just in case I haven't sold you on this title yet, I better mention the gorgeous hard back binding. Brilliant and sexy. How can you beat that?

Holub on Patterns is a very approachable way to learn a lot about design patterns. If you already know how much patterns can improve your object-oriented programming, you'll really enjoy Holub's presentation of the topic. If you don't yet grasp Design Patterns or haven't enjoyed other works on the subject, you'll just have to trust me: You want this book.

You can purchase Holub on Patterns: Learning Design Patterns by Looking at Code from bn.com. Slashdot welcomes readers' book reviews -- to see your own review here, read the book review guidelines, then visit the submission page.

26 of 211 comments (clear)

  1. Re:Getters/setters bad? by Anonymous Coward · · Score: 2, Insightful

    Under the hood, actuators and mutators are method calls (at least in C#), but if the property is simple enough, there's no reason it can't be treated like simple field access.

    Properties allow you to wrap your fields with business logic and validation, so I don't see what's so bad about that.

  2. Re:Getters/setters bad? by Misch · · Score: 4, Insightful

    Can someone explain why accessor and mutator methods (I assume this is what he means by "getters/setters") are bad?

    My guess is that in some instances, publically accessible getter/setter methods can be construed to be "exposing the underlying implementation" of a class. Of course, that just means you need to judiciously use getter and setter methods.

    That's my guess at least. I suppose I should read the book.

    --

    --You will rephrase your request for me to go to hell. Goto statements are not acceptable programming constructs
  3. Re:Getters/setters bad? by fizban · · Score: 2, Insightful

    In the OO world, objects come in two basic flavors: data objects and interface objects.

    Data objects are just groupings of data. The member variables should be public and accessible to it's users.

    Interface objects, however, encapsulate their data. They don't require the user to know about their internal data members and only provide methods members that the outside world can use to perform actions with that object.

    In the first case, you don't need getters and setters because the members are already public.

    In the second case, you shouldn't provide getters and setters because you're breaking the encapsulation.

    If you have classes with lots of getters/setters, then those classes are really just data objects and you should just make the members public and save yourself a bunch of typing.

    --

    +1 Insightful, -1 Troll. What can I say, I'm an Insightful Troll.

  4. Re:Getters/setters bad? by Greyfox · · Score: 3, Insightful
    Object Oriented programming is all about hiding data. You expose just the data you need through very specific interfaces. If you have an object that has getters and setters for every data element in the object then you're still exposing the internal workings of that object and should reconsider your design.

    Of course, being a programmer also means knowing when to break the rules, and there will be some times when you can't avoid using them. IIRC, a lot of Java stuff requires them. I prefer not to use Java if I can avoid it.

    --

    I'm trying to teach myself to set people on fire with my mind... Is it hot in here?

  5. Re:Getters/setters bad? by Dormann · · Score: 3, Insightful
    Read it again. He says that sub-classing is dangerous, not bad.

    In the same sense that using a chainsaw is dangerous, but not necessarily bad.

  6. Re:Getters/setters bad? by Anonymous Coward · · Score: 2, Insightful

    Nice how everyone who replied thus far failed to mention the Uniform Access Principle. Google it. That's why mutators/accessors are generally a good idea.

    Best practices may be generally preferable to other practices, but won't apply in every situation. Be pragmatic people!

    And to those of you talking about 'data classes', you're betraying a C-like heritage where data resided in structs or unions because of language limitations. Please keep your OO misconceptions to yourselves!

  7. Re:Getters/setters bad? by emiddlec · · Score: 4, Insightful
    The article Why getter and setter methods are evil (from above) includes the following:

    1. A fundamental precept of OO systems is that an object should not expose any of its implementation details. This way, you can change the implementation without changing the code that uses the object. It follows then that in OO systems you should avoid getter and setter functions since they mostly provide access to implementation details.

    Apparently the argument against getter / setter functions goes..

    1. OO systems should not expose implementation
    2. Getter and setter functions mostly expose access to implementation details
    3. Therefore, OO systems should avoid getter and setter functions

    While the logic is sound, I think that item #2 is debatable.. If you design an object and mindlessly add get/set functions for every piece of private data in the object, then you're probably guilty of exposing the implementation. But if you design the object's public interface first, and decide on the private data afterwards, I would guess that you're probably in the clear to have used get/set functions "correctly." IMO it's not the functions themselves that are the problem, but rather the adherence to correct design principles.

  8. Re:Getters/setters bad? by Anonymous Coward · · Score: 1, Insightful

    yes but then you are assfucked if you want to trigger an event when that value is changed. hence setters are good here. something like event ValueChanged

  9. Re:Bring back procedural languages by GlassHeart · · Score: 2, Insightful
    Just because code isn't object-oriented doesn't mean it's bad code, and anybody who tells you otherwise is just being dogmatic. However, there's a good chance that such code is less well-isolated from the rest of the system, and your one-liner fixes can more easily introduce unintended consequences.

    One common pattern with maintained code is the cancerous growth of special cases to deal with new requirements. Over time, the special cases dwarf the original code, and it becomes very hard to even figure out what it's supposed to be doing.

    Yes, OO doesn't solve all problems, yet "procedural languages"* are also well-known to have many problems of its own. OO is also easy to get wrong, as evidenced by your "have to throw them away periodically" observation. However, I can't help but feel that you're making conclusions based on limited and anecdotal evidence of failures.

    * Many OO languages are procedural.

  10. Re:Getters/setters bad? by GuyWithLag · · Score: 2, Insightful

    Funny, I allways thought that GUI widgets were THE example of OO-ness....

  11. Re:Bring back procedural languages by guitaristx · · Score: 2, Insightful

    OOP is a wonderful thing because it enforces tons of those wonderful principles that are taught in Computer Science classes 'round the globe: encapsulation, abstraction, data hiding, "black-box" programming, etc. OOP is NOT supposed to be the end-all, be-all of programming, just as the creation of C and other higher-level programming languages was not to be the death of assembly programming.

    What, so often, people fail to realize is that software is designed to be built in layers. See the TCP/IP stack, for instance. This allows for one of those wonderful OOP principles to work - abstraction. If the nature of the OOP system/API/et. al. is causing you more problems than it's solving, then I say it's not OOP's fault, it's the designer's fault. I don't believe that OOP is the solution to all problems - some problems are square, and some programming paradigms are round. Problems arise not because of the evil nature of OOP, but because someone tried to pound a square peg into a round hole. OO languages and systems give you the ability to make things object-oriented, but none that I've seen prevent you from using procedural programming techniques when your square programming problem doesn't fit nicely into a round OO hole.

    Generally, I've found that people who hate OOP were forced into using it without getting enough exposure to it to appreciate it. Nobody's gotten rid of procedural programming, just like nobody's gotten rid of assembly programming. We just have higher-level tools and paradigms for dealing with high-level programming tasks.

    --
    I pity the foo that isn't metasyntactic
  12. Should be titled "Holub on Java Patterns" by pammon · · Score: 4, Insightful

    The dirty unacknowledged secret of design patterns is that they're strongly coupled to a language. For example, switching from a statically typed language like Java to a dynamic one with forwarding substantially changes the approach to Factory, Proxy, Singleton, Observer and others. In fact, they're often rendered trivial. The claim that the approaches described in the book apply to any language is just not true. These patterns are for Java and Java-like languages.

    1. Re:Should be titled "Holub on Java Patterns" by pammon · · Score: 2, Insightful

      Patterns are often applied as a substitute for missing language features. Using the Decorator pattern makes little sense if you can add methods and variables to individual objects (like in Python or Self).

  13. Re:Getters/setters bad? by chromatic · · Score: 3, Insightful

    Polymorphism a the building block of OO, not inheritance. It's a shame that so few popular languages make this clear.

  14. Re:Getters/setters bad? by philci52 · · Score: 5, Insightful

    In the first case, you don't need getters and setters because the members are already public.

    Having public members is generally a bad idea and gives me nightmares of old C structures. Here is the reason that you should use set/get methods:

    1. Debugging - Try and trace a variable every time it is set when the variables are public. Wonder why you can't figure where it is going wrong? Adding 100 break points? Using set/get here can save hours of debugging.

    2. No loss of Speed - most compilers will optomize your set/get functions if they are inline, so there is no performance penalty (atleast for c++).

    3. Maintainance - Suppose that when data member A is updated, now a count needs to be kept. Using a setter function allows you to change code in one 1 place. Also, suppose a variable type changes from an int to a double. You can still keep around the integer setters/getters for older classes and use new accessor for the new methods for objects that need it.

    From the article, its not that setters/getters are bad themselves, but that overuse of them is bad. Here is the key quote: Don't ask for the information you need to do the work; ask the object that has the information to do the work for you.

  15. Re:Getters/setters bad? by fupeg · · Score: 2, Insightful

    Your "data object" is not an object at all. Read the most basic definition. Your "data object" is exactly what Holub is talking about as being "evil." Your "data object" is a struct, and you are being procedural, not object oriented. The whole point of OO is to combine these two things into an object. Accessors violate this paradigm. In a good OO system, other objects never ask for information about other objects, they simply ask the other object to perform tasks.

  16. Re:Getters/setters bad? by marms · · Score: 3, Insightful

    An obvious reason not to use "public" members in Data Objects is if the member must be validated to prevent illegal values from being assigned. Or if changing one member will automatically cause other members to be changed. Or if the internal encoding of an object differs from the external representation. Or if sychronization or transactions are needed. Or to prevent access/mod by certain classes or users or under other circumstances. Need I go on? Yes, there are times when using "public" members is fine (think: simple code). But there are a multitude of reasons not to. YMMV.

  17. Re:Getters/setters bad? by TheSunborn · · Score: 3, Insightful

    (Talking about the way java does things)

    But you don't code to the interface(api) of a HashTable. You code to the Map interface, which is described as: "
    An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value."

    And you don't(Should not) give a HashTable to a method. Give it a Map insted. The only place in your code where the exact type matters, is where you create the object.

    That most people call them hashtables insted of associative array(or maps) are because they mix implementation and interface.

    I once did implement a Map using a linked list. Not effective, but a nice way to show the difference between interface and implementation.

  18. Re:Getters/setters bad? by killjoe · · Score: 3, Insightful

    It looks like we have come full circle. Nowadays it's fashionable to create value objects which are pretty much like the hashes and recordsets of the old days. Make value objects with public attributes, make action objects that take the value objects and maipulate them.

    As I said just like the old days.

    --
    evil is as evil does
  19. Re:Getters/setters bad? by KyleCordes · · Score: 2, Insightful

    I usually say something like this:

    Map someMapINeed = new HashMap();

    so that the implementation chosen (HashMap, the Java 1.2+ expression of the general idea of a hash table) is present only at that one spot, the rest of the code doesn't care about the Hashness of it, it just uses it as a Map.

    It is a common "smell" in Java code, to refer to something specific (HashMap) in a parameter list (most commonly) when you only need the generic.

  20. Re:Getters/setters bad? by kpat154 · · Score: 2, Insightful

    Ok, this post completely misses the point. You don't provide getters/setters to insulate the client from functionality - you provide getters/setters to insulate the client from change.

    Change is inevitable. You've got to plan for it. So, 6 months down the road when you realize that you need to change this requirement and add some functionality you won't be able to because you've directly exposed the member without hiding it behind a getter/setter.

  21. Re:Another pompous "expert"? by MarkusQ · · Score: 3, Insightful

    I think you are missing his point. In ruby, as in SmallTalk, or any other real OO language, "+=" is just a message (in this case, one that is automatically defined when you define "+"), to say that it's better or worse than another semantically equivalent message is just silly.

    Yes, they are semantically distinct in Java, but that is a statement about Java, not about OO programming.

    -- MarkusQ

  22. Re:Getters/setters bad? by Greyfox · · Score: 2, Insightful
    Check out the specs for anything with "bean" in the name, and also java data objects. Pretty much anywhere you get into pushing data around, the spec will call for getters and setters.

    Having the IDE create them automatically is going the wrong way, encouraging bad programming habits and illustrating the IDE designer's failure to comprehend the nature of object oriented design.

    --

    I'm trying to teach myself to set people on fire with my mind... Is it hot in here?

  23. Re:Getters/setters bad? by Canberra+Bob · · Score: 2, Insightful

    "In a good OO system, other objects never ask for information about other objects, they simply ask the other object to perform tasks"

    At some stage you will have to get some data from an object. Very simple example, lets say you are writing up a shopping cart. Add item objects to the cart. How will this cart keep track of the subtotal without querying the item objects as to their price?
    OK, we have:
    cart.add(item);
    At some stage the cart object will have to call: item.getPrice();
    Very simple example, but I am curious how this shopping cart would operate if the item was never queried to find out its price?

  24. More-OO-than-thou religious fanatics by Latent+Heat · · Score: 3, Insightful
    First it was this Law of Demeter business where you weren't supposed to invoke a method of an object you retrieved from another object and you were to either implement gobs of forwarding methods or implement "visitor" objects to do the forwarding or some such thing.

    Then there was the "inheritance is bad" deal where everything is to be done with composition and you have to write gobs of forwarding methods between the object and the object it contains.

    Now there is this "Get/Set is harmful" -- no, "Evil" I say because this is a matter of religion. So what are you supposed to do when you need to get some representation of state out of an object -- to display it? Can you do a getStateValue()? Oh, no! You have to hand that object an AWT graphics context object and have the object render itself. So much for reusing that object outside of Java.

    Or in another Golub article referenced on this topic, you are not supposed to have set functions to initialize an object to a required state -- you are supposed to pass your object a "Visitor" or "Strategy" object that supplies the state -- through what? An interface with a whole raft of get functions?

    Suppose the approach was, "Do you have a whole lot of get methods on an object? Why are they there? Is it because you need to retrieve the state of an object to print it out? Have you considered giving your object a Print() method and getting rid of all of the get methods? Are you concerned that your object is now hard-wired into a particular print driver? Have you considered implementing an abstract print interface and implementing void Print(IPrintInterface my_printer) as the Print method?"

    But no. The approach is that get/set is "evil" or "smells bad" or some such thing. An object with get/set is a kind of shame and you have to go to extreme contortions in your code, spinning off bunches of classes you never needed before to avoid the embarrassment of having get/set.

  25. Draw thyself?! by Anonymous+Brave+Guy · · Score: 2, Insightful
    So what are you supposed to do when you need to get some representation of state out of an object -- to display it? Can you do a getStateValue()? Oh, no! You have to hand that object an AWT graphics context object and have the object render itself.

    When I first read that, I assumed you were joking, and had just made that up as an absurd example to exaggerate the author's point. Then I read TFA, and realised that Holub himself advocated exactly that. Talk about getting your priorities completely wrong!

    Getters and setters are not "evil". They are a design choice, as Holub himself acknowledges. If you have a lot of these for a particular class, this is often symptomatic of a confused design, but the author missed the point here. The bits you're supposed to hide are the individual aspects of an object's state that must satisfy various invariant conditions. It doesn't make sense to change these independently, because you might violate that invariant. Hence, you only provide operations on the class that change them collectively such that they still satisfy the required invariant conditions.

    However, if something can be changed in isolation without violating any invariants, there's no harm in providing a mutator method to do it. That applies up to and including classes with no invariants ("structs") where you can sensibly modify any aspect of the state in isolation. Moreover, if some aspect of state has a meaning in isolation, there is no harm in providing an accessor method to look up that state, even if you wouldn't allow it to be changed directly because of invariant constraints.

    Confusing this with the idea that an object should be the only thing that can do anything with itself (such as drawing itself on a screen) demonstrates a spectacular failure to understand the underlying issues here. In fact, it's easy to show this: consider an operation where two or more user-defined types are involved, and you can't restrict all the knowledge about each object to that object and still perform the operation. Attempting to do so often results in exactly the problem with Holub's "draw thyself" example: it tightly binds two separate subsystems, forcing one class to live in both of them, with all the attendant spaghettiness that brings.

    Now, Holub clearly realises that mixing UI code with business logic has unfortunate problems. He can protest about inner classes and facade patterns all he likes, but the fundamental design concern -- coupling the UI system to a data-handling system -- is still there. The only way to break the bad coupling is to separate out the facade code or whatever from the original class, and put it in a UI-based layer where it belongs. Of course, that probably requires using some form of accessor methods, which takes up back to where we started.

    Who is this guy anyway, and why do some people here think he's good at this stuff? I honestly haven't heard of him before, and I'm fairly open-minded, but I'm not forming a good impression from reading a couple of his articles that people have cited in this thread.

    --
    If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.