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

时间:2010-04-05 22:11:15

标签: 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;
}

...和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,让Camera公开其数据。这将消除对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 个答案:

答案 0 :(得分:13)

我通常会说 no ,它们不应该,因为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({{1}之间创建硬依赖关系})。你基本上拥有原始版本的所有易用性,但没有耦合。

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

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

答案 1 :(得分:4)

我通常这样做:业务对象在业务层DLL中是“纯粹的”。然后,我在WCF层添加一个Camera.MapToCameraDataContract扩展方法。我也特别在服务层上有反向扩展方法(CameraDataContract.MapToCamera)。

所以从本质上讲,我是第一种方式,但ToData方法是只有WCF层知道的扩展方法。

答案 2 :(得分:0)

第一个(暴露DTO)比我更好。它更简单,运行速度更快,更易于理解和维护。由于DTO确实不依赖于数据库对象,因此它仍然可以轻松实现减少依赖性的目标。

答案 3 :(得分:0)

我在我的DTO上自行设置了方法:

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

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

这样我的域对象就不必知道我的DTO了。

答案 4 :(得分:0)

您的服务WCF是否为WCF?

如果是,那么您可以选择将业务对象用作DTO(只要您的业务对象是持久性无知的)。如果您这样做,我建议您将CameraData类更改为ICameraData接口,并使Camera实现ICameraData。保持接口上的属性(DataContract等)。

然后,您可以将业务对象从客户端传递到服务器。请注意特定客户端或服务器端的任何逻辑。

blog post中的第一张图片显示了重新使用业务对象对象客户端是多么容易(对话框是“添加服务引用”时显示的内容)。这篇博文有一些重新使用商业对象的信息。

我无法告诉您要使用ExposeToReporter实现什么,但是你是对的,它看起来不对,我个人在IReporter上放了一个采用ICameraData参数的方法,然后在记者身上设置细节在那之内。

这个东西的一个很好的学习来源是dnrtv。在标题中观看WCF的所有内容,尤其是Miguel Castro的Extreme WCF!