适配器模式是否与封装冲突?

时间:2010-12-08 19:45:33

标签: design-patterns tdd soa adapter

很长一段时间以来,我已经利用Adapter模式从调用代码中抽象出我的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 /代理,那么它是否会更有意义并且与Adapter模式更一致?

如果是这种情况,那么我担心封装。在典型的用例中,调用服务来检索数据,然后将数据填充到我的业务对象中。如果我想要将只读属性暴露给我的UI,那么我就不能将这些属性的赋值委托给另一个对象,比如适配器。

思想?

2 个答案:

答案 0 :(得分:1)

在给予它更多的思考和一些额外的阅读之后,我上面描述的方法更实际上遵循Bridge模式。这种认识帮助我看到了缺失的部分 - 适配器!就像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成名)在他的书中有关于这个主题的伟大论文。我的解决方案是通过将所有这些代码放在一个单独的程序集中来“伪装”封装,并为所有属性提供setter内部作用域,这些属性对于使用代码应该是只读的。这允许适配器设置属性,同时防止客户端代码执行相同的操作。

这不是完美的,但它是一个解决方案。如果你有其他的想法似乎不是什么伎俩,我很乐意听到它们!

答案 1 :(得分:0)

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