The Tell, Don’t Ask (TDA) principle suggests that it is better to issue an object a command do perform some operation or logic, rather than to query its state and then take some action as a result.

It is related to the Flags Over Objects antipattern as well as the Anemic Domain Modelantipattern.

You can easily spot violations of TDA in code that queries or uses several properties of an object in order to perform some calculation.

This is especially problematic when the same kind of calculation is done in many places (violating the Don’t Repeat Yourself principle), but can represent a design deficiency even if it only occurs in one location in the current codebase.

// Violates TDA
public class CpuMonitor
{
public int Value { get; set; }
} public class Client
{
public void AlertService(List<CpuMonitor> cpuMonitors)
{
foreach (var cpuMonitor in cpuMonitors)
{
if (cpuMonitor.Value > )
{
// alert
}
}
}
} // Refactored
public class CpuMonitor
{
private readonly int _alertThreshold; public CpuMonitor(int alertThreshold)
{
_alertThreshold = alertThreshold;
} public int Value { get; set; }
public bool ExceedsThreshold { get { return Value >= _alertThreshold; } }
} public class Client
{
public void AlertService(List<CpuMonitor> cpuMonitors)
{
foreach (var cpuMonitor in cpuMonitors)
{
if (cpuMonitor.ExceedsThreshold)
{
// alert
}
}
}
} // Refactored Further
public class CpuMonitor
{
private readonly int _alertThreshold;
private readonly Action<CpuMonitor> _alertAction; public CpuMonitor(int alertThreshold, Action<CpuMonitor> alertAction)
{
_alertThreshold = alertThreshold;
_alertAction = alertAction;
} public int Value { get; set; }
public bool ExceedsThreshold { get { return Value >= _alertThreshold; } } public void Sample()
{
if (ExceedsThreshold)
{
_alertAction(this);
}
}
} public class Client
{
public void AlertService(List<CpuMonitor> cpuMonitors)
{
foreach (var cpuMonitor in cpuMonitors)
{
cpuMonitor.Sample();
}
}
}

In the example above, the first refactoring looks at the magic number representing the alert threshold, and moves this concept into the monitor itself.

This might not be worth correcting if this client code were the only instance of this behavior, but if you find repetition in this kind of code, follow the Don’t Repeat Yourself principle and consolidate it into a class (in this case, CpuMonitor).

The initial refactoring is still querying each monitor and then performing some action based on that query, and so is still asking, not telling.

The final refactoring moves the responsibility for sending alerts into the monitor itself, while still avoiding tightly coupling it to any particular alert implementation.

In this way, monitor remains loosely coupled from whatever alert implementation the system might have in place.

It’s not necessary to follow the “don’t ask” part of this principle to the extreme of eliminating all access to objects’ state.

In general, it’s ok to query an object for its state, provided the information isn’t being used to make a decision related to the object.

If it is, then that decision and any corresponding behavior should most likely be moved within the object itself.

Another consequence of violating TDA is that often magic numbers or business rules end up sprinkled throughout code that references object state, rather than embedded within the object itself or passed into the object as a defined and well-named construct (such as CpuAlertThreshold in the example above).

    class Program
{
static void Main(string[] args)
{
Person xiaoMing = new Person("小明");
xiaoMing.WhatToDo = "泡面";
new Bolier(, xiaoMing.DoSthUseHotWater).HeatUp(); Person daMing = new Person("大明");
daMing.WhatToDo = "洗脸";
new Bolier(, daMing.DoSthUseHotWater).HeatUp(); }
} public class Person
{
public string Name { get; private set; }
public string WhatToDo { get; set; }
public Person(string name)
{
Name = name;
} public void DoSthUseHotWater(Bolier bolier)
{
Console.WriteLine(string.Format("{0}用{1}度的水{2}", Name, bolier.WaterTemperature, WhatToDo));
}
} /// <summary>
/// 烧水壶
/// </summary>
public class Bolier
{
public int WaterTemperature { get; private set; }
private int necessaryWaterTemperature;
private Action<Bolier> completeAction; public Bolier(int necessaryWaterTemperature, Action<Bolier> completeAction)
{
this.necessaryWaterTemperature = necessaryWaterTemperature;
this.completeAction = completeAction;
} public void HeatUp()
{
while (WaterTemperature < necessaryWaterTemperature)
{
Console.WriteLine(string.Format("{0}-当前水温:{1}", DateTime.Now.ToString(), WaterTemperature));
Thread.Sleep();
WaterTemperature += ;
} completeAction?.Invoke(this);
}
}

随机推荐

  1. 简历上的哪些内容才是 HR 眼中的干货?

    资源业绩能力知识自评 https://www.zhihu.com/question/39722495/answer/199846861

  2. WPF 4 单词拼写检查(SpellCheck)

    原文:WPF 4 单词拼写检查(SpellCheck)      在WPF中 Textbox 和RichTextBox 控件都内置了拼写检查属性,但该属性目前默认仅支持English.Spanish. ...

  3. 【推荐网站】下载国外网盘+强大的离线下载站—offcloud.com

    博主在网上浏览时看到一篇帖子,推荐了一个离线下载网站–offcloud.com,支持上传种子文件.磁力链和几十家网盘的直连下载,厉害了我的哥,这是个啥网站这么666.即使之前咱们写了几篇文章来自建下载 ...

  4. .NET中System.Diagnostics.Stopwatch、System.Timers.Timer、System.Threading.Timer 的区别

    1.System.Diagnostics.Stopwatch Stopwatch 实例可以测量一个时间间隔的运行时间,也可以测量多个时间间隔的总运行时间. 在典型的 Stopwatch 方案中,先调用 ...

  5. teamcity build web project arguments

    /p:Configuration=%system.Configuration%  => Release /p:DeployOnBuild=%system.DeployOnBuild%  => ...

  6. UBUTUN 通过蓝牙连接Hoary和诺基亚手机

    通过蓝牙连接Hoary和诺基亚手机 这个how to已经用Hoary.诺基亚6630和一个道尔芯片(Dongle)蓝牙(Usb蓝牙)测试过了.通过这个How to,你可以:-通过蓝牙,从你的电脑发送文 ...

  7. Have You Tried Delphi on Amazon Linux? (就是AWS用的Linux)

    The new Delphi Linux compiler enables customers to take new or existing Windows server applications ...

  8. Another maybe monad library for ruby

    欢迎任何形式的转载,但请务必注明出处:http://www.cnblogs.com/liangjingyang 项目地址:https://github.com/liangjingyang/maybe_ ...

  9. Silverlight消散,WinRT登台

    2011年,Silverlight刚开始有蓬勃发展的起色,不利的传言就开始大量流传.不安的Silverlight开发者们要求微软澄清,但得到的只是沉默.终于随着微软在BUILD上亮相Window 8以 ...

  10. 【canvas】基础练习一 图形

    Demo1[绘制一条线] <!DOCTYPE html> <html lang="en"> <head> <meta charset=&q ...