7

问题

在执行涉及多个对象的功能时,如何遵守“告诉,不问”的原则。

示例 - 生成报告

我有以下对象(仅用于说明目的):

车、马、兔

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

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
4

4 回答 4

8

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

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

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

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

结束时CreateReport,报表对象应该产生自己的结果。

于 2012-03-09T15:53:24.500 回答
6

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

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

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

于 2012-03-10T03:34:39.020 回答
3

这正是访问者模式的用途。

于 2012-03-10T03:51:16.037 回答
1

我不知道这个模式的确切名称(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 {
    ...
}

这样你就可以拥有同一个域对象的多个表示,而不违反“告诉不问”的原则。

于 2013-01-27T19:51:51.413 回答