Я написал себе неплохую небольшую доменную модель с графиком объекта, который выглядит так:
-- 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...
}
Это теория, и она кажется хорошей идеей, когда она складывается просто так:)
Но для этого для работы весь граф объектов нужно будет построить до посетителя, посетив, иначе я бы получил NRE слева и в центре.
То, что я хочу сделать, - позволить посетителю назначать объекты на график при посещении каждого элемента, при этом целью является использование шаблона Special Case для объектов, где данные отсутствуют в 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;
}
}
Я честно думал, что это сработает, но С# выдает мне ошибку:
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;
// ...
На этом этапе я знаю, что я довольно далеко от шаблона посетителя и гораздо ближе к Factory, и я начинаю задаваться вопросом, с самого начала ли я с этим не соглашался...
Кто-нибудь еще сталкивается с такой проблемой? Как вы его преодолели? Существуют ли какие-либо шаблоны проектирования, которые хорошо подходят для этого сценария?
Извините за публикацию такого looong вопроса и хорошо сделано для прочтения до сих пор:)
EDIT В ответ на полезные ответы от Florian Greinacher и gjvdkamp, я остановился на относительно простой реализации factory, которая выглядит так:
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...
}
}