Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??

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; }

In reply to A novice's thoughts on applying Demeter to extension code by diotalevi

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (2)
As of 2024-04-26 03:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found