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. WPF MessageBox 添加确认取消按钮 并判断

    很简单的功能随笔 if (System.Windows.MessageBox.Show("您确定要删除吗?", "提示:", MessageBoxButton. ...

  2. 读BeautifulSoup官方文档之html树的搜索(1)

    之前介绍了有关的四个对象以及他们的属性, 但是一般情况下要在杂乱的html中提取我们所需的tag(tag中包含的信息)是比较复杂的, 现在我们可以来看看到底有些什么搜索的方法. 最主要的两个方法当然是 ...

  3. thinkphp5 的一些笔记

    Model里面的一些属性添加 protected $resultSetType = 'collection'; protected $autoWriteTimestamp = 'timestamp'; ...

  4. C#匹配中文字符串的4种正则表达式分享

    本文介绍在C#中使用匹配中文的正则表达式,包括纯中文.有中文.中文开头.中文结尾等几个正则表达式示例.在正则表达式中,中文可以通过Unicode编码来确定正则表达式范围. 在C#中,匹配中文的正则表达 ...

  5. WPF 在一个dll创建一个Window(包含xaml),在另一个dll中再次继承 会出错

    https://social.msdn.microsoft.com/Forums/vstudio/en-US/e92390eb-bbfa-42fb-baa9-2286444c0dca/the-comp ...

  6. Android动画基础——属性动画(Property Animation)

    本篇涉及例子下载:Github 本篇讲android 3.0引入的属性动画框架,上篇写视图动画View Animation时就说过ViewAnimation的缺点,那就是动画作用的是view本身的视觉 ...

  7. C#高性能大容量SOCKET并发(七):协议字符集

    原文:C#高性能大容量SOCKET并发(七):协议字符集 UTF-8 UTF-8是UNICODE的一种变长字符编码又称万国码,由Ken Thompson于1992年创建.现在已经标准化为RFC 362 ...

  8. 如何替换Windows的Shell(即explorer.exe)

    原文:如何替换Windows的Shell(即explorer.exe) 下载一个可以查看用户的SID的软件,如SysInternals套装中的PsGetsid.exe(地址:http://www.it ...

  9. .NET重思(二)接口和抽象类的取舍

    不得不说,接口和抽象类好像啊~两者都不可以实例化,并且未实现的部分都是由派生类实现的. 他们主要有这么个区别: (1)抽象类的派生类可以是派生类,换言之,抽象成员在派生类中不一定完全实现,而接口要求其 ...

  10. C#中比较两个对象的地址是否相同(也是引用计数的问题,和Java一样)

    private void button1_Click(object sender, EventArgs e) {     char[] ch = { 'z', 's', 'w', 'a', 'n',  ...