5

假设我有以下课程:

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()),这感觉很奇怪。另外,如果有更好的解决方案,请与我分享!我仍然是一个面向对象的新手。

4

5 回答 5

13

我通常会说不他们不应该,因为 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每次想要支持新用例时都修改以域为中心的对象。在我看来,最好在类中放置一些只读的公共属性,以便映射器可以访问他们需要的属性。

于 2010-04-05T22:46:04.203 回答
4

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

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

于 2010-04-05T22:30:18.873 回答
0

第一个(暴露 DTO)对我来说更可取。它更简单,运行速度更快,更容易理解和维护。由于 DTO 确实对数据库对象没有依赖关系,因此它仍然很容易实现减少依赖关系的目标。

于 2010-04-05T22:50:12.477 回答
0

我将 to/from 方法放在我的 DTO 上:

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

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

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

于 2010-04-05T22:52:15.863 回答
0

您的服务是 WCF 还是 WCF?

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

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

我的博客文章中的第一张图片显示了在客户端重用业务对象对象是多么容易(当您执行“添加服务引用”时会显示对话框)。博客文章有一些关于重用 biz 对象的陷阱之一的信息。

我不知道你想用 ExposeToReporter 实现什么,但你是对的,它看起来不正确,我个人会在 IReporter 上放置一个采用 ICameraData 参数的方法,然后在其中设置有关记者的详细信息。

dnrtv是一个很棒的学习资源。观看标题中包含 WCF 的所有内容,尤其是 Miguel Castro 的 Extreme WCF!

于 2010-04-05T23:03:56.923 回答