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

Шаблон проектирования для класса с более чем 100 свойствами

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

Фон

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

Предложения

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

ИЗМЕНИТЬ Прочитав некоторые замечательные ответы и подумав об этом дальше, я не думаю, что на этот вопрос действительно есть один ответ. Однако, поскольку мы закончили моделирование нашего дизайна в соответствии с LBrushkin Answer, я дал ему кредит. Хотя и не самый популярный ответ, ответ Л.Брушкина помог нам определить несколько интерфейсов, которые мы собираем и повторно используем во всем приложении, а также подталкиваем нас к исследованию некоторых шаблонов, которые могут быть полезными в будущем.

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

Ответ 3

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

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

Ответ 4

Hmmm... Все ли они действительно релевантные специально и только для счета-фактуры? Обычно я видел что-то вроде:

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

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

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

Ответ 8

Я использовал словарь < string, string > для чего-то вроде этого.

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

Ответ 10

Вы не должны мотивироваться чисто эстетическими соображениями.

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

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

Это были бы веские причины:

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

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

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

Если ни одно из этих соображений не применимо в вашем случае, то какая точка?

Ответ 11

Похоже, что для конечного результата вам нужно создать объект счета-фактуры со 100 свойствами. У вас есть 100 таких свойств в каждом случае? Возможно, вам понадобится factory, класс, который будет производить счет-фактуру с меньшим набором параметров. Другой метод factory может быть добавлен для каждого сценария, где релевантные поля счета-фактуры релевантны.

Ответ 12

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

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 ;

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

Ответ 13

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

Ответ 14

Вы можете попробовать LINQ, это будет для вас свойство auto-gen. Если все поля распределены по нескольким таблицам, вы можете создать представление и перетащить представление на свой конструктор.

Ответ 15

Словарь? почему бы и нет, но не обязательно. Я вижу тег С#, ваш язык имеет отражение, хорошо для вас. В моем коде Python у меня было несколько слишком больших классов, и рефлексия помогает много:

for attName in 'attr1', 'attr2', ..... (10 other attributes):
    setattr( self, attName, process_attribute( getattr( self, attName ))

Если вы хотите преобразовать 10 членов строки из некоторой кодировки в UNICODE, некоторые другие члены строки не должны быть затронуты, вы хотите применить некоторую числовую обработку к другим членам... convert types... a for loop beats copy -распространение большого количества кода в любое время для чистоты.

Ответ 16

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

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

Учебник (т.е. упрощенное использование в реальном мире) будет выглядеть так: -

 class invoice:
    int id;
    address shipto_address;
    address billing_address;
    order_date date;
    ship_date date;
    .
    .
    . 
    line_item invoice_line[999];

  class line_item;
    int item_no.
    int product_id;
    amt unit_price;
    int qty;
    amt item_cost;
    .
    .
    .

Итак, я удивлен, что у вас нет, по крайней мере, массива строк.

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

Ответ 17

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

удачи:)