39

我对代码完整书中的一段感到有些困惑。

在“要避免的类”部分中,它写道:

“避免以动词命名的类只有行为但没有数据的类通常不是真正的类。考虑将像 DatabaseInitialization() 或 StringBuilder() 这样的类变成其他类的例程”

我的代码主要由没有数据的动词类组成​​。有 invoicereaders、pricecalculators、messagebuilders 等。我这样做是为了将课程集中到每个任务上。然后我将依赖项添加到其他类以获得其他功能。

如果我正确理解该段落,我应该使用类似的代码

class Webservice : IInvoiceReader, IArticleReader {
    public IList<Invoice> GetInvoices();
    public IList<Article> GetArticles();
}

而不是

class InvoiceReader : IInvoiceReader {
    public InvoiceReader(IDataProvider dataProvider);
    public IList<Invoice> GetInvoices();
}

class ArticleReader : IArticleReader {
    public ArticleReader(IDataProvider dataProvider);
    public IList<Article> GetArticles();
}

编辑 感谢所有回复。

我的结论是,我当前的代码比 OO 更具 SRP,但它也受到“贫血域模型”的影响。

我相信这些见解将来会对我有所帮助。

4

10 回答 10

20

InvoiceReader、PriceCalculator、MessageBuilder、ArticleReader、InvoiceReader 等类名称实际上并不是动词名称。它们实际上是“名词代理名词”类名。见代理名词

动词类名可能是 Validate、Operate、Manage 等。显然,这些更适合用作方法,但作为类名会很尴尬。

“名词-代理-名词”类名的最大问题是它们对类的实际作用(例如,UserManager、DataProcessor 等)几乎没有什么意义。因此,他们更容易臃肿并失去内部凝聚力。(参见单一职责原则)。

因此,带有 IInvoiceReader 和 IArticleReader 接口的 WebService 类可能是更清晰、更有意义的 OO 设计。

这为您提供了简单、明显的名词类名称“WebService”,以及清楚地宣传 WebService 类可以为调用者做什么的“名词代理名词”接口名称。

您可能还可以通过为另一个名词添加前缀来赋予实际类更多含义,例如 PaymentWebService。

然而,在更具体地描述类可以为调用者做什么方面,接口总是比单个类名更好。随着类变得越来越复杂,还可以添加具有有意义名称的新接口。

于 2010-01-19T16:13:49.440 回答
11

我个人忽略了这个“规则”。.NET Framework 本身充满了“动词”类:TextReader, BinaryWriter, XmlSerializer, CodeGenerator, StringEnumerator, HttpListener, TraceListener, ConfigurationManager, TypeConverter, RoleProvider... 如果您认为该框架设计不佳,那么请务必不要使用这样的名称。

史蒂夫的意图是可以理解的。如果您发现自己创建了几十个类只是为了执行一项特定任务,这可能是领域模型贫乏的迹象,而应该能够自己完成这些事情的对象却没有。但是在某些时候,您必须在“纯” OOP 和SRP之间做出选择。

我的建议是这样的:如果您发现自己创建了一个作用于单个“名词”类的“动词”类,请诚实地考虑“名词”类是否可以自行执行该操作。但是不要仅仅为了避免动词类而开始创建上帝对象或提出无意义/误导性的名称。

于 2010-01-19T16:06:22.890 回答
5

不要盲目听从任何建议。这些只是指导方针。

也就是说,名词是非常好的类名,只要它们对逻辑对象进行建模。由于“Person”类是所有“Person”对象的蓝图,因此将其称为“Person”非常方便,因为它允许您这样推理:“我将根据用户的输入创建一个 Person,但首先我需要来验证它……”

于 2010-01-19T16:24:12.047 回答
3

请注意“避免”一词的使用。如果您使用它们,它不会消除,根除或在地狱中燃烧。

作者的意思是,如果你发现自己有一堆以动词命名的类,而你碰巧只是静态地创建这些类,调用一个函数并忘记它们,这可能表明你正在分离一点有点太多的阶级问题。

但是,在某些情况下,创建类来实现一个动作是一件好事,例如当您对同一动作有不同的策略时。一个很好的例子是 IComparer<>。它所做的只是比较两件事,但是有几种比较事物的方法。

正如作者建议的那样,在这些情况下,一个好方法是创建一个接口并实现它。IComparer<> 再次浮现在脑海中。

另一种常见的情况是当动作有很重的状态时,比如加载文件。将状态封装在一个类中可能是合理的。

于 2010-01-19T16:07:08.093 回答
2

本质上,这本书的意思是,OO 设计是关于提取对象(名词)并识别发生在这些对象上和这些对象之间的操作(动词)。

名词成为对象,动词成为对这些对象进行操作的方法。

这个想法是

程序模型越接近现实世界的问题,程序就会越好。

实际上,对象的有用之处在于它可以表示特定状态。然后你可以有这个类的几个不同的实例,每个实例都持有不同的状态来代表问题的某些方面。

对于 InvoiceReader 类

  • 你只会创建一个实例
  • 它代表的唯一状态是包含 dataProvider
  • 它只包含一种方法

把它放在一个物体上没有任何好处。

于 2010-01-19T15:42:53.423 回答
1

声明只有行为但没有数据的类通常不是真正的类。是完全错误的。

将行为提取到单独的类中是重构中一件好事且常见的事情。它可以有状态,但也不需要有一个。你需要有干净的接口,并在你认为有必要的情况下实现它们。

此外,无状态类非常适合仅在短时间内需要的计算。您将它们实例化(或者,请求某种工厂来获取它们),进行必要的计算,然后将它们扔到垃圾中。您可以随时随地获得您行为的适当“版本”。

通常我发现接口的不同实现有一些状态(例如在构造函数中设置),但有时你的类的类型可以完全决定它的行为。

例如:

public interface IExporter
{
    /// <summary>
    /// Transforms the specified export data into a text stream.
    /// </summary>
    /// <param name="exportData">The export data.</param>
    /// <param name="outputFile">The output file.</param>
    void Transform(IExportData exportData, string outputFile);
}

可以实现为

class TabDelimitedExporter : IExporter { ... }
class CsvExporter : IExporter { ... }
class ExcelExporter : IExporter { ... }

要实现从IExportData(无论是什么)导出到 CSV 文件,您可能根本不需要任何状态。ExcelExporter另一方面,可以具有用于导出选项的各种属性,但也可以是无状态的。

[编辑]

移动GetInvoicesGetArticles进入WebService类意味着您将它们的实现绑定到 WebService 类型。将它们放在单独的类中将允许您对发票和文章有不同的实现。总的来说,将它们分开似乎更好。

于 2010-01-19T16:32:41.393 回答
0

少关注名字。关于名称的规则只是不良实践的经验法则指标。重要的一点是:

只有行为但没有数据的类通常不是真正的类

在您的情况下,您的类看起来好像既有数据又有行为,而且它们也可以称为“发票”和“文章”。

于 2010-01-19T15:42:01.243 回答
0

这取决于。存在许多以 Read 和 Write 动词命名的类,因为这些类还创建、维护和表示与它们正在读取或写入的数据源的连接。如果您的班级正在这样做,最好将它们分开。

如果 Reader 对象只包含解析逻辑,那么将类转换为实用方法是可行的方法。不过,我会使用比 Webservice 更具描述性的名称。

于 2010-01-19T15:42:21.130 回答
0

我认为这本书建议的设计如下:

class Article : IIArticleReader
{
    // Article data...

    public IList<Article> GetArticles(); 
}
于 2010-01-19T15:50:04.280 回答
0

这是 OO 中“动词 vs 名词”的经典解释:

http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html

于 2010-01-21T10:36:48.443 回答