【问题标题】:Resolving a call-chain anti-pattern解决调用链反模式
【发布时间】:2011-03-22 16:13:13
【问题描述】:

我已经开始注意到我的 ASP.NET 开发中的一些反模式。这让我很困扰,因为感觉保持良好设计是正确的做法,但同时它闻起来很不对劲。

问题是这样的:我们有一个多层应用程序,底层是一个类,处理对向我们提供数据的服务的调用。上面是一层可以转换、操作和检查数据的类。上面是 ASP.NET 页面。

在许多情况下,来自服务层的方法在进入视图之前不需要任何更改,因此模型只是直接通过,例如:

public List<IData> GetData(int id, string filter, bool check)
{
    return DataService.GetData(id, filter, check);
}

这并没有错,也不一定很糟糕,但它会产生一种奇怪的复制/粘贴依赖性。我也在做底层服务,它也大量复制了这种模式,并且自始至终都有接口。所以发生的事情是,“我需要将int someotherID 添加到GetData”所以我将它添加到模型、服务调用者、服务本身和接口中。 GetData 实际上代表了几种方法,这些方法都使用相同的签名但返回不同的信息,这并没有帮助。界面对这种重复有所帮助,但它仍然会时不时出现。

这个反模式有名字吗?是否有解决方案,或者对架构进行重大更改是唯一真正的方法?听起来我需要展平我的对象模型,但有时数据层正在进行转换,因此它具有价值。我还喜欢将我的代码在“调用外部服务”和“提供页面数据”之间分开。

【问题讨论】:

    标签: c# asp.net anti-patterns


    【解决方案1】:

    我建议您使用query object pattern 来解决此问题。基本上,您的服务可能具有如下签名:

    IEnumerable<IData> GetData(IQuery<IData> query);
    

    在 IQuery 接口中,您可以有一个将工作单元作为输入的方法,例如事务上下文或类似 ISession(如果您使用的是 NHibernate 等 ORM 并返回 IData 对象列表)。

    public interface IQuery<T> 
    {
     IEnumerable<T> DoQuery(IUnitOfWork unitOfWork);
    }
    

    这样,您可以创建符合您要求的强类型查询对象,并为您的服务提供一个干净的接口。来自 Ayende 的 This article 很好地了解了这个主题。

    【讨论】:

    • 我认为您的回答可以很好地解决我遇到的问题,而无需进行大量更改。其他一些人提出了类似的建议,但你实际上给它起了个名字。我非常专注于抽象函数本身,我没有想到我可以捆绑参数列表来单独抽象出那个元素。
    【解决方案2】:

    听起来你需要另一个接口,所以方法变成这样:

    public List<IData> GetData(IDataRequest request)
    

    【讨论】:

      【解决方案3】:

      您正在委派到另一层,这根本不是一件坏事。

      您可以在此处添加一些其他逻辑,或者在其他方法中添加仅属于该层的其他逻辑,或者换成将层委托给另一个实现,因此它当然可以很好地利用层有问题。

      你可能有太多的层次,但我不会仅仅因为看到这个而这么说,更多的是因为没有看到其他任何东西。

      【讨论】:

        【解决方案4】:

        根据您的描述,听起来您在应用程序中遇到了抽象的“权衡”之一。

        考虑那些“调用链”不再“传递”数据但需要一些转换的情况。现在可能不需要它,当然可以为YAGNI 制作案例。

        但是,在这种情况下,似乎不需要太多的技术债务来处理,因为能够轻松地在层之间对数据进行更改。

        【讨论】:

          【解决方案5】:

          我也使用这种模式。但是,我将它用于将我的域模型对象与我的数据对象分离。

          在我的例子中,我没有像您在示例中那样“通过”来自数据层的对象,而是将其“映射”到位于我的域层中的另一个对象。我使用AutoMapper 来消除手动操作的痛苦。

          在大多数情况下,我的域对象看起来与它源自的数据对象完全相同。但是,有时我需要展平来自我的数据对象的信息……或者我可能对我的数据对象中的所有内容等都不感兴趣。我将数据对象映射到仅包含字段的自定义域对象我的领域层感兴趣。

          这还有一个副作用,当我决定重新分解或更改我的数据层以进行其他操作时,它不必影响我的域对象,因为它们是使用映射技术解耦的。

          这是对自动映射器的描述,我认为这是这种设计模式试图实现的目标:

          AutoMapper 面向模型投影场景,将复杂的对象模型展平为 DTO 和其他简单对象,其设计更适合序列化、通信、消息传递,或者只是域和应用层之间的防损坏层

          【讨论】:

            【解决方案6】:

            其实,你选择走的路,才是拥有你所拥有的原因(我不是说不好)。

            首先,我说你的做法很正常。

            现在,让我想想你的图层:

            • 您的服务 - 提供某种强类型访问模型。这意味着它有一些类型的参数,在一些特殊类型的方法中使用它们,这些方法再次返回一些特殊类型的结果。
            • 您的服务访问层 - 也提供相同类型的模型。这样它就可以为特殊类型的方法使用特殊类型的参数,返回特殊类型的结果。
            • 等等...

            为了不混淆,这里是我所说的特殊种类

            public UserEntity GetUserByID(int userEntityID);
            

            在此示例中,您需要准确传递 Int,同时准确调用 GetUserByID,它将准确返回 UserEntity 对象。

            现在是另一种方法

            还记得SqlDataReader 的工作原理吗?不是强类型,对吧? 在我看来,您在这里要求的是您缺少一些非强类型层。

            为此:您需要在图层中的某处从强类型切换到非强类型。

            Example:

            public Entity SelectByID(IEntityID id);
            public Entity SelectAll();
            

            所以,如果你有这样的东西而不是服务访问层,那么你可以为你想要的任何参数调用它。

            但是,这几乎是在创建自己的 ORM,所以我认为这不是最好的方法。

            【讨论】:

              【解决方案7】:

              必须定义什么样的责任归于哪一层,并将这种逻辑只放在它所属的层中。

              如果您不必在特定方法中添加任何逻辑,则只是通过是绝对正常的。有时您可能需要这样做,而抽象层将在那时获得回报。

              最好有并行的层次结构,而不仅仅是向上传递底层层的对象,所以每个层都使用它自己的类层次结构,如果你觉得层次结构没有太大区别,你可以使用 AutoMapper 之类的东西。这为您提供了灵活性,并且您始终可以用特定方法/类中的自定义映射代码替换自动映射,以防层次结构不再匹配。

              如果你有许多签名几乎相同的方法,那么你应该考虑查询规范模式。

              IData GetData(IQuery<IData> query)
              

              然后,在表示层中,您可以为自定义查询规范对象实现数据绑定器,其中单个 aspnet 处理程序可以实现特定查询对象的创建,并将它们传递给单个服务方法,该方法将其传递给单个存储库方法,它可以根据特定的查询类进行调度,可能带有访问者模式。

              IQuery<IData> BindRequest(IHttpRequest request)
              

              通过这种自动映射和查询规范模式,您可以将重复减少到最低限度。

              【讨论】:

                猜你喜欢
                • 2014-09-23
                • 2021-10-16
                • 1970-01-01
                • 1970-01-01
                • 2020-08-30
                • 2023-04-07
                • 1970-01-01
                • 2015-02-21
                • 1970-01-01
                相关资源
                最近更新 更多