Предположим, что у меня есть следующий класс:
class Camera
{
public Camera(
double exposure,
double brightness,
double contrast,
RegionOfInterest regionOfInterest)
{
this.exposure = exposure;
this.brightness = brightness;
this.contrast = contrast;
this.regionOfInterest = regionOfInterest;
}
public void ConfigureAcquisitionFifo(IAcquisitionFifo acquisitionFifo)
{
// do stuff to the acquisition FIFO
}
readonly double exposure;
readonly double brightness;
readonly double contrast;
readonly RegionOfInterest regionOfInterest;
}
... и DTO для переноса информации о камере через сервисную границу (WCF), скажем, для просмотра в WinForms/WPF/Web приложении:
using System.Runtime.Serialization;
[DataContract]
public class CameraData
{
[DataMember]
public double Exposure { get; set; }
[DataMember]
public double Brightness { get; set; }
[DataMember]
public double Contrast { get; set; }
[DataMember]
public RegionOfInterestData RegionOfInterest { get; set; }
}
Теперь я могу добавить метод к Camera
выставить его данные:
class Camera
{
// blah blah
public CameraData ToData()
{
var regionOfInterestData = regionOfInterest.ToData();
return new CameraData()
{
Exposure = exposure,
Brightness = brightness,
Contrast = contrast,
RegionOfInterest = regionOfInterestData
};
}
}
или, я могу создать метод, который требует, чтобы специальный IReporter был передан в том, чтобы Камера выставила свои данные. Это удаляет зависимость от уровня Contracts (Камера больше не должна знать о CameraData):
class Camera
{
// beep beep I'm a jeep
public void ExposeToReporter(IReporter reporter)
{
reporter.GetCameraInfo(exposure, brightness, contrast, regionOfInterest);
}
}
Таким образом, который я должен сделать? Я предпочитаю второе, но это требует, чтобы IReporter имел поле CameraData (который изменяется GetCameraInfo()
), который чувствует себя странным. Кроме того, если существует какое-либо еще лучшее решение, совместно используйте со мной! Я - все еще объектно-ориентированный newb.
Я бы обычно сказал нет , они не должны, потому что DTO специфичны для службы или приложения, тогда как модель предметной области является вашим «самым внутренним» уровнем и не должна иметь зависимостей. DTO - это детали реализации чего-то иного , чем модель предметной области, и поэтому они нарушают абстракцию для вашей модели предметной области, чтобы знать о них.
Рассматривали ли вы вариант использования AutoMapper для этого? Так вы в конечном итоге напишете намного меньше кода. В этом случае, я думаю, вы могли бы просто обойтись:
Mapper.CreateMap<RegionOfInterest, RegionOfInterestData>();
Mapper.CreateMap<Camera, CameraData>();
И позже:
CameraData cd = Mapper.Map<Camera, CameraData>(camera);
Это не только уменьшает отток кода, но и разделяет код сопоставления на его собственный «слой сопоставления» - он у вас есть. или несколько модулей, которые регистрируют эти сопоставления, которые вы можете вставить в любую сборку, действительно использующую DTO.
И, конечно же, вы всегда можете создать методы расширения для упрощения фактического сопоставления:
public static class CameraExtensions
{
public static CameraData ToCameraData(this Camera camera)
{
return Mapper.Map<Camera, CameraData>(camera);
}
}
Что делает все это так же просто, как написать camera.ToCameraData ()
, но без создание жесткой зависимости между объектом домена ( Camera
) и DTO ( CameraData
). У вас есть практически вся простота использования вашей исходной версии, но без привязки.
Если вы создаете эти зависимости, потому что пытаетесь создать объект CameraData
из частных данных Camera
, которые не публикуются публично, то моя немедленная реакция будет такой: не совсем верно об этом дизайне.Почему бы не сделать доступными только для чтения свойства объекта Camera
? Если вы все равно предоставляете доступ к ним внешнему миру с помощью метода ToData
, то вы явно не скрываете эту информацию, вы просто затрудняете доступ к ней.
Что, если через 3 месяца вы решите, что вам нужен другой тип DTO? Вам не нужно изменять объект Camera
, ориентированный на домен, каждый раз, когда вы хотите поддержать новый вариант использования. На мой взгляд, лучше поместить в класс несколько общедоступных свойств, доступных только для чтения, чтобы картографы могли получить доступ к нужным им атрибутам.
Первый (разоблачение DTO) для меня намного предпочтительнее. Это проще, будет работать быстрее, будет проще в понимании и обслуживании. Поскольку DTO действительно не зависит от объекта базы данных, он по-прежнему легко достигает цели уменьшения зависимостей.
Я помещаю методы to / from в свои DTO:
[DataContract]
public class CameraData
{
...
public Camera ToCamera() { ... }
public static CameraData FromCamera(Camera c) { ... }
}
Таким образом, мои объекты домена не должны знать о моих DTO.
Обычно я делаю это следующим образом: бизнес-объекты «чисты» в DLL (библиотеках) бизнес-уровня. Затем я добавляю метод расширения Camera.MapToCameraDataContract в слой WCF. У меня также обычно есть метод обратного расширения (CameraDataContract.MapToCamera) на уровне обслуживания.
По сути, я делаю это первым способом, но метод ToData - это метод расширения, о котором знает только уровень WCF.
Являются ли ваши службы WCF WCF?
Если да, то вы можете использовать свои бизнес-объекты в качестве DTO (пока ваши бизнес-объекты игнорируют постоянство). Если вы это сделали, я бы рекомендовал изменить ваш класс CameraData на интерфейс ICameraData и заставить Camera реализовать ICameraData. Сохраните атрибуты (DataContract и т. Д.) В интерфейсе.
Затем вы можете передать бизнес-объект от клиента к серверу. Помните о любой логике, относящейся конкретно к клиентской или серверной стороне.
Первое изображение в моем сообщении в блоге здесь показывает, насколько легко повторно использовать ваши бизнес-объекты на стороне клиента (диалоговое окно - это то, что отображается, когда вы делаете «добавление ссылки на службу»). В сообщении блога есть информация об одной из ловушек повторного использования бизнес-объектов.
Я не могу сказать, чего вы пытаетесь достичь с помощью ExposeToReporter, но вы правы, это выглядит неправильно, лично я бы поместил метод в IReporter, который принимает параметр ICameraData, а затем установил детали для репортера. внутри этого.
Замечательный источник изучения этого материала - dnrtv . Смотрите все с WCF в названии, но особенно Extreme WCF от Мигеля Кастро!