适配器模式与封装冲突吗?

发布于 2024-10-06 16:24:39 字数 969 浏览 4 评论 0原文

很长一段时间以来,我一直利用适配器模式从调用代码中抽象出我的 WCF 服务客户端,以便我可以有效地对我的业务对象进行单元测试,而不依赖于服务客户端。例如:

public class MyBusinessObject
{
    private ITheService _service;

    public MyBusinessObject(ITheService service) { _service = service; }

    public void DoSomethingOnTheServer() { _service.DoSomething(); }
}

现在适配器的接口和具体实现公开了与服务代理本身相同的契约。因此,继续这个例子:

public interface ITheService
{
    void DoSomething();
    ServerObject GetData();
}

public class DefaultService : ITheService
{
    public void DoSomething() { ... }
    public ServerObject GetData()
    {
        using (var proxy = new ActualServiceClient())
        {
            return proxy.GetData();
        }
    }
}

这工作得很好,我能够有效地测试我的业务对象等。

我的问题源于这样一个事实:我从与服务强耦合的第二个方法返回一个类型。如果适配器返回我要使用的类型的实例而不是来自服务的 DTO/代理,这不是更有意义并且与适配器模式更一致吗?

如果是这样的话,那么我担心的是封装。在典型的用例中,调用该服务来检索数据,然后将其填充到我的业务对象中。如果我想将只读属性公开给我的 UI,那么我无法将这些属性的分配委托给另一个对象,例如适配器。

想法?

For quite a while now I've utilized the Adapter pattern to abstract my WCF service clients from calling code so that I can effectively unit test my business objects without dependence on the service clients. For example:

public class MyBusinessObject
{
    private ITheService _service;

    public MyBusinessObject(ITheService service) { _service = service; }

    public void DoSomethingOnTheServer() { _service.DoSomething(); }
}

Right now the interface and concrete implementation of the adapter expose the same contract as the service proxy itself. So, continuing the example:

public interface ITheService
{
    void DoSomething();
    ServerObject GetData();
}

public class DefaultService : ITheService
{
    public void DoSomething() { ... }
    public ServerObject GetData()
    {
        using (var proxy = new ActualServiceClient())
        {
            return proxy.GetData();
        }
    }
}

This works well and good and I am able to effectively test my business objects, etc.

My problem stems from the fact that I am returning a type from the second method that is strongly-coupled to the service. Wouldn't it make more sense and be more consistent with the Adapter pattern if the adapter returned an instance of the type I'm going to use rather than the DTO/proxy from the service?

If that is the case, then I am concerned about encapsulation. In a typical use case, the service is being called to retrieve data which is then populated into my business object. If I want to have read-only properties exposed to my UI, then I can't delegate assignment of these properties to another object, like the adapter.

Thoughts?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我的黑色迷你裙 2024-10-13 16:24:39

经过更多的思考和一些额外的阅读后,我上面描述的方法实际上更遵循桥接模式。这种认识帮助我找到了缺失的部分——适配器!就像 Massimiliano 所说的那样,我现在有一个适配器位于我的业务对象和服务之间。适配器负责将 WCF 服务公开的 POCO/DTO/Entity/... 与我的业务对象“适配”。

我的业务对象不再在其构造函数中引用服务 (ITheService),而是引用服务适配器 (ITheServiceAdapter)。该接口如下所示:

internal interface ITheServiceAdapter
{
    void DoSomething();
    MyBusinessObject GetData();
}

在具体实现(TheServiceAdapter)中,我使用 AutoMapper 将实际服务返回的基于服务器的 POCO/DTO“调整”为我的业务对象,例如:

internal class TheServiceAdapter : ITheServiceAdapter
{
    private ITheService _service;

    public TheServiceAdapter(ITheService service) { _service = service; }

    public void DoSomething() { ... }

    public MyBusinessObject GetData()
    {
        var data = _service.GetData();

        return Mapper.Map<ServiceObject, MyBusinessObject>(data);
    }
}

这非常有效,并且满足我抽象服务实现的要求来自我的业务对象。与 WCF 代理类型相关的唯一代码是适配器。另外,我仍然可以通过注入服务适配器的模拟实现来干净地对我的业务对象进行单元测试。而且,因为我选择信任 AutoMapper,所以我不需要对适配器类进行单元测试,并将通过集成测试捕获该代码中的任何问题。那么,一切都很好——对吧?

当然这仍然没有解决封装的问题。幸运的是,Rockford Lhotka(因 CSLA 出名)在他的书中就该主题发表了一篇精彩的论文。我的解决方案是通过将所有这些代码放在一个单独的程序集中,并为所有属性的设置器提供内部范围,这些属性对于使用代码来说应该是只读的,从而“伪造”封装。这允许适配器设置属性,同时防止客户端代码执行相同的操作。

它并不完美,但它是一个解决方案。如果您有其他看起来不那么诡计多端的想法,我很乐意听取它们!

After giving it more thought and some additional reading, the approach I described above is more actually following the Bridge pattern. That realization helped me see the missing piece - the adapters! So much like Massimiliano was saying, I now have an adapter that sits between my business object and the service. The adapter is responsible for "adapting" the POCO/DTO/Entity/... exposed by the WCF service to/from my business objects.

Instead of my business object taking a reference to the service (ITheService) in its constructor, it now takes a reference to a service adapter (ITheServiceAdapter). This interface looks like:

internal interface ITheServiceAdapter
{
    void DoSomething();
    MyBusinessObject GetData();
}

In the concrete implementation (TheServiceAdapter), I use AutoMapper to "adapt" the server-based POCO/DTO returned by the actual service to my business object like:

internal class TheServiceAdapter : ITheServiceAdapter
{
    private ITheService _service;

    public TheServiceAdapter(ITheService service) { _service = service; }

    public void DoSomething() { ... }

    public MyBusinessObject GetData()
    {
        var data = _service.GetData();

        return Mapper.Map<ServiceObject, MyBusinessObject>(data);
    }
}

This works great and satisfies my requirement to abstract the service implementation from my business objects. The only code that is tied to the WCF proxy types is the adapter. Plus, I can still unit test my business objects cleanly by injecting a mock implementation of the service adapter. And, because I choose to trust AutoMapper, I have no need to unit test the adapter classes and will catch any issues in that code through integration tests. So, all is good - right?

Of course this still didn't address the question of encapsulation. Fortunately, Rockford Lhotka (of CSLA fame) has a great dissertation on the subject in his book. My solution was to "fake" encapsulation by placing all of this code in a separate assembly and giving the setters internal scope for all properties that should appear read-only to consuming code. This allows the adapter to set the properties while preventing client code from doing the same.

It's not perfect, but it's a solution. If you have other ideas that don't seem to be as trick-ish, I'm open to hearing them!

做个少女永远怀春 2024-10-13 16:24:39

服务永远不应该返回仅在“服务器端世界(WCF)”上有意义的对象,这不仅仅是一个耦合问题
我可以建议您应该创建一个由 WCF 返回的 POCO 对象。
您可以根据需要创建此对象:在您的情况下,您可以添加将公开给 UI 的只读属性。当然,您需要一个将复杂/服务器对象转换为 POCO 对象的对象。要实现此目的,您可以创建一个由适配器使用的类来构建 POCO 对象

A service should never return an object that makes sense only on the "server side world(WCF)" it is not just a coupling matter
What I can suggest is you should create a POCO object that will be returned by the WCF.
You can create this object as you want: in you case you can add only read-only property that will be exposed to the UI. Of course you need an object that converts your complex/server object into POCO object.To achieve this you may create a class that is used by the adapter to build-up the POCO objects

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文