Подтвердить что ты не робот

Классы, которые следует избегать (полный код)

Я немного смущен абзацем в полной книге кода.

В разделе "Классы, которые следует избегать" он читает:

"Избегайте классов, названных в честь глаголов. Класс, который имеет только поведение, но нет данных, как правило, не является классом. Подумайте о том, чтобы превратить класс, например 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, но он также страдает от "модели анемичного домена".

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

4b9b3361

Ответ 1

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

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

Самая большая проблема с именами классов "существительное-существительное" заключается в том, что они могут дать очень мало смысла в отношении того, что действительно делает класс (например, UserManager, DataProcessor и т.д.). В результате они с большей вероятностью раздуваются и теряют внутреннюю сплоченность. (См. Принцип единой ответственности).

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

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

Ответ 4

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

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

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

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

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

Ответ 5

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

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

Идея состоит в том, что

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

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

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

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

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

Ответ 6

Утверждение Класс, который имеет только поведение, но не имеет данных, обычно не является классом. неправильно.

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

Кроме того, классы без учета состояния отлично подходят для вычислений, которые вам нужны только в течение короткого периода времени. Вы создаете их экземпляр (или запрашиваете Factory какого-то рода, чтобы получить их), выполните необходимые вычисления, а затем выбросите их в мусор. Вы можете иметь соответствующую "версию" вашего поведения в любом месте и в любое время.

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

Например:

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

Ответ 7

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

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

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

Ответ 8

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

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

Ответ 9

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

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

    public IList<Article> GetArticles(); 
}