in reply to Re: Law of Demeter and Class::DBI
in thread Law of Demeter and Class::DBI

"In an OO language you end up with setters and getters because the language can't look"

You get them because the developer put them in. Good OO code does not NEED getters and setters, but is merely code impersonating OO code. Not that non-OO code is wrong, but massive getter/setter combos are definitely wrong.

Replies are listed 'Best First'.
Re^3: Law of Demeter and Class::DBI
by fergal (Chaplain) on Nov 17, 2004 at 11:37 UTC
    You haven't given any examples so I don't really understand what you're saying. Can you give me an example of good OO code that doesn't use getters and setters. Objects have fields (or properties or members). There needs to be a way to set and get them and it needs to be overrideable.

    In Perl for example, you can directly access the underlying hash. Are you saying that this is a good way of doing things?

    I'd say that's a bad way of doing things. This kind of code is tied to the underlying representation of the object and prevents you from reusing the code with other objects that would have had a compatible interface if you'd used getters and setters.

    By the way, when I say getters and setters I include things like Class::Accessor::Lvalue which lets you do

    print $o->some_field; $o->some_field = 5;
    which gives a simple interface while still allowing override of get and set behaviour.
      I agree that accessing the underlying hash is a bad idea, but that's not the point. Among other things, OO is about putting the logic near the data.

      Let's say we want to indicate that a logger object should flush itself to disk when it's appropriate (it may take some time, so we can't do it right away). An example of not so good OO design:

      #in a time sensitive loop $logger->isFlushPending(1); #later on if($logger->isFlushPending) { $logger->flush(); $logger->isFlushPending(0); }

      It's bad OO design to use getters to read the state of an object and then use that information to make decisions about how to use the object. The logic controlling the object is now located outside of the object rather than inside it.

      So you avoid the getters (asking the object what state it has) and instead tell the object what you want it to do and it can sort out how to do that itself.

      #in a time sensitive loop $logger->setFlushPending(); #later on $logger->flushPending();

      and the logic controlling whether to flush the log data to disk is now implemented in the flushPending() method. Inside the object, not outside.

      See also: TellDontAsk

      /J

        I largely agree* with the principle of Tell Don't Ask. It's basically saying that where possible you should not expose the field. This is fair enough but some objects require exposed fields. For example an Employee has a Salary field which you should be able to set and get. There's no way around that. The fact that it is a get/settable field is a fundamental part of the interface for an Employee.

        What I'm saying is that in the situation where you must expose a field then it should be exposed in a way that allows you to control and customise the sets and gets and in Perl and most other languages that means having getters and setters.

        The original post that I replied to said

        Good OO code does not NEED getters and setters

        The only way you can write OO code without getters and setters is to either have no fields or to have fields but access them directly. The first is impossible (applying Tell Don't Ask might give you less fields but can't eliminate them all) and the second is a bad idea as you said yourself.

        * I think the idea of putting the logic beside the data is ok up to a point but I'm really starting to wish for good multimethods in Perl. In the example you gave, it's possible that your log flushing method would be useful for many types of object (basically any object that implemented isFlushPending and flush). So attaching it to a particular class means that all these objects which would find it useful will have to inherit from this class. This brings in the perils of multiple inheritance (perilous in most languages anyway).

        I've wondered about this - when a behaviour you want hasn't been given to the object yet do you give that object the method that implements the behaviour or wrap it somehow? If you wrap it, how do you avoid having a zillion custom namespaces and having to have mental models of all those layers? I've recently taken to just directly giving these methods directly to the object but I wonder how you'd handle this.