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

Design patterns 适配器模式与封装冲突吗?,design-patterns,tdd,soa,adapter,Design Patterns,Tdd,Soa,Adapter,很长一段时间以来,我一直利用适配器模式从调用代码中抽象出我的WCF服务客户端,这样我就可以在不依赖服务客户端的情况下有效地单元测试我的业务对象。例如: public class MyBusinessObject { private ITheService _service; public MyBusinessObject(ITheService service) { _service = service; } public void DoSomethingOnTheS

很长一段时间以来,我一直利用适配器模式从调用代码中抽象出我的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,那么我不能将这些属性的分配委托给另一个对象,比如适配器


想法?

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

在对其进行更多思考和阅读之后,我上面描述的方法实际上更符合桥接模式。这一认识帮助我看到了缺失的部分——适配器!正如Massimiliano所说,我现在有了一个适配器,它位于我的业务对象和服务之间。适配器负责“适配”POCO/DTO/Entity/。。。由WCF服务向我的业务对象公开/从我的业务对象公开

我的业务对象不再在其构造函数中引用服务(ITEService),而是引用服务适配器(ITEServiceAdapter)。此界面看起来像:

internal interface ITheServiceAdapter
{
    void DoSomething();
    MyBusinessObject GetData();
}
在具体实现(ServiceAdapter)中,我使用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);
    }
}
内部类TheServiceAdapter:itheseserviceadapter
{
私人资讯科技服务(it);;
公共服务适配器(ITheService服务){u service=service;}
公共void DoSomething(){…}
公共MyBusinessObject GetData()
{
var data=_service.GetData();
返回Mapper.Map(数据);
}
}
这非常有效,满足了我从业务对象抽象服务实现的需求。唯一绑定到WCF代理类型的代码是适配器。另外,通过注入服务适配器的模拟实现,我仍然可以干净地对业务对象进行单元测试。而且,因为我选择信任AutoMapper,所以我不需要对适配器类进行单元测试,并且将通过集成测试捕获代码中的任何问题。所以,一切都很好,对吗

当然,这仍然没有解决封装问题。幸运的是,Rockford Lhotka(CSLA名人)在他的书中有一篇关于这个主题的伟大论文。我的解决方案是“伪造”封装,将所有这些代码放在一个单独的程序集中,并为setters提供所有属性的内部作用域,这些属性对于消费代码来说应该是只读的。这允许适配器设置属性,同时防止客户端代码执行相同的操作

这并不完美,但它是一个解决方案。如果你有其他的想法,似乎不是那么诡计多端,我愿意听他们