【问题标题】:"Tell, Don't Ask" over multiple domain objects“告诉,不要问”多个域对象
【发布时间】:2012-03-09 15:44:12
【问题描述】:

问题

在执行涉及多个对象的功能时,如何遵守"Tell, Don't Ask" 原则。

示例 - 生成报告

我有以下物品(仅供说明):

车马兔

这些对象之间没有关系,但我确实想根据这些对象生成一个报告:

createHtmlReport(Car car, Horse horse, Rabbit rabbit){
    Report report = new Report()

    report.setSomeField(car.getSerialNumber())
    report.setAnotherField(horse.getNumberOfLegs())
    // ...etc       
}

这种方法的问题是它必须从每个对象中“提取”数据,这违反了“告诉,不问”规则。我宁愿隐藏每个对象的内部,让它们为我生成报告:

car.createHtmlReport()   
horse.createHtmlReport()
rabbit.createHtmlReport()

...但后来我收到了 3 份部分报告。此外,我认为 Rabbit 不必知道如何生成我需要的每一个报告(HTML、JMS、XML、JSON ......)。

最后,在生成报告时,我可能想要打开多个项目:

if (car.getWheels() == 4 || horse.getLegs() == 4)
    // do something

【问题讨论】:

  • +1 & fav 链接和问题。

标签: oop domain-driven-design tell-dont-ask


【解决方案1】:

报告应保持创建自我的能力。

在这种情况下,每个IReportable 对象都应该实现void UpdateReport(Report aReport)

Report.CreateReport(List<Reportable> aList) 被调用时,它会遍历List 并且每个对象在其自己的UpdateReport 实现中都会调用:

aReport.AddCar(serialNumber)
aReport.AddHorse(horseName)

CreateReport 的末尾,报告对象应该产生自己的结果。

【讨论】:

  • 访客&双重调度规则!
  • 要清楚,Report 必须实现AddCarAddHorse?我假设这些方法名称仅作为示例,但它们非常具有误导性。实际上,我浪费了大约 10 分钟,只是为了了解这些方法与 Car 和 Horse 类型本身无关 o_O
【解决方案2】:

“告诉不问”规则的目标是帮助您确定应该由给定对象承担的责任最终在其外部实施的情况(坏事)。
在您的案例中,我们可以看到哪些责任?我看到的是:

1) 知道如何格式化报告(xml、ascii、html 等)
2) 知道哪个报告发生了什么

第一个显然不属于域对象(汽车、马等)。 2)应该去哪里?可以建议域对象,但如果您的系统中有多个不同的报告,您最终会用关于不同报告细节的知识来负担您的对象,这些细节看起来和闻起来都很糟糕。更不用说它会违反单一责任原则:成为 Rabbit 是一回事,但知道 Rabbit 信息的哪些部分应该出现在报告 X 和报告 Y 上是另一回事。 因此,我将设计封装数据内容的类,这些数据内容会出现在特定类型的报告中(并可能执行必要的计算)。我不会担心他们读取 Rabbit、Horse 或 Car 的数据成员。此类实现的职责是“为特定类型的报告收集数据”,您有意识地认为它应该位于域对象之外。

【讨论】:

    【解决方案3】:

    这正是Visitor Pattern 的用途。

    【讨论】:

      【解决方案4】:

      我不知道这个模式的确切名称(Visitor, Builder, ...):

      public interface HorseView {
          void showNumberOfLegs(int number);
      }
      
      public interface CarView {
          void showNumberOfWheels(int number);
          void showSerialNumber(String serialNumber);
      }
      
      public class Horse {
      
          void show(HorseView view) {
              view.showNumberOfLegs(this.numberOfLegs);
          }
      
      }
      
      public class Car {
      
          void show(CarView view) {
              view.showNumberOfWheels(this.numberOfWheels);
              view.showSerialNumber(this.serialNumber);
          }
      
      }
      
      public class HtmlReport implements HorseView, CarView {
      
          public void showNumberOfLegs(int number) {
              ...
          }
      
          public void showNumberOfWheels(int number) {
              ...
          }
      
          public void showSerialNumber(String serialNumber) {
              ...
          }
      
      }
      
      public XmlModel implements HorseView, CarView {
          ...
      }
      
      public JsonModel implements HorseView, CarView {
          ...
      }
      

      这样您就可以对同一个域对象进行多种表示,而不会违反“告诉不问”的原则。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-09-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-05
        • 1970-01-01
        • 2014-08-11
        相关资源
        最近更新 更多