Some Common Sense on OOP

A few days ago, I ran across an interesting and commonsensical article on OOP by James Hague over at programming in the twenty-first century. In it he says that while OOP is sometimes useful, it's not a fundamental particle of computing. Too often, he says, OOP is blindly applied to inappropriate problems. Problems that aren't complicated enough to warrant its use and the difficulties that it brings.

It is, as I say, an interesting post and I was going to blog about it in a little more detail but Rob Pike beat me to it with his own post on OOP that makes the same point—and even mentions Hague's post—in a more provocative way. Pike points to an example of OOP run amok that can be characterized only as terrifying. Pike says of it

Local discussion focused on figuring out whether this was a joke or
not. For a while, we felt it had to be even though we knew it wasn't.

I can't begin to describe the horror of it so you'll have to go over to Pike's post and follow the link that he gives.

The problem itself is to determine the operating system you're running on and output a value judgment about it. As Pike says, this is a trivial problem that can be solved easily with a simple table look up in any language. Here it is in Elisp, for example:

(defvar systems '((usg-unix-v "UNIX" "good")
                  (gnu/linux "UNIX" "good")
                  (windows-nt "Windows" "bad")
                  (darwin "Mac OS" "far superior")))

(let ((box (assoc system-type systems)))
  (if box
      (message "This is a %s box and therefore %s." (cadr box) (caddr box))
    (message "This is not a box.")))

Simple, extensible, and not a class to be seen.

This entry was posted in Programming and tagged . Bookmark the permalink.
  • I expanded upon your version, adding the ability to match using regexps or functions.


    (defvar systems '((usg-unix-v "UNIX" "good")
    ;; `string-equal' converts symbols to string automagically
    ((lambda (sys) (string-equal "gnu/linux" sys)) "UNIX" "good")
    ("^windows" "Windows" "bad")
    (darwin "Mac OS" "far superior")))

    (defun assoc-match (key alist)
    "Return non-nil if KEY matches the `car' of an element of ALIST.
    The `car' of each element of ALIST can be a:

    string - Compared to KEY with `string-match'

    function - Passed KEY as an argument. It should return non-nil if
    it considers KEY to match

    symbol - Compared to KEY with `eq'

    The value is actually the first element of ALIST whose `car' matches KEY."
    (when (consp alist)
    (let* ((elt (car alist))
    (elt-cmp (car elt)))
    (if (cond
    ;; `string-match' accepts only strings
    ((stringp elt-cmp) (string-match elt-cmp (format "%s" key)))
    ((functionp elt-cmp) (funcall elt-cmp key))
    ((symbolp elt-cmp) (eq elt-cmp key)))
    elt
    (assoc-match key (cdr alist))))))

    (let ((box (assoc-test system-type systems)))
    (if box
    (message "This is a %s box and therefore %s." (cadr box) (caddr box))
    (message "This is not a box.")))

    Still far simpler than their version.

  • Whoops, I forgot to link to the Gist.

  • Alexander Haeckel

    I actually think this could have been a good example of OOP if the execution wasn't done so badly. The main problem I see here is, that these guys are actually teaching students about programming. They have some ideas on patterns and static typing and try to mold their program logic into the type system. They are bad programmers independent of language or paradigm.

    The problem of the elisp solutions is the reliance on global variables and the exposure of the implementation details and the lack of static typing.

    A reasonable Java implementation of that functionality would look something like this:

    package box;

    import java.util.Map;
    import java.util.HashMap;

    public class Box{

    public static void main(String[] args){
    for(Box b : new Box[]{
    new Box("gnu/linux", "UNIX", "good"),
    new Box("darwin", "Mac OS", "bad"),
    new Box("windows-nt", "Windows", "ugly")
    }){
    Box.Info.addBox(b);
    }

    for(String os : new String[]{
    "gnu/linux",
    "darwin",
    "windows-nt",
    "ios"
    }){
    System.out.println(Box.Info.gradeBox(os));
    }
    }

    public static class Info{
    private static String default_message = "This is not a Box.";
    private static String format = "This is a %s box and therefore %s.";
    private static Map info = new HashMap();

    private Info(){}

    public static void addBox(Box box){
    info.put(box.key, box);
    }

    public static String gradeBox(String key){
    String message = default_message;
    if(info.containsKey(key)){
    Box box = info.get(key);
    message = String.format(format, box.type, box.grade);
    }
    return message;
    }
    }

    private String key;
    private String type;
    private String grade;

    public Box(String key, String type, String grade){
    this.key = key;
    this.type = type;
    this.grade = grade;
    }
    }