Должны ли бизнес-объекты создавать собственные DTO? - PullRequest
5 голосов
/ 06 апреля 2010

Предположим, у меня есть следующий класс:

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 (Camera больше не должна знать о CameraData):

class Camera
{
    // beep beep I'm a jeep

    public void ExposeToReporter(IReporter reporter)
    {
        reporter.GetCameraInfo(exposure, brightness, contrast, regionOfInterest);
    }
}

Так что мне делать? Я предпочитаю второе, но оно требует, чтобы у IReporter было поле CameraData (которое изменяется на GetCameraInfo()), что кажется странным. Кроме того, если есть еще лучшее решение, пожалуйста, поделитесь со мной! Я все еще объектно-ориентированный новичок.

Ответы [ 5 ]

13 голосов
/ 06 апреля 2010

Я бы вообще сказал нет , они не должны этого делать, потому что 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 каждый раз, когда вы хотите поддержать новый вариант использования. На мой взгляд, лучше добавить в класс несколько общедоступных свойств, доступных только для чтения, чтобы картостроители могли получить доступ к необходимым им атрибутам.

4 голосов
/ 06 апреля 2010

Я обычно так поступаю: бизнес-объекты являются «чистыми» в DLL (библиотеках) бизнес-уровня. Затем я добавляю метод расширения Camera.MapToCameraDataContract в слой WCF. У меня также обычно есть метод обратного расширения (CameraDataContract.MapToCamera) на уровне сервиса.

По сути, я делаю это первым способом, но метод ToData - это метод расширения, о котором знает только слой WCF.

0 голосов
/ 06 апреля 2010

Ваши услуги WCF для WCF?

Если это так, вы можете использовать свои бизнес-объекты в качестве DTO (если ваши бизнес-объекты невежественны). Если вы сделали это, я бы порекомендовал изменить класс CameraData на интерфейс ICameraData и заставить Camera реализовывать ICameraData. Сохраняйте атрибуты (DataContract и т. Д.) В интерфейсе.

Затем вы можете передать бизнес-объект от клиента к серверу. Помните о любой логике, которая конкретно относится к стороне клиента или сервера.

Первое изображение в моем блоге показывает, как легко повторно использовать объекты на стороне вашего бизнес-объекта на стороне клиента (диалоговое окно - это то, что отображается, когда вы делаете «добавить ссылку на сервис»). В сообщении блога есть некоторая информация об одном из способов повторного использования бизнес-объектов.

Я не могу сказать, чего вы пытаетесь достичь с помощью ExposeToReporter, но вы правы, это выглядит неправильно, лично я бы поставил метод на IReporter, который принимает параметр ICameraData, а затем установил детали для репортера в этом.

Удивительным источником изучения этого материала является dnrtv . Смотрите все с WCF в названии, но особенно Extreme WCF от Miguel Castro!

0 голосов
/ 06 апреля 2010

Я помещаю методы to / from в мои DTO:

[DataContract]
public class CameraData
{
    ...
    public Camera ToCamera() { ... }

    public static CameraData FromCamera(Camera c) { ... }
}

Таким образом, мои доменные объекты не должны знать о моих DTO.

0 голосов
/ 06 апреля 2010

Первое (разоблачение DTO) гораздо предпочтительнее для меня. Это проще, будет работать быстрее, будет легче понять и поддерживать. Поскольку DTO действительно не зависит от объекта базы данных, он все же легко достигает цели уменьшения зависимостей.

...