Я написал себе симпатичную простую небольшую модель предметной области с графом объектов, который выглядит так:
-- Customer
-- Name : Name
-- Account : CustomerAccount
-- HomeAddress : PostalAddress
-- InvoiceAddress : PostalAddress
-- HomePhoneNumber : TelephoneNumber
-- WorkPhoneNumber : TelephoneNumber
-- MobilePhoneNumber : TelephoneNumber
-- EmailAddress : EmailAddress
Эта структура полностью расходится с устаревшей базой данных, с которой мне приходится работать, поэтому я определил плоский DTO, который содержит данные для каждого элемента в графе клиентов - у меня есть представления и хранятся процедуры в базе данных, которые позволяют мне взаимодействовать с данными, используя эту плоскую структуру в обоих направлениях, все это работает отлично и здорово :)
Сглаживание модели предметной области в DTO для вставки / обновления - это просто, но у меня возникают проблемы с взятием DTO и созданием из него модели предметной области ... моей первой мыслью было реализовать посетителя, который будет посещать каждый элемент в граф клиента и при необходимости вставьте значения из DTO, примерно так:
class CustomerVisitor
{
public CustomerVisitor(CustomerDTO data) {...}
private CustomerDTO Data;
public void VisitCustomer(Customer customer)
{
customer.SomeValue = this.Data.SomeValue;
}
public void VisitName(Name name)
{
name.Title = this.Data.NameTitle;
name.FirstName = this.Data.NameFirstName;
name.LastName = this.Data.NameLastName;
}
// ... and so on for HomeAddress, EmailAddress etc...
}
Это теория, и это кажется хорошей идеей, когда все изложено просто так :)
Но для того, чтобы это работало, весь граф объектов должен быть построен до того, как посетитель erm посетил, иначе я бы получил NRE слева направо и по центру.
Что я хочу сделать, так это позволить посетителю назначать объекты графу, когда он посещает каждый элемент, с целью использовать шаблон особого случая для объектов, данные которых отсутствуют в DTO, например.
public void VisitMobilePhoneNumber(out TelephoneNumber mobileNumber)
{
if (this.Data.MobileNumberValue != null)
{
mobileNumber = new TelephoneNumber
{
Value = this.Data.MobileNumberValue,
// ...
};
}
else
{
// Assign the missing number special case...
mobileNumber = SpecialCases.MissingTelephoneNumber.Instance;
}
}
Я искренне думал, что это сработает, но C # выдает ошибку:
myVisitor.VisitHomePhone(out customer.HomePhoneNumber);
Поскольку вы не можете передавать параметры ref / out таким образом :(
Итак, мне осталось посетить независимые элементы и восстановить график, когда это будет сделано:
Customer customer;
TelephoneNumber homePhone;
EmailAddress email;
// ...
myVisitor.VisitCustomer(out customer);
myVisitor.VisitHomePhone(out homePhone);
myVisitor.VisitEmail(out email);
// ...
customer.HomePhoneNumber = homePhone;
customer.EmailAddress = email;
// ...
На данный момент я понимаю, что я довольно далеко от паттерна посетителей и гораздо ближе к фабрике, и я начинаю задаваться вопросом, не правильно ли я подошел к этому вопросу с самого начала ...
Кто-нибудь еще сталкивался с такой проблемой? Как вы это преодолели? Существуют ли какие-либо шаблоны проектирования, которые хорошо подходят для этого сценария?
Приносим извинения за такой длинный вопрос, и молодцы, что дочитали до этого места :)
ИЗМЕНИТЬ В ответ на полезные ответы Флориана Грайнахера и gjvdkamp я остановился на относительно простой реализации фабрики, которая выглядит следующим образом:
class CustomerFactory
{
private CustomerDTO Data { get; set; }
public CustomerFactory(CustomerDTO data) { ... }
public Customer CreateCustomer()
{
var customer = new Customer();
customer.BeginInit();
customer.SomeFoo = this.Data.SomeFoo;
customer.SomeBar = this.Data.SomeBar
// other properties...
customer.Name = this.CreateName();
customer.Account = this.CreateAccount();
// other components...
customer.EndInit();
return customer;
}
private Name CreateName()
{
var name = new Name();
name.BeginInit();
name.FirstName = this.Data.NameFirstName;
name.LastName = this.Data.NameLastName;
// ...
name.EndInit();
return name;
}
// Methods for all other components...
}
Затем я написал класс ModelMediator для обработки взаимодействия между уровнем данных и моделью предметной области ...
class ModelMediator
{
public Customer SelectCustomer(Int32 key)
{
// Use a table gateway to get a customer DTO..
// Use the CustomerFactory to construct the domain model...
}
public void SaveCustomer(Customer c)
{
// Use a customer visitor to scan for changes in the domain model...
// Use a table gateway to persist the data...
}
}