【问题标题】:Does Adapter pattern conflict with encapsulation?适配器模式是否与封装冲突?
【发布时间】:2010-12-08 19:45:33
【问题描述】:

很长一段时间以来,我一直使用适配器模式将我的 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,那么我不能将这些属性的分配委托给另一个对象,例如适配器。

想法?

【问题讨论】:

    标签: design-patterns tdd soa adapter


    【解决方案1】:

    经过深思熟虑和额外阅读后,我上面描述的方法实际上更符合桥接模式。这种认识帮助我看到了缺失的部分——适配器!就像 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 闻名)在他的书中有一篇关于这个主题的精彩论文。我的解决方案是通过将所有这些代码放在一个单独的程序集中并为所有应该对消费代码显示为只读的属性提供设置器内部范围来“伪造”封装。这允许适配器设置属性,同时防止客户端代码执行相同操作。

    它并不完美,但它是一个解决方案。如果您有其他看起来不那么花哨的想法,我愿意听取他们的意见!

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-10-14
        • 1970-01-01
        • 2017-08-01
        • 1970-01-01
        • 2011-07-24
        • 1970-01-01
        • 1970-01-01
        • 2011-02-06
        相关资源
        最近更新 更多