Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tell, Don't Ask and Single Responsibility - doing new things with data in a class

I've got a case where "Tell, don't Ask" seems to conflict with the "Single responsibility" principle. I've looked at other discussions on the subject but not yet been able to work out the most appropriate object oriented approach for this situation.

I have a program which reads and manipulates collections of data from various sources. I have created a class to hold and manipulate the data (a "DataSet" class). It includes methods for performing various operations on the datasets, such as comparing two datasets to generate a new one which contains the differences, and writing datasets to a file.

I now want to perform some analysis on a dataset and output the results to a report. My first attempt at coding this interrogates the dataset to extract information from it and then constructs the report but this seems to go against the "Tell, don't ask" principle. So: should I put the analysis methods inside the DataSet class and tell the dataset to analyse itself and generate a report? Does this break the Single Responsibility principle? What if I want to perform other types of analysis in future - by DataSet class could become very bloated with lots of different analysis routines which are nothing to do with its core purpose.

Can anyone suggest the best approach here? Is there a particular design pattern which addresses this issue?

like image 815
Bob Avatar asked May 22 '12 20:05

Bob


1 Answers

Whenever you design software, you always have to balance different principles, because many of them conflict. For instance, the DRY (Don't Repeat Yourself) Principle often conflicts with Single Responsibility Principle, particularly when two things do similar, but not exactly the same thing.

Often times you have to decide which principle is more important and emphasis that principle over another one (although you should try to adhere to as many as possible). Often times, principles work together, sometimes they work against each other.

In this case, Tell Don't Ask works with other principles, such as the Law of Demeter (which despite it's name is still a principle as far as software goes and is better described as the principle of least knowledge).

What the LoD tells us is that a method of an object should only call other methods

  • On Itself
  • On objects passed into it as a parameter
  • Any objects created/instantiated with an object passed by parameter
  • the objects direct component objects
  • Or a global variable

It's not specifically said, but I feel that the order of preference of selection of methods to call should be in that order as well, with global variables being last resort. But, that's neither here nor there.

So, if we combine Tell, Don't Ask with LoD then it's perfectly ok to pass objects to another object for "asking". Meaning, You have an Analysis object which you "Tell" to do something, passing the DataSet object as a parmeter. That's adhering to TDA. Inside the Analysis object's method you're adhering to LoD by only accessing "close friend" data.

This also conforms to SRP, since your DataSet is still just a DataSet and your Analysis object is an Analysis object.

The key take away here is that these principles are often "relativistic". Meaning that, from the perspective of the parent object that gets the data and wants to perform the analysis, you're "telling" the analysis object to do something.

The purpose of TDA is that your parent code should not query your DataSet for its state and then make decisions based on that. It should instead pass objects to other objects and have those objects perform their responsibilities, which may including querying those objects for their state, but that's ok because it's in the context of their responsibility.

Further reference here:

http://pragprog.com/articles/tell-dont-ask

EDIT:

If you would like a more authoritative source, there's no one better than Martin Fowler himself (read towards the end, you'll find this commentary)

http://martinfowler.com/bliki/TellDontAsk.html

But personally, I don't use tell-dont-ask. I do look to co-locate data and behavior, which often leads to similar results. One thing I find troubling about tell-dont-ask is that I've seen it encourage people to become GetterEradicators, seeking to get rid of all query methods. But there are times when objects collaborate effectively by providing information. A good example are objects that take input information and transform it to simplify their clients, such as using EmbeddedDocument. I've seen code get into convolutions of only telling where suitably responsible query methods would simplify matters 1. For me, tell-don't-ask is a stepping stone towards co-locating behavior and data, but I don't find it a point worth highlighting

like image 71
Erik Funkenbusch Avatar answered Oct 01 '22 22:10

Erik Funkenbusch