告訴,不要詢問

2013 年 9 月 5 日

告訴,不要詢問是一種原則,有助於人們記住物件導向的重點在於將資料與操作該資料的函式綑綁在一起。它提醒我們,與其詢問物件資料並對該資料執行動作,我們應該告訴物件要執行什麼動作。這鼓勵將行為移入物件中,以配合資料。

讓我們用一個範例來說明。假設我們需要監控特定值,如果值高於某個限制,則發出警報。如果我們以「詢問」的風格撰寫這段程式碼,我們可能會有一個資料結構來表示這些事物…

class AskMonitor...

  private int value;
  private int limit;
  private boolean isTooHigh;
  private String name;
  private Alarm alarm;

  public AskMonitor (String name, int limit, Alarm alarm) {
    this.name = name;
    this.limit = limit;
    this.alarm = alarm;
  }

…並將它與一些存取器結合,以取得這些資料

class AskMonitor...

  public int getValue() {return value;}
  public void setValue(int arg) {value = arg;}
  public int getLimit() {return limit;}
  public String getName()  {return name;}
  public Alarm getAlarm() {return alarm;}

然後我們會像這樣使用資料結構

AskMonitor am = new AskMonitor("Time Vortex Hocus", 2, alarm);
am.setValue(3);
if (am.getValue() > am.getLimit()) 
  am.getAlarm().warn(am.getName() + " too high");

「告訴,不要詢問」提醒我們,應將行為放入監控物件本身(使用相同的欄位)。

class TellMonitor...

  public void setValue(int arg) {
    value = arg;
    if (value > limit) alarm.warn(name + " too high");
  }

將會像這樣使用

TellMonitor tm = new TellMonitor("Time Vortex Hocus", 2, alarm);
tm.setValue(3);

許多人發現告訴,不要詢問是一個有用的原則。物件導向設計的基本原則之一是將資料和行為結合在一起,以便我們系統的基本元素(物件)同時結合兩者。這通常是一件好事,因為這些資料和操作它們的行為緊密結合:其中一個的變更會導致另一個的變更,了解其中一個有助於你了解另一個。緊密結合的事物應該在同一個元件中。思考告訴,不要詢問是一種幫助程式設計師了解如何增加這種共置的方式。

但就我個人而言,我不會使用告訴,不要詢問。我確實會尋找共置資料和行為,這通常會導致類似的結果。我發現告訴,不要詢問令人困擾的一件事是,我看到它鼓勵人們成為GetterEradicators,試圖擺脫所有查詢方法。但有時物件透過提供資訊有效地進行協作。一個很好的例子是接收輸入資訊並轉換它以簡化其客戶端的物件,例如使用EmbeddedDocument。我看到程式碼陷入只告訴的糾結,而適當負責的查詢方法會簡化問題[1]。對我來說,告訴,不要詢問是共置行為和資料的踏腳石,但我認為這一點不值得強調。

延伸閱讀

此原則最常與 Andy Hunt 和「Prag」Dave Thomas(實用程式設計師)有關。他們在 IEEE 軟體專欄網站上的一篇文章 中描述了它。

備註

1: 確實,有時甚至應捨棄資料和行為共置的更基本原則,以利於其他考量,例如分層。良好的設計完全在於權衡取捨,而資料和行為共置只是一個需要考慮的因素。