Параметры стиля OO против параметров типа - PullRequest
3 голосов
/ 31 декабря 2008

Скажем, у вас есть два метода:

Номер 1:

void AddPerson(Person person)
{
  // Validate person
  if(person.Name != null && IsValidDate(person.BirthDate)
    DB.AddPersonToDatabase(person);
}

Номер 2:

void AddPerson(string name, DateTime birthDate)
{
  Person p = new Person(name, birthDate);
  DB.AddPersonToDatabase(person);
}

Какой из двух методов является лучшим? Я знаю, что первый является более правильным с точки зрения ОО, но я чувствую, что второй более читабелен, и вам не нужно проверять, что объект действителен, так как параметры в этом уверены. Мне просто не нравится проверять объекты везде, где я передаю их в качестве параметров. Есть ли другие подходы?

EDIT: Спасибо за все ответы. Чтобы уточнить, проверка в конструкторе и методе IsValid, конечно, является хорошим подходом, но в моем коде действительное состояние человека часто зависит от контекста и может варьироваться от метода к методу. Это может быть признаком плохого дизайна.

Код является лишь примером для описания проблемы.

Ответы [ 12 ]

10 голосов
/ 31 декабря 2008

Первый не должен проверять person.Name и person.BirthDate - они должны проверяться автоматически конструктором Person. Другими словами, если вы передали Персона, вы должны знать, что она действительна.

С другой стороны, вам нужно проверить, что person не является пустой ссылкой.

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

2 голосов
/ 31 декабря 2008

Другой вариант будет:

void AddPerson(Person person)
{  // Validate person  
   if(person.IsValid)
   {
     DB.AddPersonToDatabase(person);
   }
}

Предполагая, что Person не проверяет себя при создании. Что в некоторых случаях является жизнеспособным, если объект может быть недопустимым состоянием, пока он является переходным

2 голосов
/ 31 декабря 2008

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

1 голос
/ 31 декабря 2008

Определенно лучше передавать объект Person, а не набор примитивных типов в качестве параметров. Сравните следующие два метода


public static void Withdrawal(Account account, decimal amount)
{
    DB.UpdateBalance(account.AccountNumber, amount);
}

public static void Withdraw(int accountNumber, decimal amount)
{
    DB.UpdateBalance(accountNumber, amount);
}

Два метода выглядят почти одинаково, но второй небезопасен. Int может прийти откуда угодно, так что вы облажались, если напишите это:

private void CloseTransaction(Transaction tran)
{
    BankAccounts.Withdrawal(tran.Account.RoutingNumber, tran.Amount);
        // logic error: meant to pass Account.AccountNumber instead of Account.RoutingNumber
}

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

Я работал в компании, которая писала банковское программное обеспечение, и мы действительно столкнулись с ошибкой такого типа в производстве. Это происходило только во время передачи конкретного вида хранилища, и было обнаружено только тогда, когда один из наших банков заметил, что их балансы GL уменьшались на несколько долларов каждый раз, когда они запускали процесс на конец месяца. Банк подозревал кражу сотрудников в течение нескольких месяцев, но только благодаря тщательному анализу кода кто-то отследил проблему до ошибки в нашем программном обеспечении.

1 голос
/ 31 декабря 2008

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

person.SetBirthDate(Person person)
person.ResetPassword(Person person)

Но в этом случае я предпочитаю первое, потому что, как сказал Грег Бич, метод ничего не должен знать о доменном объекте.

Кстати, подумайте о перегрузке (СУХОЙ - не повторяйте себя):

void AddPerson(Person person)
{
  if(person.Name != null && IsValidDate(person.BirthDate)
    DB.AddPersonToDatabase(person);
}

void AddPerson(string name, DateTime birthDate)
{
  Person p = new Person(name, birthDate);
  this.AddPerson(p);
}
1 голос
/ 31 декабря 2008

Я предпочитаю первое (передача объекта), потому что оно уменьшает связь API с объектом. Если вы измените объект Person, например, добавьте новое свойство, такое как Nickname, которое ранее не требовалось, тогда в первом случае вам не нужно менять общедоступный API, тогда как во втором вам нужно либо изменить метод, либо добавить новую перегрузку.

0 голосов
/ 31 декабря 2008

Лучшая инкапсуляция, если вы используете объекты; вот для чего они. Я думаю, что люди попадают в беду, когда они слишком часто используют примитивы.

Не должно быть возможности создать недопустимого человека. Конструктор должен проверить допустимые параметры и вызвать исключение IllegalArgumentException или завершить проверку, если они недопустимы. Вот что такое программирование по контракту.

0 голосов
/ 31 декабря 2008

Я думаю, что Джон в значительной степени прибил это. Person должен нести ответственность за обеспечение того, чтобы действительное лицо было создано в его конструкторе.

Об ответственности, которая создает или не создает объект Person (будь то метод AddPerson или его вызывающая сторона), прочитайте

http://en.wikipedia.org/wiki/GRASP_%28Object_Oriented_Design%29#Creator

Речь идет о вопросах ответственности в ООП. В вашем конкретном случае, если AddPerson обернул вызов к интерфейсу БД, я не совсем уверен в этом. Это зависит от того, для чего этот объект Person используется вне этого контекста. Если это делается исключительно для того, чтобы содержать данные, которые будут добавлены в базу данных, возможно, хорошей идеей будет их создание в методе AddPerson, поскольку это избавляет пользователя вашего класса от необходимости знать о классе Person. ,

0 голосов
/ 31 декабря 2008

Я предполагаю, что методы не являются частью типа Person. Имея это в виду, я чувствую, что они оба имеют слишком много знаний о другом типе (Person) ИМО. Первая версия не должна проверять создание действительного экземпляра лица. Если это является обязанностью звонящего, то каждый звонящий должен это сделать. Это хрупко.

Вторая версия имеет сильную зависимость от другого Типа из-за того, что она создает экземпляр этого типа.

Я бы определенно предпочел первую версию, но я бы переместил часть проверки из этого фрагмента кода.

0 голосов
/ 31 декабря 2008

Я бы просто создал перегрузки для обоих. Особенно учитывая, что они могут быть созданы автоматически.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...