Шаблон копіювання класу?


11

Зараз я працюю соло розробником свого поточного проекту. Я успадкував проект від іншого розробника, який відтоді покинув компанію. Це веб-додаток із стилем перегляду моделей у C #. Він використовує Entity Framework для реляційного відображення об'єктів. І існує два різних набори класів для типів у доменній моделі. Один набір використовується для взаємодії з ОРМ, а інший використовується як моделі в системі MVC. Наприклад, можуть бути два класи:

public class Order{
    int ID{get;set;}
    String Customer{get;set;}
    DateTime DeliveryDate{get;set;}
    String Description{get;set;}
}

і

public class OrderModel{
    String Customer{get;set;}
    DateTime DeliveryDate{get;set;}
    String Description{get;set;}
    public OrderModel( Order from){
        this.Customer= from.Customer;
        // copy all the properties over individually
    }
    public Order ToOrder(){
        Order result =new Order();
        result.Customer = this.Customer;
        // copy all the properties over individually
    }

}

Я можу придумати кілька недоліків такого підходу (більше місць для зміни коду, якщо щось змінюється, більше об'єктів, що сидять навколо пам’яті, більше часу витрачається на копіювання даних навколо), але я не дуже впевнений, у чому тут переваги. Більше гнучкості для модельних класів, мабуть? Але я міг би отримати це, підкласируючи також сутність класів. Моя схильність полягала б у об'єднанні цих двох класових груп або, можливо, модельні класи будуть підкласами класів сутності. Тож я пропускаю щось важливе тут? Це загальна модель дизайну, про яку я не знаю? Чи є вагомі причини не переживати рефактор, про який я замислююся?

ОНОВЛЕННЯ

Деякі з відповідей тут змушують мене зрозуміти, що в моєму первинному описі проекту бракувало важливих деталей. Існує також третя група класів, які існують у проекті: класи моделей сторінки. Саме вони фактично використовуються як модель, що підтримує сторінку. Вони також містять інформацію, яка є специфічною для інтерфейсу, і не зберігалася б із замовленням у базі даних. Прикладним класом моделі сторінки може бути:

public class EditOrderPagelModel
{
    public OrderModel Order{get;set;}
    public DateTime EarliestDeliveryDate{get;set;}
    public DateTime LatestAllowedDeliveryDate{get;set;}
}

Я повністю бачу, що корисність цієї третьої групи тут відрізняється, і не планую об’єднувати її з чимось іншим (хоча я можу перейменувати її).

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

Я також повинен зазначити, що клієнт, представлений тут як рядок, повинен був спростити приклад, не тому, що він насправді представлений таким чином у системі. Фактична система має замовника як виразний тип у доменній моделі, має свої властивості


1
"Я успадкував проект від іншого розробника, який відтоді покинув компанію" є сайт, присвячений історіям, які починаються саме так .

Щодо вашого оновлення: це здається занадто великим непрямим для недостатньої користі.
Роберт Харві

@RobertHarvey Що ти маєш на увазі як занадто велика непрямість?
Роберт Рікеттс

1
The OrderModel OrderViewModel (те, що ви, мабуть, називаєте EditOrderPageModel) є достатнім непрямим; дивіться мою відповідь нижче.
Роберт Харві

@RobertHarvey Це звучить як відповідь на питання, яке я насправді намагався задати
Роберт Рікеттс

Відповіді:


16

Тож я пропускаю щось важливе тут?

ТАК

Хоча вони виглядають як одне і те ж і представляють одне і те ж у домені, вони не є однаковими (OOP) об'єктами.

Один - Orderвідомий частиною коду для зберігання даних. Інше - це те Order, що відомо в інтерфейсі користувача. Хоча приємний збіг обставин, що ці класи мають однакові властивості, їм це не гарантується .

Або поглянути на це з іншого погляду, врахуйте Принцип єдиної відповідальності. Основний мотиватор тут полягає в тому, що «у класу є одна причина для зміни». Особливо, коли ви маєте справу з широко розповсюдженими структурами, такими як ORM та / або фреймворк інтерфейсу, ваші об'єкти повинні бути добре ізольованими, оскільки зміни в структурі часто призводять до змін ваших організацій.

Прив’язуючи ваші сутності до обох рамок, ви робите це набагато гірше.


7

Мета Моделі перегляду - забезпечити розв'язку двома способами: наданням даних у формі, якої потрібен Перегляд (незалежно від моделі), і (особливо в MVVM), відштовхуючи частину або всю логіку Перегляду назад від Погляду до моделі перегляду.

У повністю нормалізованій моделі Клієнт не буде представлений у Моделі рядком, а скоріше ідентифікатором. Якщо вона потребує імені замовника, Модель шукатиме ім’я з таблиці Клієнти, використовуючи CustomerID, знайдений у таблиці Замовлення.

З іншого боку, модель перегляду може більше зацікавити Nameклієнта, а не ідентифікатор. У нього немає потреби в ідентифікаторі, якщо Клієнт не оновлюється в Моделі.

Крім того, Модель перегляду може, і зазвичай, набувати іншої форми (якщо тільки це не чисто CRUD ). Об'єкт Модель перегляду рахунків-фактур може мати ім’я клієнта, адреси та позиції, але модель містить клієнтів та описи.

Іншими словами, рахунки-фактури зазвичай не зберігаються так само, як вони відображаються.


3

У деякому відношенні ви праві: вони обоє посилаються на той самий domainobject, який називається order. Але ви помиляєтеся в тому, що це не справжнє дублювання, ніж інше представлення - походить з різних цілей. Якщо ви хочете зберегти своє замовлення, ви, наприклад, хочете отримати доступ до кожної колонки. Але ти не хочеш це просочувати назовні. Крім того, що проштовхувати цілі об'єкти через дріт - це не те, що ви дійсно хочете. Мені відомі випадки, коли колекція МБ відсилалася ordersклієнту, де вистачить кількох кілобайт .

Якщо коротко розповісти коротко - ось основні причини, які ви хочете зробити так:

1) Інкапсуляція - приховування інформації вашої заявки

2) Малі моделі швидко серіалізуються і легко передаються

3) Вони виконують різні цілі, хоча перекриваються, і тому повинні бути різні об'єкти.

4) Іноді має сенс мати для різних запитів різні ціннісні об’єкти . Я не знаю, як це в C #, але на Java можна використовувати POJO для збору результатів. Якщо мені потрібен сингл, orderя використовую Order -Entity, але якщо мені потрібні лише деякі стовпці, заповнені сумами даних, використання менших об'єктів є більш ефективним.


У структурі структури сутності об'єкти є простими старими об'єктами C #, тому надсилання їх по дроту насправді не становить великої проблеми. Я бачу корисність того, що за певних обставин можна надіслати лише частину змісту замовлення.
Роберт Рікеттс

Один чи купа - це не проблема, як я вже сказав. Але є випадки, коли у вас є МБ даних, що сповільнює час завантаження. Подумайте про час завантаження мобільних пристроїв
Thomas Junk

0

(Відмова: Я бачив лише, що він використовується таким чином. Я, можливо, неправильно зрозумів справжню мету цього. Ставтесь до цієї відповіді з підозрою.)

Ось ще один зниклий біт: перетворення між Orderі OrderModel.

OrderКлас прив'язаний до вашого ОРЗ, в той час як OrderModelприв'язаний до дизайну вашого виду моделі.

Зазвичай два способи будуть надані "магічним способом" (іноді їх називають DI, IoC, MVVM чи ще іншою справою). Тобто, замість того, щоб реалізувати ці два способи перетворення як частину OrderModel, вони замість цього будуть належати до класу, призначеного для перетворення цих речей; цей клас буде якимось чином зареєстрований у рамках, щоб фреймворк міг його легко шукати.

public class OrderModel {
    ...
    public OrderModel(Order from) { ... }
    public Order ToOrder() { ... }
}

Причиною цього є те, що щоразу, коли відбувається перехід від місця OrderModelдо Orderабо навпаки, ви знаєте, що деякі дані повинні бути завантажені або збережені в ORM.


Якщо я тримаю обох навколо, я, безумовно, хотів би встановити їх, щоб конверсія була більш автоматизованою
Роберт Рікеттс

@RobertRicketts Те, що я бачив, виглядає приблизно так: IMvxValueConverter . Хоча я, можливо, неправильно зрозумів його призначення.
rwong
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.