我对代码完整书中的段落感到有些困惑。
在“要避免的类”一节中,它显示为:
“避免使用动词命名的类只有行为但没有数据的类通常不是一个类。考虑将类似DatabaseInitialization()或StringBuilder()的类转换为其他类的例程”
我的代码主要由没有数据的动词类组成。有发票阅读器,价格计算器,消息构建器等。我这样做是为了将每个类集中到一个任务。然后我将依赖关系添加到其他类以获取其他功能。
如果我正确理解了段落,我应该使用像
这样的代码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,但它也受到“贫血领域模型”的影响。
我相信这些见解会在将来帮助我。
答案 0 :(得分:20)
类名称如InvoiceReader,PriceCalculator,MessageBuilder,ArticleReader,InvoiceReader实际上不是动词名称。它们实际上是“名词代理 - 名词”类名。请参阅agent nouns。
动词类名称将类似于Validate,Operate,Manage等。显然,这些更好地用作方法,并且作为类名称会非常笨拙。
“名词代理 - 名词”类名称的最大问题在于它们对类的实际作用(例如UserManager,DataProcessor等)几乎没有意义。结果他们更容易臃肿并失去内部凝聚力。 (见Single Responsibility Principle)。
因此,带有IInvoiceReader和IArticleReader接口的WebService类可能是更清晰,更有意义的OO设计。
这为您提供了简单明了的名词类名“WebService”,以及“名词代理 - 名词”接口名称,它清楚地宣传了WebService类可以为调用者做些什么。
您可能还可以通过为另一个名词添加前缀来为实际类赋予更多含义,例如PaymentWebService。
然而,在更具体地描述类可以为调用者做什么时,接口总是比单个类名更好。随着类变得越来越复杂,新接口也可以添加有意义的名称。
答案 1 :(得分:11)
我个人忽略了这个“规则”。 .NET Framework本身充满了“动词”类:TextReader
,BinaryWriter
,XmlSerializer
,CodeGenerator
,StringEnumerator
,HttpListener
,{ {1}},TraceListener
,ConfigurationManager
,TypeConverter
...如果您认为框架设计不合理,那么请不要使用这些名称。
我的建议是这样的:如果你发现自己创建了一个“动词”类,它作用于单个“名词”类,请诚实地思考“名词”类是否可以自己执行动作。但是,为了避免使用动词类,不要开始创建God Objects或提出无意义/误导性的名称。
答案 2 :(得分:5)
不要盲目听取任何建议。这些只是指导原则。
那就是说,名词制作非常好的类名,只要他们为逻辑对象建模。由于“Person”类是所有“Person”对象的蓝图,因此将其称为“Person”非常方便,因为它允许您这样推理:“我将根据用户的输入创建Person,但首先我需要验证它......“
答案 3 :(得分:3)
请注意使用“避免”一词。如果你曾经使用它,它不会消除,根除或在地狱中燃烧。
作者的意思是,如果你发现自己有一堆所有以动词命名的类,你所要做的就是静态创建类,调用一个函数而忘记它们,这可能表明你是分离一点点关注的问题。
但是,在某种情况下,创建实现操作的类是一件好事,例如当您对同一操作有不同的策略时。一个很好的例子是IComparer&lt;&gt;。它所做的只是比较两件事,但有几种比较方法。
正如作者所建议的那样,在这些情况下,一个好方法是创建一个接口并实现它。的IComparer&LT;&GT;再次浮现在脑海中。
另一种常见情况是当操作处于繁重状态时,例如加载文件。将状态封装在一个类中可能是合理的。
答案 4 :(得分:2)
本书的基本内容是OO设计是关于提取对象(名词)和识别在这些对象之间和之间发生的操作(动词)。
名词成为对象,动词成为对这些对象进行操作的方法。
这个想法是
更接近程序模型真实 世界问题,方案越好 将是。
实际上,对象的有用之处在于它可以表示特定的状态。然后,您可以拥有此类的几个不同实例,每个实例都拥有不同的状态来表示问题的某些方面。
对于InvoiceReader类
将它放在一个物体中没有任何好处。
答案 5 :(得分: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
可能具有各种出口选项属性,但也可能是无国籍。
<强> [编辑] 强>
将GetInvoices
和GetArticles
移动到WebService
类意味着您将其实现与WebService类型联系起来。将它们放在单独的类中将允许您对发票和文章进行不同的实现。总的来说,将它们分开似乎更好。
答案 6 :(得分:0)
少关注名字。关于名称的规则只是一个不良做法的经验法则指标。重点是:
只有行为但没有数据的类通常不是真正的类
在您的情况下,看起来您的类同时具有数据和行为,并且它们也可以称为“发票”和“文章”。
答案 7 :(得分:0)
这取决于。许多类以Read和Write动词命名,因为这些类还创建,维护和表示与它们正在读取或写入的数据源的连接。如果你的课程正在这样做,最好将它们分开。
如果Reader对象只包含解析逻辑,那么将类转换为实用方法是可行的方法。我会使用比Webservice更具描述性的名称。
答案 8 :(得分:0)
我认为这本书提出了如下设计:
class Article : IIArticleReader
{
// Article data...
public IList<Article> GetArticles();
}
答案 9 :(得分:0)
这是OO中'动词与名词'的经典对象:
http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html