You wrote
I know the following is bad
and here is already your misconception: you think regligiously in terms of good and bad about this. Instead, my interpretation of Tell-Don't-Ask is, when you see a code snippet like
if(a.isX){
a.doY();
}
you may consider to refactor it to a.doYWhenX();
. But before you decide about this refactoring, also check
if
doYWhenX()
is a useful abstraction in your contextif
doYWhenX()
has some reusage potentialif it helps to make a.isX (or
a.isX()
) privateif it makes the using code more readable
is it worth the hassle
So my recommendation is to change your mindset about "Tell-Don't-Ask principle" - it is a rough guideline, a rule-of-thumb design heuristic, nothing more.
Now apply this to your case with two objects:
if(a.isX && b.isP){
a.doY();
b.doQ();
}
Whether it it more suitable to refactor this
to
a.doYWhenX(b);
orto
b.doQWhenP(a);
orto
myService.doYQWhenXP(a,b)
depends heavily on the context, which variant in your real world context creates best readability, best reusability or best encapsulation. Meaningless names like doX
or isP
are not suitable for making such a decision.