in reply to Re: Re^5: OO Getters/Setters
in thread OO Getters/Setters

From my point of view, there are very few times when you would want to expose member variables (a.k.a attributes) to the public

But if you're blindly adding accessors/mutators, you might has well have made them public. This isn't a blanket ban on accessors/mutators, but a note that people should be more careful about adding them.

You spoke of tied scalars etc, but by that argument you invalidated your previous argument over performance.

Perl's objects are already slow. In any case, my tied scalar example was nothing more than an example. It demonstrates that $obj->attr and $obj->{attr} are only different in syntax, since you could use a tied scalar to preform exactly the same operations that an accessor/mutator could do. I don't recommend either approach, though, but rather eliminating accessors/mutators all together whenever possible.

Which of these is a better object for a vending machine?

sub money . . . # Accessor/mutator to how much money is in the machine sub get_drink . . . # Spit a drink out

Or this, which avoids mutators entirely:

sub insert_coin . . . # Insert a coin (quarter, dime, whatever) into m +achine sub money . . . # Accessor to how much money is in the machine sub get_drink . . . # Spit a drink out

The second one will restrict what kind of money you can put it and adds that coin's value to the internal attribute. It is not a mutator, because it doesn't provide direct access to that attribute. money is an accesor in either case, but I don't consider it a problem, since the user needs some way of figuring out how much money is in the machine so they can insert more if need be. get_drink has the same implementation in either case, and would only spit a drink out if there was enough money in the machine.

The second interface is more complex, but it's a better design because we've abstracted the operation of inserting coins into the machine. We can subclass it to take other kinds of coins or to take cash (though insert_coin wouldn't be the best name in that case). The point is that you've restricted what kind of data can go in, which the first class would have a much harder time doing.

----
I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
-- Schemer

: () { :|:& };:

Note: All code is untested, unless otherwise stated

Replies are listed 'Best First'.
Re: Re: Re: Re^5: OO Getters/Setters
by linux454 (Pilgrim) on Jan 02, 2004 at 20:00 UTC
    But if you're blindly adding accessors/mutators, you might has well have made them public.

    Not quite sure what you mean by blindly here. As for making them public, the whole point of encapsulation is to hide implementation with an interface. Thus why should I make them public.

    This isn't a blanket ban on accessors/mutators, but a note that people should be more careful about adding them.

    More careful? How so? I would say that a/m should be used for all attributes (a.k.a. member variables) that contribute to the public interface in the least. I prefer to use the encapsulation within my objects as well, so that I can make changes internal to the object and not have to change code elsewhere. I admit sometimes this is unavoidable, or I break this pattern internally

    The second one will restrict what kind of money you can put it and adds that coin's value to the internal attribute. It is not a mutator, because it doesn't provide direct access to that attribute.

    I don't know where you came up with this idea, that a mutator may only set a member variable directly without processing, but you need to leave that definition behind. The job of a mutator is just that to mutate values. A mutator is most certainly allowed, and in fact is its job, to take input and decide what to do with it. The mutator may decide not to mutate b/c of invalid input, or it may recognize several different types of input, and be able make intelligent decisions on what to store in the object.

    For example, say we have a temperature object, internally we store the temperature in degrees celcius; However, we have a mutator (temperature [or as I prefer setTemperature, but to each his own]) that will accept temperatures in either celcius, farenheit, and perhaps other scales as well. The mutator takes the input and then does the conversion to celcius before storing the data into the object. If we took your approach then a farenheit temperature may be stored into the object, and then calculations further down the chain will get really messed up. So the solution to your method would be to create a conversion routine that you would have to pass the foreign value through and then pass the celcius temp back to the mutator. This what your telling us? You can't possibly believe that is a cleaner implementation. If you want to be pedantic, fine, but you need to realize that pedantry will only get you so far in this industry. This type of pigeon holing of the accessor/mutator definition only leads to spaghetti code, and obfuscated interfaces. I have not once encountered your definition of A/M methods.

    The excuse that "this is a case to use a mutator," is invalid, as we are talking of a methodology. Encapsulation is applicable in the simplest case just as in the most complex. I pity the programmer who truly believes that requirements will never change and writes a program with that attitude, only to find that the requirements have changed, and in the end created much more work for themselves.

      Not quite sure what you mean by blindly here.

      I meant putting an accessor/mutator on every field, or even a large ammount of fields, without putting thought into it.

      As for making them public, the whole point of encapsulation is to hide implementation with an interface. Thus why should I make them public.

      No, encapsulation is the reason why you should avoid them in the first place, or (perhaps better) make them private or protected.

      I don't know where you came up with this idea, that a mutator may only set a member variable directly without processing, but you need to leave that definition behind.

      No, that's the accepted definition in most OO lituature. If your method does something besides direct access, it's not an accessor or mutator.

      . . . we have a mutator (temperature or as I prefer setTemperature, but to each his own) that will accept temperatures in either celcius, farenheit, and perhaps other scales as well . . . If we took your approach then a farenheit temperature may be stored into the object, and then calculations further down the chain will get really messed up. So the solution to your method would be to create a conversion routine that you would have to pass the foreign value through and then pass the celcius temp back to the mutator.

      I was thinking about just such a situation a few days ago, and thought up this solution: the setTemperature (or whatever you call it) method should take an object of type Temperature. The Temperature class provides methods like get_celsius and get_farenheit which do all the conversions for you. Likewise, its constructor would take temperatures in various scales. Methods that set and return complex objects are no more accessors/mutators then those that are doing the complexity themselves, so this method can't be considered a use of accessors/mutators.

      ----
      I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
      -- Schemer

      : () { :|:& };:

      Note: All code is untested, unless otherwise stated

        Of course this is your opinion. You are certainly entitled, as are we all. Though while you are coding in your pedantic world, I'll be over here writing less, easier-to-maintain, easier-to-use, and possibly more efficient OOP code.