Шаблон Дублирования Класса?

11

В настоящее время я работаю сольным разработчиком над моим текущим проектом. Я унаследовал проект от другого разработчика, который с тех пор покинул компанию. Это веб-приложение в стиле модель-вид-контроллер в C #. Он использует Entity Framework для реляционного отображения объектов. И есть два разных набора классов для типов в модели предметной области. Один набор используется для взаимодействия с ORM, а другой используется в качестве моделей в системе 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
Модель заказа. OrderViewModel (то, что вы, вероятно, называете EditOrderPageModel) является достаточной косвенностью; см. мой ответ ниже.
Роберт Харви
@RobertHarvey Это звучит как ответ на вопрос, который я на самом деле пытался задать
Роберт Рикеттс

Ответы:

16

Так я что-то упустил здесь?

ДА

Хотя они выглядят одинаково и представляют одно и то же в домене, они не являются одинаковыми (ООП) объектами.

Один из них Orderизвестен как часть кода, предназначенная для хранения данных. Другой, Orderкак известно UI. Хотя это приятное совпадение, что эти классы имеют одинаковые свойства, они не гарантированы .

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

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

Telastyn
источник
7

Цель модели представления состоит в том, чтобы обеспечить разделение двумя способами: предоставляя данные в форме, которая требуется представлению (независимо от модели), и (особенно в MVVM), отодвигая часть или всю логику представления назад из представления. к модели просмотра.

В полностью нормализованной модели Customer будет представлен в модели не строкой, а идентификатором. Модель, если ей нужно имя клиента, будет искать имя из таблицы Customers, используя CustomerID, найденный в таблице Orders.

Модель представления, с другой стороны, может больше интересоваться Nameклиентом, а не идентификатором. Идентификатор не нужен, если только Клиент не обновляется в Модели.

Кроме того, модель представления может и обычно принимает другую форму (если это не чистый CRUD ). Объект Invoice View Model может иметь имя клиента, адреса и позиции, но модель содержит клиентов и описания.

Другими словами, счета-фактуры обычно хранятся не так, как они отображаются.

Роберт Харви
источник
3

В некотором отношении вы правы: они оба ссылаются на один и тот же предметный объект, который называется order. Но вы ошибаетесь в том, что это не реальное дублирование, а другое представление, исходящее из разных целей. Если вы хотите сохранить свой заказ, вы, например, хотите получить доступ к каждому столбцу. Но вы не хотите просочиться наружу. Кроме того, проталкивать целые сущности через провод - это не то, что вам действительно нужно. Мне известны случаи, когда клиенту ordersотправлялась коллекция из МБ, где было бы достаточно нескольких КБ .

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

1) Инкапсуляция - скрытие информации вашего приложения

2) Малые модели быстро сериализуются и легко передаются

3) Они служат разным целям, хотя они частично совпадают, и поэтому должны быть разные объекты.

4) Иногда имеет смысл иметь для разных запросов разные объекты-значения . Я не знаю, как это в C #, но в Java можно использовать POJO для сбора результатов. Если мне нужно одно, orderя использую Order -Entity, но если мне нужны только столбцы, заполненные данными, использование меньших объектов более эффективно.

Томас Джанк
источник
Структура структуры сущностей состоит в том, что сущности являются простыми старыми объектами C #, поэтому отправка их по сети не представляет большой проблемы. В некоторых обстоятельствах я вижу полезность возможности посылать только часть содержимого заказа.
Роберт Рикеттс
Один или куча не проблема, как я уже сказал. Но есть случаи, когда у вас есть МБ данных, что замедляет время загрузки. Подумайте о времени загрузки мобильных устройств
Томас Джанк
0

(Отказ от ответственности: я только видел, как он используется таким образом. Возможно, я неправильно понял истинную цель этого. Относитесь к этому ответу с подозрением.)

Вот еще один недостающий бит: преобразование между Orderи OrderModel.

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

Как правило, эти два метода будут предоставлены «волшебным образом» (иногда называемые DI, IoC, MVVM или еще чем-то другим). То есть вместо того, чтобы реализовывать эти два метода преобразования как часть OrderModel, они будут принадлежать классу, предназначенному для взаимного преобразования этих вещей; этот класс будет каким-то образом зарегистрирован в фреймворке, чтобы фреймворк мог легко его найти.

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

Причина этого в том , что всякий раз , когда есть переход от OrderModelк , Orderили наоборот, вы знаете , что некоторые данные должны быть загружены из или сохранены в ОРМ.

rwong
источник
Если я держу их обоих, я определенно хотел бы настроить их так, чтобы преобразование было более автоматизированным
Роберт Рикеттс
@RobertRicketts То, что я увидел, выглядит так: IMvxValueConverter . Хотя, возможно, я неправильно понял его цель.
15:00