views:

321

answers:

3

I somehow must have overlooked the "tell, don't ask" OOP principle all these years because I just learned about it a couple days ago for the first time.

But the context was a discussion about validation code that had been moved out of an ASP.NET web form page and into a data/business object, and there was no "Validate()" method, just a save method that itself did validation and (supposedly) raised an exception. I asked why this was designed in this way and I was directed to OOP's "tell, don't ask" principle, which I'd never heard of, so then we looked together at Google and I was immediately educated. ;)

Still, something doesn't smell right, shouldn't data be scrubbed before it gets handed off away from the user and into the business layer where it is processed and/or collected, rather than the other way around? I'm confused as to how this makes for good design.

It seems like the rule of "tell, don't ask" pertains to the idea that you shouldn't ask the target object about the target object's state, and that the principle was never really meant to apply to the data being passed to the target object.

+2  A: 

I thought it sounded like a load of "Best Practices" and "Design Methodologies" gone wrong, but now it kind of makes sense to me. Look at it this way:

Imagine putting validation in the Business Object, but the "what do I do if validation fails' in the presentation layer. This would allow multiple different presentation layers to reuse the same validation logic, but handle errors differently.

public Foo
{
  Validate(Baz bar)
  {
   if(!is_number(bar)) throw numberexception();
  }

  AssignBar(Baz bar)
  {
   Validate(bar);
  }
}


//...

try
{
  foo.AssignBar(bar);
}
catch(numberexception e)
{
  alert('Not a number!');
}

n.b. you can argue all you want about throwing exceptions, it was meant as an example. Return states, bools, whatever you want.

Tom Ritter
+2  A: 

I agree with AviewAview, but would only throw exceptions if the user tells (not if he asks):

public Foo
{
  bool Validate(Baz bar)
  {
        if(!is_number(bar)) return false;
        return true;
  }

  AssignBar(Baz bar)
  {
        if (!Validate(bar)) throw numberexception();
  }
}


//...

try
{
  foo.AssignBar(bar);
}
catch(numberexception e)
{
  alert('Not a number!');
}
Patrick McDonald
A: 

I wonder if this is more an issue of "separation of concerns" than "tell don't ask". Whose responsibility is it to validate the data? Arguably, it's the thing responsible for persisting it.

Of course, sometimes it's useful to validate the data in multiple layers. If this is the case for your app, I have no problem with exposing the validation logic in the "user" layer. But I would still want it in the business layer.

jcrossley3