2010-01-19 3 views
37

Я несколько смущен о абзаце в полной книге кода.Классы, которых следует избегать (код завершен)

В разделе «Классы, чтобы избежать» гласит:.

«Избегайте классов с именем после глаголов класса, который имеет только поведение, но нет данных, как правило, на самом деле не класс Рассмотрим поворота класс как 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(); 
} 

Изменить Спасибо за все ответы.

Мое заключение состоит в том, что мой текущий код больше SRP, чем OO, но он также страдает от «модели анемичного домена».

Я уверен, что эти идеи помогут мне в будущем.

+1

Я согласен с Торариной. Классы существуют, чтобы что-то делать. В противном случае они просто «строят» с претензиями. – kdgregory

+2

Собственно, 'InvoiceReader' и' ArticleReader' в этом конкретном примере имеют некоторое состояние (частное поле 'dataProvider'). – Groo

+1

это, конечно же, чистый подход OO, функторы, которые вы предпочли бы глагол для (поскольку они притворяются функциями первого класса) и, возможно, некоторые статические классы –

ответ

20

Названия классов, такие как InvoiceReader, PriceCalculator, MessageBuilder, ArticleReader, InvoiceReader, на самом деле не являются именами глаголов. Они действительно являются именами имен существительных-существительных-существительных. См. agent nouns.

Имя класса глагола было бы чем-то вроде Validate, Operate, Manage и т. Д. Очевидно, что они лучше используются в качестве методов и будут довольно неудобными в качестве имен классов.

Самая большая проблема с именами классов «существительное-существительное» заключается в том, что они могут дать очень мало смысла в отношении того, что действительно делает класс (например, UserManager, DataProcessor и т. Д.). В результате они с большей вероятностью раздуваются и теряют внутреннюю сплоченность. (См. Single Responsibility Principle).

Поэтому класс WebService с интерфейсами IInvoiceReader и IArticleReader, вероятно, является более четким и содержательным дизайном OO.

Это дает вам простое, очевидное имя существительного «WebService», наряду с именами интерфейса «существительное-существительное», которые четко рекламируют, что класс WebService может сделать для вызывающих.

Возможно, вы также можете придать больше смысла фактическому классу, добавив другое существительное, например PaymentWebService.

Однако интерфейсы всегда лучше, чем одно имя класса, более конкретно описывая, что класс может сделать для вызывающих. По мере усложнения класса новые интерфейсы также могут быть добавлены с содержательными именами.

+1

Я согласен со всем, что вы здесь сказали, хотя примеры в книге относятся к существительным агента, таким как 'StringBuilder' (иронично, что .NET Framework буквально имеет этот точный класс). 'InvoiceReader' говорит мне намного больше о цели класса, чем' WebService'. – Aaronaught

+2

@ Я не знаю, да, возможно, я бы использовал более конкретное имя, чем WebService, например PaymentWebService, чтобы дать больше возможностей. Однако интерфейсы являются ключевой частью описания того, что делает класс. Они могут делать это гораздо более точно и на более гранулированном уровне, тем более, что класс растет по размеру (т. Е. Можно добавить больше интерфейсов). – Ash

+0

@Jeff, да, наверное, самое главное получить консенсус, когда вы работаете над подходом к именованию, а затем придерживаетесь его. т.е. согласованность, которую использует стиль наименования. – Ash

0

Фокус меньше на имя. Правило о названии - это просто индикатор правила для плохой практики. Важный момент заключается в следующем:

Класс, который имеет только поведение, но нет данных, как правило, на самом деле не класс

В вашем случае, это выглядит, как если бы ваши классы как данные, так и поведение, и что их также можно назвать «Счет-фактура» и «Статья».

+0

Не совсем. InvoiceReader просто анализирует поток данных и создает объект счета-фактуры. – adrianm

+0

@Jeff: за исключением того, что это неправильный путь. Объект доступа к данным обычно работает для определенного поставщика. Должна быть какая-то * абстрактная фабрика *, которая создает DAO на основе экземпляра поставщика. – Thorarin

+0

@Thorain - gah, извините, я удалил свой комментарий в процессе обновления его до ответа. И да, я согласен - я не имел в виду ничего конкретного о реализации провайдера. Абстрактная фабрика будет в порядке. –

0

Это зависит. Многие классы существуют с именем после чтения и написания глаголов, потому что классы также создают, поддерживают и представляют соединение с источником данных, который они читают или пишут. Если ваши классы это делают, вероятно, лучше оставить их отдельными.

Если объекты Reader просто содержат логическую синтаксическую разборку, то поворот классов в методы утилиты - это путь. Однако я бы назвал более подробное имя, чем Webservice.

2

В сущности, речь идет о том, что OO-проект касается извлечения объектов (существительных) и определения операций (глаголов), которые происходят на этих объектах и ​​между ними.

Существительные становятся объектами, глаголы становятся методами, которые действуют на этих объектах.

Идея заключается в том, что

ближе программа моделирует реальный мир проблема, тем лучше программа будет.

На практике полезная вещь с объектом заключается в том, что она может представлять конкретное состояние.Тогда у вас может быть несколько разных экземпляров этого класса, у каждого из которых есть другое состояние, чтобы представлять какой-то аспект проблемы.

В случае InvoiceReader класса

  • вы только собираетесь создать один экземпляр
  • единственное состояние, которое он представляет, что из содержащий DataProvider
  • содержит только один метод

нецелесообразно размещать его в объекте.

0

Я думаю, что книга предлагает дизайн, как следующее:

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

    public IList<Article> GetArticles(); 
} 
+0

Хотя это больше соответствует структуре, предложенной цитируемым абзацем, я не думаю, что это намеченный результат из этого совета :) – Thorarin

+0

Может быть или нет, он открыт для интерпретации. – Ariel

11

Я игнорировать это «правило», лично. .NET Framework сама полна "глагол" классы: TextReader, BinaryWriter, XmlSerializer, CodeGenerator, StringEnumerator, HttpListener, TraceListener, ConfigurationManager, TypeConverter, RoleProvider ... если вы считаете, что рамочный плохо разработаны, а затем всеми средствами, не используйте имена, подобные этим.

Цель Стива понятна. Если вы обнаружите, что десятки классов создаете десятки классов только для выполнения конкретной задачи, это, скорее всего, признак anemic domain model, где объекты, которые должны, могут сделать это сами по себе. Но в какой-то момент вам нужно сделать выбор между «чистым» ООП и SRP.

Мое предложение было бы так: если вы обнаружите, что создаете класс «глагол», который действует на одном классе «существительное», честно подумайте о том, может ли класс «существительное» выполнять действие самостоятельно. Но не начинайте создавать God Objects или придумывайте бессмысленные/вводящие в заблуждение имена с единственной целью - избегать глагольного класса.

+9

NET Framework - это просто - структура. Большинство людей не пишут фреймворки, и имена их классов должны отражать это. – 2010-01-19 16:33:36

3

Обратите внимание на использование слова «Избегайте». Это не устранить, не искоренить или не сжечь в аду, если вы когда-нибудь их используете.

То, что автор имел в виду, состоит в том, что если вы обнаруживаете, что с кучей классов все названные в честь глаголов, и все, что вам нужно сделать, это статически создавать классы томов, вызывать одну функцию и забывать о них, это, вероятно, признак того, что вы разделяют немного слишком много проблем класса.

Однако существует ситуация, когда создание классов для реализации действия - это хорошая вещь, например, когда у вас разные стратегии для одного и того же действия. Очень хорошим примером является IComparer <>. Все, что он делает, это сравнить две вещи, но есть несколько способов сравнения вещей.

Как автор предложил в таких случаях хороший способ сделать это путем создания интерфейса и его реализации. IComparer <> снова приходит на ум.

Другая распространенная ситуация, когда действие имеет тяжелое состояние, например, загрузка файлов. Может быть оправдано инкапсулировать состояние в классе.

5

Не следуйте советам вслепую. Это всего лишь рекомендации.

Это говорит, Существительные делают очень хорошие имена классов, если они моделируют логические объекты. Поскольку класс «Личность» является планом для всех объектов «Человек», вызов его «Лицо» очень удобен, потому что он позволяет вам рассуждать так: «Я создам Лица из ввода пользователя, но сначала мне нужно для его проверки ... »

+6

Даже этот совет;) – JeffH

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. Наличие их в отдельных классах позволит вам иметь разные реализации как для счетов-фактур, так и для статей. В целом, кажется, лучше иметь их отдельно.

Смежные вопросы