http://www.perlmonks.org?node_id=409709

In reading Re^4: Law of Demeter and Class::DBI, I re-learned that keeping the logic near the data is a good thing. In Re^5: Law of Demeter and Class::DBI I asked how a user is supposed to attach new logic that the author of the class did not provide. There are a few ways to do this and I'm seeking your thoughts on when each alternative is more appropriate. I'm starting with the idea that putting the new logic into my own package is a poor idea because that immediately violates the principal I'm trying to understand.

How do famously popular OO languages like Java, C#, .Net, and VB handle these problems?

The rule of thumb I've learned is: Tell, don't ask. I'll quote from jplindstrom in Re^4: Law of Demeter and Class::DBI for a moment because I don't want you to decide not to read this part because it is on another page.

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.

-- jplindstrom

Assume I'm going to use this logger object and that it implements the first interface but I want to write my own code cleanly. This task probably occurs to other objects which implement reasonable APIs but for some reason just don't have the logic that I need them to. Perhaps I have an Employee object but I would like to have some logic about whether the salary is above average. This is a domain for business-level logic to show up all over the place. It won't exist in object to start with and it has to be put somewhere.

Assume also that I have a context of "here" which is some code that is using the object and a Rule of Demeter violation is involved.

Please consider the following alternates or any others you find worthwhile. I'm obviously inexpert regarding the Rule of Demeter so my perspective on what things I can and should be doing is limited. I have ordered this list by things so that the things I wish to mention and dismiss are at the start and the better solutions are at the end.

Ignore Demeter

I will write code like $Employee->Manager->CostCenter and just not worry about it. This is appealing because this is what I already do and I haven't found a practical reason to stop doing this. I've never been bitten by the problems the Rule of Demeter is designed to protect against.

Wrap Demeter in a local function

I will write a locally known function which does the double dereference for me. I've now polluted my local namespace

sub main::ManagersCostCenter::{ my $Employee = shift; return $Employee->Manager->CostCenter; }

Subclass Employee to add the function

Now I have to remember that MyEmployee is just like Employee except that I've added a property or two to it. I don't like this because there may be some other code that subclasses Employee and now I have to remember an entirely new package name. When I have multiple places to solve this problem, I end up with lots of subclassing.

I don't like it because now I have to remember more things.

package MyEmployee; @ISA = 'Employee'; sub ManagersCostCenter { $self->Manager->CostCenter; }

Delegate

This is cleaner in logic than subclassing but doesn't prevent me from having to memorize this additional class and how it relates to Employee. It also means I now have to deal with AUTOLOAD and how much of Employee's interface MyEmployee is going to have to know about.

package MyEmployee; sub ManagersCostCenter { $self->Manager->CostCenter; } sub new { ... } sub can { ... } sub AUTOLOAD { ... }

Add the logic to the class

This is appealing because I won't have to consult with the author of the class, don't have to do complicated logic to redispatch methods, haven't played and ISA games, and it looks transparent to my user-level code. This has a namespace collision problem Employee's interface changes and adds the same function. This also enables polymorphism so if there are other unrelated objects which also implement this method, this class benefits.

sub Employee::ManagersCostCenter { $Employee->Manager->CostCenter; }

Add the logic to a parallel namespace

This is also appealing but it breaks polymorphism because to get this function I have to use a special calling convention and any polymorphism-using code isn't going to know about that.

$Employee->My::Employee::ManagersCostCenter; sub My::Employee::ManagersCostCenter { $Employee->Manager->CostCenter; }