An Extension To TellDontAsk
More than five years go, Martin Fowler pinpointed one of the biggest problems in Object-Oriented Programming in his famous TellDontAsk article. In his writing, he reminded programmers that they should trust their objects with performing the work for them, rather than asking the objects to provide the data that they would later work with themselves.
This is something that I very much agree with but, of course, this principle alone won’t guarantee that our code is object-oriented. I think it is not enough to trust an object with doing the work – some more design effort is needed in order to ensure that said object won’t cause procedural code later on.
Let’s see an example:
/** * All employees of a company. * You can hire more, fire some of them, give them a raise etc. \*/ public final class AllEmployees implements Employees { //constructor //other methods from Employees interface (hire, fire, raise etc) @Override public List<Employee> filter(final Map<String, String> skills) { //return the List of those who have the specified skills. } }
The above class will create a proper object which respects Mr. Fowler’s principle: it will take care of the employees and it will even filter them for us, no questions asked. However, it may will cause some damage around it and here why: once we perform a filtering, we are left with a List
which is discriminating everyone!
Are those filtered employees never going to get a raise? Will they never be fired or we will never hire someone with the same skills (same filters)? Of course we will still want to give raises, fire or hire someone similar, but we are now out of the context, we now have just a dumb List
in our hands: in order for the employees on this list to have the same rights and obligations as the rest, we will need to write procedural code (maybe a lot of code).
Here is what I think we should do: we should add a new implementation of Employees
, called FilteredEmployees
, which would take that Map in its constructor and make sure that it only handles employees who have the skills that we asked for. This way, they still work for the same company and nothing has changed aside from the fact that now we know them better, we know they have some skills that others don’t. We won’t have to write code to handle or transform a List
, we will still have an instance of Employees
. Now our class looks like this:
/** * All employees of a company. * You can hire more, fire some of them, give them a raise etc. \*/ public final class AllEmployees implements Employees { //constructor //other methods from Employees interface (hire, fire, raise etc) @Override public Employees filter(final Map<String, String> skills) { return new FilteredEmployees(..., skills); } }
I would say the idea is to try to implement the situation itself rather than tell an object to lead you to said situation. That is, we implemented those filtered employees because the original object could not perform the filtering for us while maintaining the context. Simply telling the object to do it would have brought us to the same situation (of working with people that have the given skills) but these people wouldn’t be Employees anymore, they would be just a list.
I see all this as an extension to the TellDontAsk principle. How to make sure you’re heading in the right direction, I’m not really sure. However, I think the usage of the JDK (or whatever development kit you are using) is a good indicator: in an object-oriented codebase the kit should be as discrete as possible. The more you use the development kit, the less object-oriented your code really is or your abstractions are not the best they can be. On the other hand, the more you are able to add/modify/remove functionalities just by working with existing objects (or adding new implementations of existing interfaces), the more object-oriented your application is.
P.S. Here is another example of the same idea.
Published on Java Code Geeks with permission by MIhai Andronache, partner at our JCG program. See the original article here: An Extension To TellDontAsk Opinions expressed by Java Code Geeks contributors are their own. |
Rather than introducing a special class for FilteredEmployees to provide the fact that the employees contained in this container class are in a filtered context, why not introduce a special class for “FilteredEmployee”, or a “EmployeeInContext”? The latter can even be provided with a enumerator to provide a using class with the context in which the employee was created/provided (in which case the EmployeeInContext tells the world the context) or at least allow for extensions such as EmployeeInFilteredContext and EmployeeInRaiseContext which may expose different behaviours? That way you cover both the query situations where a List of these are returned… Read more »
Yes, I think your proposal is also a way of doing it. It depends on what direction you’re coming from. I am not sure but I would say that the “context by container” provides better encapsulation (again, maybe).
I would also let the Employee (singular) provide its context (the container), but I would avoid names/prefixes such as “Context” in code. In this particular case, the Employee interface would probably have the method “Employees :: team()” — that “team” would be the container you’re talking about :)
Nice attempt.Well done.Keep it up.