According to Explanation on how "Tell, Don't Ask" is considered good OO, I know I should avoid get the state of an object and then decides the actions to take to that object, eg:
Bad:
public class Manager{
public void scheduleCallback(){
if(this.systemMonitor.temperature>100){
this.systemMonitor.soundAlarms();
}
}
}
Good:
public class Manager{
public void scheduleCallback(){
this.systemMonitor.scheduleCallback();
}
}
public class SystemMoniter{
public void scheduleCallback(){
if(this.temperature>100){
this.soundAlarms();
}
}
}
However, in practice, I found the example above is not similar to real code, instead SystemMoniter may need to call other objects from its parent:
public class Manager{
private LED led;
private Speaker speaker;
private Screen screen;
public void scheduleCallback(){
this.systemMonitor.scheduleCallback(LED led,Speaker speaker,Screen screen);
}
}
public class SystemMoniter{
public void scheduleCallback(LED led,Speaker speaker,Screen screen){
if(this.temperature>100){
led.turnRed();
speaker.play("alarm.mp3");
screen.play("alarm.mp4");
}
}
}
which I think moving the logic that requires parent objects to Manager and then asks systemMoniter to get the temperature is more simple:
public class Manager{
private LED led;
private Speaker speaker;
private Screen screen;
public void scheduleCallback(){
if(this.systemMoniter.temperature>100){
led.turnRed();
speaker.play("alarm.mp3");
screen.play("alarm.mp4");
}
}
}
I tried passing a callback to SystemMoniter:
public class SystemMoniter{
public void scheduleCallback(Runnable runnable){
if(this.temperature>100){
runnable.run();
}
}
}
public class Manager{
private LED led;
private Speaker speaker;
private Screen screen;
public void scheduleCallback(){
this.systemMoniter.scheduleCallback(new Runnable(){
public void run(){
led.turnRed();
speaker.play("alarm.mp3");
screen.play("alarm.mp4");
}
});
}
}
But I found it is worse because the codes with callback requires me to yo-yo between caller and callee to understand, which is a maintain nightmare.
And sometimes I found "Tell, don't ask" may result in circular dependency:
Tell, don't ask:
public class A{
public B b;
public void run(){
if(this.state){
//some code in A
this.b.run();
}
}
}
public class B{
public A a;
public void run(){
if(this.state){
//some code in B
this.a.run();
}
}
}
non Tell, don't ask (move the logic and action to perform to parent):
public class ManagerOfAB(){
public void run(){
if(b.state){
//some code in A
}
if(a.state){
//some code in B
}
}
}
which I think the "non Tell, don't ask" version is easier to understand. And real world, for example, there may need a engineer to check the system actively, if the engineer finds the system is not working, some actions may need to take to repair the system, which is a kind of "get state of that object, then decide to do something of that object", right? So I think "get state of that object, then decide to do something of that object" is not always bad.
So my question is, according to the codes above, are "need to call objects in parent object" and "avoid circular dependency" reasons to avoid "Tell, don't ask"?