in reply to Re^8: Assignable Subroutines
in thread Assignable Subroutines

Why isn't your salary in the database? Why aren't you changing it there?

If your object is simply an access layer above the database (Class::DBI or something along those lines), then the mutator is no longer trivial.

"There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

Replies are listed 'Best First'.
Re^10: Assignable Subroutines
by dragonchild (Archbishop) on Jan 26, 2005 at 14:24 UTC
    How do you change it there? What is the mechanism by which you are going in and fiddling those bits? Presumably, it's some sort of access layer. Presumably, the access layer is made up of objects. And, presumably, you need a mutator.

    Once you want a mutator, you want the syntactic sugar that goes with assignable subroutines.

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

      Which is why I mentioned Class::DBI and similar layers. Once your method is running SQL statements (as opposed to simply changing an internal variable), it's no longer a trivial mutator. Complex mutators are a different beast :)

      "There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

        So the argument is then over trivial versus non-trivial. In my expereince there are no trivial mutators. Even if they seem trivial at the begging of a project they allow you the ability to move to non-trivial implementations later without rewriting all of your code. I've always read that its good practice not to access your data directly anywhere except in your 'trivial' getters and setters. This allows you to move to more complex storage mechanisms in the future.


        ___________
        Eric Hodges
Re^10: Assignable Subroutines
by fergal (Chaplain) on Jan 26, 2005 at 22:20 UTC
    Maybe it is in the DB but I'm not changing it in the database because the part of my program that wants to mutate the Employee doesn't (and shouldn't) need to know about databases. Also, storing it in a DB does not always mean that the mutator handles updating in the DB. If persistence is handled by a framework outside the object then the mutator remains trivial and storage is handled outside of your classes.

    Basically it comes down to the fact that many objects have chunks of them that behave exactly like a record/struct in that they are nothing more than passive fields that get twiddled by outsiders. These plain old fields should still not be part of your public interface because if they need to be something more than just plain old fields in the future you're shafted (unless you're in a language that makes it possible for an object to intercept direct twiddling of it's fields and do fancy stuff - Python's properties for example).

    If you don't provide mutators, even for trivial fields, then you are just storing up pain for the future.

      If you don't provide mutators, even for trivial fields, then you are just storing up pain for the future.

      No, you fix your design so you don't need them in the first place.

      In this case, I would make an "effect" object (I'm sure there is a Pattern for this). An effect stores values that can mutate other values when applied to another object.

      my $employee = Employee->new( salary => 50_000 ); my $effect = Effect->new; $effect->set( salary => 1.02, '*' ); # A 2% raise $effect->set( frobnitz => 1.5, '+' ); # The frobnitz is up $employee->apply_effect( $effect );

      The key here is you don't need to know for certain that a given field is implememented in Employee. The apply_effect method ignores any fields it doesn't recognize (like frobnitz). I'll give you that it is more lines of code than:

      $employee->salary( $employee->salary * 1.02 );

      But it is also a superior form of encapsulation.

      "There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

        What does the following code do?

        my $effect = Effect->new; $effect->set( salary => 1.02, '*' ); # A 2% raise $employee->apply_effect( $effect );
        All one can say it that it might raise the salary by 2% or it might do nothing at all, not even report an error. So now we have a Perl that doesn't give you compile time checking and doesn't give you run time checking either!

        The exact same effect can be achieved by having all your objects inherit from a base class with

        sub AUTOLOAD { # do nothing }
        then go back to using
        $employee->salary( $employee->salary * 1.02 );
        Yes this would be an incredibly bad idea but it's exactly equivalent to using effect objects and it's a hell of a lot easier.

        There are some times when you want missing fields to be ignored but you should make that explicit in your code by doing something like

        $employee->salary( $employee->salary * 1.02 ); # we don't mind if they don't have a frobnitz eval { $employee->frobnitz( $employee->frobnitz + 1.5 ) };

        Problems with effect objects

        • Cumbersome - more code means more bugs
        • Slower
        • Ignoring errors should not be the default

        The only real use I can think of for an effect object is to use it as a callback mechanism. Various people can add effects (or even remove or reorder effects) as the object is passed around, but I still don't see a reason to make it ignore errors by default.

        While I'm at it, a couple of questions. Where is this "no mutators" idea coming from? What exactly is the damage that results from having mutators?

        I've read the Java world article and all it's saying is don't create public mutuators for fields that are for internal use only. Blindingly obvious advice but I can understand why it may have been needed. I imagine the sequence of events in Javaland to be

        • direct field access is bad (ok so far)
        • make accessor methods and change public fields to private (still ok)
        • get in the habit of making all fields private (fine, just remember that some are private because you don't want direct access and some are private because you don't want any access)
        • now that everything is called private, get confused and start adding accessors for all private fields (doh!)

        That last step would probably be performed by newer programmers, copying without understanding, seeing that public fields are not being used at all and that private + accessor seems to be "correct".

      Ahh, I was right about there being a Pattern for this. It's basically a Visitor Pattern.

      "There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

        The visitor pattern is actually just a symptom of only having single dispatch. Have a look at Visitor pattern considered useless to see what you can do in an OO language that can dispatch methods on more than one arguemnt.