业务对象应该能够创建自己的DTO吗?

xof*_*ofz 5 c# oop dto

假设我有以下课程:

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;
}
Run Code Online (Sandbox Code Playgroud)

...和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; }
}
Run Code Online (Sandbox Code Playgroud)

现在我可以添加一个方法Camera来公开它的数据:

class Camera
{
    // blah blah

    public CameraData ToData()
    {
        var regionOfInterestData = regionOfInterest.ToData();

        return new CameraData()
        {
            Exposure = exposure,
            Brightness = brightness,
            Contrast = contrast,
            RegionOfInterest = regionOfInterestData
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,我可以创建一个方法,要求传入一个特殊的IReporter,让Camera公开其数据.这消除了对Contracts图层的依赖性(Camera不再需要了解CameraData):

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

    public void ExposeToReporter(IReporter reporter)
    {
        reporter.GetCameraInfo(exposure, brightness, contrast, regionOfInterest);
    }
}
Run Code Online (Sandbox Code Playgroud)

那我该怎么办?我更喜欢第二种,但它需要IReporter有一个CameraData字段(由其改变GetCameraInfo()),这感觉很奇怪.此外,如果有更好的解决方案,请与我分享!我仍然是一个面向对象的新手.

Aar*_*ght 13

我一般会说没有,他们不应该,因为DTO的是特定的服务或应用程序,而该领域模型是你的"最里面的"层,应该没有依赖性.DTO的是什么实现细节比域模型,因此,它打破了抽象为您的域模型,以了解他们.

你考虑过为AutoMapper看过吗?你最终会以这种方式编写更少的代码.在这种情况下,我认为你可以简单地逃脱:

Mapper.CreateMap<RegionOfInterest, RegionOfInterestData>();
Mapper.CreateMap<Camera, CameraData>();
Run Code Online (Sandbox Code Playgroud)

后来:

CameraData cd = Mapper.Map<Camera, CameraData>(camera);
Run Code Online (Sandbox Code Playgroud)

这不仅减少了代码流失,而且将映射代码划分到自己的"映射层"中 - 您有一个或多个模块可以注册这些映射,您可以将它们放在真正使用DTO的任何程序集中.

当然,您始终可以创建扩展方法来简化实际映射:

public static class CameraExtensions
{
    public static CameraData ToCameraData(this Camera camera)
    {
        return Mapper.Map<Camera, CameraData>(camera);
    }
}
Run Code Online (Sandbox Code Playgroud)

这使整个事情像写作一样简单camera.ToCameraData(),但没有在域对象(Camera)和DTO(CameraData)之间创建硬依赖.您基本上拥有原始版本的所有易用性,但没有耦合.

如果您正在创建这些依赖项,因为您尝试从未公开公开的CameraData私有Camera数据创建对象,那么我的立即反应将是,这个设计并不完全正确.为什么不在Camera对象上公开只读属性?无论如何,如果你通过这种ToData方法让外界访问它们,那么你显然没有隐藏这些信息,你只是让它变得更加麻烦.

如果您决定在未来3个月内需要不同类型的DTO,该怎么办?Camera每次要支持新用例时,都不必修改以域为中心的对象.在我看来,更好的是在类中放置一些只读的公共属性,以便映射器可以访问他们需要的属性.