http://perlalchemy.blogspot.com/2010/08/objecttinyrw-and-moosexnonmoose.html
Zbigniew Lukasiak tries out Object::Tiny and wonders why it is that I didn't allow for the creation of setters when it is only a one line change.
Like most ::Tiny modules the reason is a bit complex and the result of compromises.
Object::Tiny began as an attempt to create a lighter, faster, version of Class::Accessor. A way to bulk-generate the accessor code I had to type over and over again.
However, where I differ is a strong preference for light and elegant API design.
And so I decided to implement mine with as little implementation code as possible, and as little API code as possible.
Once you have decided to go down the simplicity path, there's a couple of standard techniques you often end up using.
The first and most important is state reduction.
In their introduction to Erlang, the founders of that language describe state as one of the main sources of failures in programs. And so anything that removes state, at the very least unnecessary state, is a positive. Especially if the state reduction also results in code reduction, and a reduction in computation.
So take the following example, where we create an object with some attributes and then run some code that will use those object attributes..
my $object = Class->new; $object->foo(1); $object->bar(2); $object->do_something;This is a use case that we see fairly often, but it's really quite horrible code. It is really only the object-oriented equivalent of something like the following.
our $Object::foo = 1; our $Object::bar = 2; do_something('Object');It is especially bad code if the following code would throw an exception.
my $object = Class->new; $object->do_something;If this blows up, then you are REALLY doing something wrong, because you have allowed the creation of completely invalid objects. Now anybody taking one of these objects as a parameters needs to do with following.
sub foo { my $object = shift; unless ( $object->isa('Class') and defined $object->foo and $object->foo > 0 and defined $object->bar and $object->bar > 2 ) { die "Invalid object"; } }If you are going to create an object for something, you HAVE to be sure that the objects are trustworthy.
my $object = Class->new( foo => 1, bar => 2, ); $object->do_something;Less state, less complexity, less CPU, and less bugs.
use Class qw{ foo bar };By contrast, if we want to allow mixed readonly and readwrite, we would need some way of distinguishing. Something like the following.
use Class { readonly => [ 'foo' ], readwrite => [ 'bar' ], };No matter how you try, there's always an inherent additional element of complexity that results from the split between them.
use Class [ qw{ foo } ], [ { bar } ];
use Class { foo => 'ro', bar => 'rw', };
A comparative study was performed to assess how professional programmers use APIs with required parameters in objects' constructors as opposed to parameterless "default" constructors. It was hypothesized that required parameters would create more usable and self- documenting APIs by guiding programmers toward the correct use of objects and preventing errors. However, in the study, it was found that, contrary to expectations, programmers strongly preferred and were more effective with APIs that did not require constructor parameters.
Re:Immutable objects
hdp on 2010-08-15T13:41:25
That article's irrelevant.
1) We're talking about named constructor arguments vs. attribute setters, not positional constructor arguments vs. attribute setters; we already know that named > positional for readability, maintainability, etc. in most cases. OK, now we also know that that's even true if the named arguments are spread out over several lines of code. Big deal.
2) How people interact with constructor arguments has absolutely nothing to do with the potential complexity of your objects introduced by letting attributes be changed not only immediately after construction but also 5 packages away and 12 scopes up.