Шаблон дизайна для класса с более чем 100 свойствами - PullRequest
22 голосов
/ 27 октября 2009

Какой совет / предложения / руководство вы бы дали для разработки класса, который имеет более 100 свойств?

Фон

  • Класс описывает счет-фактуру. Счет может иметь более 100 атрибутов, описывающих его, то есть дату, сумму, код и т. Д. *
  • Система, в которую мы отправляем счет-фактуру, использует каждый из 100 атрибутов и представляется как единое целое (в отличие от разных частей, представляемых в разное время).
  • Атрибуты, описывающие счет-фактуру, являются обязательными в рамках бизнес-процесса. Бизнес-процесс не может быть изменен.

Предложения

  • Что другие сделали, когда столкнулись с разработкой класса, который имеет 100 атрибутов? т.е. создать класс с каждым из 100 свойств?
  • Как-нибудь разбить его (если да, то как)?
  • Или это достаточно нормальное явление в вашем опыте?

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

Ответы [ 17 ]

33 голосов
/ 27 октября 2009

Вы можете попытаться «нормализовать» это, как если бы вы использовали таблицу базы данных. Возможно, поместите все свойства, связанные с адресом, в класс Address, например, - тогда в вашем классе Invoice есть свойства BillingAddress и MailingAddress типа Address. Эти классы также могут быть использованы позже.

22 голосов
/ 27 октября 2009

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

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

11 голосов
/ 27 октября 2009

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

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

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

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

6 голосов
/ 27 октября 2009

Хммм ... Все ли это действительно конкретно , а только для счета? Обычно я видел что-то вроде:

class Customer:
.ID
.Name

class Address
.ID 
.Street1
.Street2
.City
.State
.Zip

class CustomerAddress
.CustomerID
.AddressID
.AddressDescription ("ship","bill",etc)

class Order
.ID
.CustomerID
.DatePlaced
.DateShipped
.SubTotal

class OrderDetails
.OrderID
.ItemID
.ItemName
.ItemDescription
.Quantity
.UnitPrice

И связывая все это вместе:

class Invoice
.OrderID
.CustomerID
.DateInvoiced

При печати счета-фактуры объедините все эти записи вместе.

Если вы действительно должны иметь один класс с более чем 100 свойствами, лучше использовать словарь

Dictionary<string,object> d = new Dictionary<string,object>();
d.Add("CustomerName","Bob");
d.Add("ShipAddress","1600 Pennsylvania Ave, Suite 0, Washington, DC 00001");
d.Add("ShipDate",DateTime.Now);
....

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

3 голосов
/ 27 октября 2009

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

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

2 голосов
/ 28 октября 2009

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

2 голосов
/ 27 октября 2009

Было бы слишком много столбцов, когда ваш класс / таблица, в которой вы храните его, начинает нарушать правила нормализации.

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

1 голос
/ 27 октября 2009

Я использовал словарь для чего-то подобного.

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

1 голос
/ 29 октября 2009

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

Простой DTO может выглядеть так:

class Form
{
    public $stuff = array();
    function add( $key, $value ) {}
}

Шлюз таблицы может быть больше похож на:

class Form
{
    function findBySubmitId( $id ) {} // look up my form
    function saveRecord() {}       // save it for my session
    function toBillingInvoice() {} // export it when done
}

И вы можете продлить это довольно легко в зависимости от того, есть ли у вас варианты счета. (Добавление метода validate () для каждого подкласса может быть целесообразным.)

class TPSReport extends Form { 
    function validate() {}
}

Если вы хотите отделить ваш DTO от механизма доставки, потому что механизм доставки является общим для всех ваших счетов, это может быть легко. Однако вы можете оказаться в ситуации, когда существует бизнес-логика в отношении успеха или неудачи счета. И это то, где я продолжаю уходить в сорняки. Но это где и модель ОО может быть полезна ... Я буду платить копейки, что будут разные счета-фактуры и разные процедуры для разных счетов-фактур, и если barfs отправки счетов-фактур, вам потребуются дополнительные процедуры: -)

class Form { 
    function submitToBilling() {}
    function reportFailedSubmit() {}
    function reportSuccessfulSubmit() {}
}
class TPSReport extends Form { 
    function validate() {}
    function reportFailedSubmit() { /* oh this goes to AR */ }
}

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

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

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

table FormSubmissions (
    id         int
    formVer    int -- fk of FormVersions
    formNum    int -- group by form submission
    fieldName  int -- fk of FormFields
    fieldValue text
)
table FormFields (
    id         int
    fieldName  char
)
table FormVersions (
    id
    name
)
select s.* f.fieldName from FormSubmissions s
left join FormFields f on s.fieldName = f.id
where formNum = 12345 ;

Я бы сказал, что это определенно тот случай, когда вы захотите пересмотреть свой путь, пока не найдете что-то удобное. Надеюсь, у вас есть некоторый контроль над такими вещами, как схема и ваша объектная модель. (Кстати ... эта таблица известна как «нормализованная»? Я видел изменения в этой схеме, обычно организованные по типу данных ... хорошо?)

1 голос
/ 27 октября 2009

В этой статье вы можете найти несколько полезных идей о свойствах Стива Йегге, которые называются Universal Design Pattern .

...