简体   繁体   English

要避免的类(代码完成)

[英]Classes to avoid (code complete)

I am somewhat confused about a paragraph in the code complete book. 我对代码完整书中的段落感到有些困惑。

In the section "Classes to avoid" it reads: 在“要避免的类”一节中,它写着:

"Avoid classes named after verbs A class that has only behavior but no data is generally not really a class. Consider turning a class like DatabaseInitialization() or StringBuilder() into a routine on some other class" “避免使用动词命名的类只有行为但没有数据的类通常不是一个类。考虑将类似DatabaseInitialization()或StringBuilder()的类转换为其他类的例程”

My code mainly consists of verb classes without data. 我的代码主要由没有数据的动词类组成​​。 There are invoicereaders, pricecalculators, messagebuilders etc. I do this to concentrate the classes to one task each. 有发票阅读器,价格计算器,消息构建器等。我这样做是为了将每个类集中到一个任务。 Then I add dependencies to other classes for other functionality. 然后我将依赖关系添加到其他类以获取其他功能。

If I understand the paragraph correctly I should use code like 如果我正确理解了段落,我应该使用代码

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

rather than 而不是

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

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

Edit Thanks for all the replies. 编辑感谢所有回复。

My conclusion is that my current code is more SRP than OO but that it also suffers from the "anemic domain model". 我的结论是,我目前的代码比OO更多SRP,但它也受到“贫血领域模型”的影响。

I'm sure theses insights will help me in future. 我相信这些见解将来会帮助我。

Class names like InvoiceReader, PriceCalculator, MessageBuilder, ArticleReader, InvoiceReader are not actually verb names. InvoiceReader,PriceCalculator,MessageBuilder,ArticleReader,InvoiceReader等类名实际上不是动词名称。 They are really "noun agent-noun" class names. 它们实际上是“名词代理 - 名词”类名。 See agent nouns . 代理商名词

A verb class name would be something like Validate, Operate, Manage etc. Obviously these are better used as methods and would be quite awkward as class names. 动词类名称将类似于Validate,Operate,Manage等。显然,这些更好地用作方法,并且作为类名称会非常笨拙。

The biggest problem with "noun agent-noun" class names is that they can give very little meaning as to what the class actually does (eg UserManager, DataProcessor etc). “名词代理 - 名词”类名的最大问题在于它们对类的实际作用(例如UserManager,DataProcessor等)几乎没有意义。 As a result they are more likely to be bloated and to lose internal cohesion. 结果他们更容易臃肿并失去内部凝聚力。 (See Single Responsibility Principle ). (参见单一责任原则 )。

Therefore the WebService class with the IInvoiceReader and IArticleReader interfaces is probably the clearer and more meaningful OO design. 因此,具有IInvoiceReader和IArticleReader接口的WebService类可能是更清晰,更有意义的OO设计。

This gives you the simple, obvious noun class name "WebService", along with "noun agent-noun" interface names that clearly advertise what the WebService class can do for callers. 这为您提供了简单明了的名词类名“WebService”,以及“名词代理 - 名词”接口名称,它清楚地宣传了WebService类可以为调用者做些什么。

You could probably also give more meaning to the actual class by prefixing another noun, for example PaymentWebService. 您可能还可以通过为另一个名词添加前缀来为实际类赋予更多含义,例如PaymentWebService。

However the interfaces are always better than a single class-name at describing more specifically what the class can do for callers. 但是,在更具体地描述类可以为调用者做什么时,接口总是比单个类名更好。 As the class grows more complex, new interfaces can also be added with meaningful names. 随着类变得越来越复杂,新接口也可以添加有意义的名称。

I ignore this "rule", personally. 我个人无视这个“规则”。 The .NET Framework itself is full of "verb" classes: TextReader , BinaryWriter , XmlSerializer , CodeGenerator , StringEnumerator , HttpListener , TraceListener , ConfigurationManager , TypeConverter , RoleProvider ... if you think that the Framework is poorly designed, then by all means, don't use names like these. .NET框架本身就充满了“动词”类: TextReaderBinaryWriterXmlSerializerCodeGeneratorStringEnumeratorHttpListenerTraceListenerConfigurationManagerTypeConverterRoleProvider ......如果你认为框架设计不当,然后通过各种手段,不要使用这些名称。

Steve's intent is understandable. 史蒂夫的意图是可以理解的。 If you find yourself creating dozens upon dozens of classes just to perform a specific task, it's likely a sign of an anemic domain model , where the objects that should be able to do these things by themselves are not. 如果你发现自己只是为了执行一项特定的任务而创建了几十个类,这可能是一个贫血领域模型的标志, 应该能够自己做这些事情的对象不是。 But at some point you have to make a choice between "pure" OOP and the SRP . 但在某些时候,你必须在“纯粹的”OOP和SRP之间做出选择。

My suggestion would be this: If you find yourself creating a "verb" class that acts on a single "noun" class, think honestly about whether or not the "noun" class can perform the action by itself. 我的建议是这样的:如果你发现自己创建一个“动词”类作用于单个“名词”类,请诚实地思考“名词”类是否可以自己执行动作。 But don't start creating God Objects or come up with meaningless/misleading names for the sole purpose of avoiding the verb-class. 但是,不要开始创造God Objects,或者为了避免使用动词类而提出毫无意义/误导性的名称。

Don't follow any advice blindly. 不要盲目听从任何建议。 These are just guidelines. 这些只是指导方针。

That said, Nouns make very good class names , as long as they model logical objects. 也就是说,只要名词模拟逻辑对象, 名词就会成为非常好的类名 Since the "Person" class is a blueprint for all "Person" objects, calling it "Person" is very handy, because it allows you to reason like this: "I'll create a Person from the user's input, but first I need to validate it..." 由于“Person”类是所有“Person”对象的蓝图,因此将其称为“Person”非常方便,因为它允许您这样推理:“我将根据用户的输入创建Person,但首先我需要验证它......“

Please note the use of the word "Avoid". 请注意使用“避免”一词。 It's not eliminate, or eradicate, or burn in hell if you ever use them. 如果你曾经使用它,它不会消除,根除或在地狱中燃烧。

What the author meant is that if you find yourself with a bunch of classes all named after verbs and all you happen to do is statically create thoses classes, call one function and forget about them, it probably is a sign that you are separating a little bit too much class concerns. 作者的意思是,如果你发现自己有一堆所有以动词命名的类,你所要做的就是静态创建类,调用一个函数而忘记它们,这可能是你分开一点点的标志有点太多关注。

However, there are situation where creating classes to implement an action is a good thing such as when you have different strategies for the same action. 但是,有些情况下,创建实现操作的类是一件好事,例如当您对同一操作有不同的策略时。 A very good example is IComparer<>. 一个很好的例子是IComparer <>。 All it does is compare two things, but there are several way of comparing things. 它只是比较两件事,但有几种比较方法。

As the author suggested, in those cases, a good way to do that is by creating an interface and implementing it. 正如作者所建议的那样,在这些情况下,一个好方法是创建一个接口并实现它。 IComparer<> comes to mind again. IComparer <>再次浮现在脑海中。

Another common situation is when the action has an heavy state, such as loading files. 另一种常见情况是当操作处于繁重状态时,例如加载文件。 It might become justified to encapsulate the state in a class. 将状态封装在类中可能是合理的。

Essentially what the book is saying is that OO design is about extracting the objects (the nouns) and identifying the operations (the verbs) that occur on and between those objects. 本书的基本内容是OO设计是关于提取对象(名词)和识别在这些对象之间和之间发生的操作(动词)。

The nouns become the objects, the verbs become the methods that operate on these objects. 名词成为对象,动词成为对这些对象进行操作的方法。

The idea is that the 这个想法是

closer the program models the real world problem, the better the program will be. 越接近程序模拟现实世界的问题,程序就越好。

In practical terms the useful thing with an object is that it can represent a particular state. 实际上,对象的有用之处在于它可以表示特定的状态。 Then you can have several different instances of this class each holding a different state to represent some aspect of the problem. 然后,您可以拥有此类的几个不同实例,每个实例都持有不同的状态来表示问题的某些方面。

In the case of the InvoiceReader class 在InvoiceReader类的情况下

  • you are only going to be creating one instance 你只会创建一个实例
  • the only state it represents is that of containing a dataProvider 它所代表的唯一状态是包含dataProvider的状态
  • it only contains one method 它只包含一种方法

there is no advantage to placing it in an object. 将它放在一个物体中没有任何好处。

The statement A class that has only behavior but no data is generally not really a class. 语句只有行为但没有数据的类通常不是一个类。 is plain wrong. 是完全错的。

Extracting behavior into a separate class is a good and common thing to do in refactoring. 将行为提取到单独的类中是重构中的一个好常见的事情。 It can have state, but also does not need to have one. 它可以有状态,但也不需要有状态。 You need to have clean interfaces, and implement them anyway you find necessary. 您需要拥有干净的界面,并在您认为必要时实施它们。

Also, stateless classes are great for calculations you need only for a short period of time. 此外,无状态类非常适合您在短时间内进行的计算。 You instantiate them (or, request a Factory of some sort to get them), do the necessary calculations, and then throw them to garbage. 您实例化它们(或者,请求某种类型的工厂来获取它们),进行必要的计算,然后将它们扔到垃圾箱中。 You can have the appropriate "version" of your behavior available anywhere, anytime. 您可以随时随地获得适当的行为“版本”。

Usually I find that different implementations of an interface have some state (set in constructor, for example), but sometimes the type of your class can determine its behavior completely. 通常我发现接口的不同实现具有某种状态(例如,在构造函数中设置),但有时类的类型可以完全确定它的行为。

For example: 例如:

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);
}

may be implemented as 可以实现为

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

To implement exporting from IExportData (whatever that may be) to a CSV file, you probably don't need any state at all. 要实现从IExportData (无论可能是什么)导出到CSV文件,您可能根本不需要任何状态。 ExcelExporter , on the other hand, could have various properties for export options, but could also be stateless. 另一方面, ExcelExporter可以具有导出选项的各种属性,但也可以是无状态的。

[Edit] [编辑]

Moving GetInvoices and GetArticles into the WebService class means you will tie their implementation to the WebService type. GetInvoicesGetArticles移动到WebService类意味着您将其实现与WebService类型联系起来。 Having them in separate classes will allow you to have different implementations for both invoices and articles. 将它们放在单独的类中将允许您对发票和文章进行不同的实现。 Overall, it seems better to have them separate. 总的来说,将它们分开似乎更好。

Focus less on the name. 更少关注名称。 The rule about the name is just a rule-of-thumb indicator for a poor practice. 关于名称的规则只是一个不良做法的经验法则指标。 The important point is this: 重点是:

A class that has only behavior but no data is generally not really a class 只有行为但没有数据的类通常不是一个类

In your case, it looks as though your classes have both data and behavior, and that they may as well be called "Invoice" and "Article". 在您的情况下,看起来您的类同时具有数据和行为,并且它们也可以称为“发票”和“文章”。

It depends. 这取决于。 A lot of classes exist named after Read and Write verbs because the classes also create, maintain, and represent a connection to the data source they're reading to or writing from. 许多类以Read和Write动词命名,因为这些类还创建,维护和表示与它们正在读取或写入的数据源的连接。 If your classes are doing that, it is probably best to keep them separate. 如果您的课程正在这样做,最好将它们分开。

If the Reader objects just contain parsing logic, then turning the classes into utility methods is the way to go. 如果Reader对象只包含解析逻辑,那么将类转换为实用方法是可行的方法。 I'd go with a more descriptive name than Webservice though. 不过,我会使用比Webservice更具描述性的名称。

I think the book suggests a design like the following: 我认为这本书提出了如下设计:

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

    public IList<Article> GetArticles(); 
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM