代码之家  ›  专栏  ›  技术社区  ›  BenAlabaster

.NET重构,干的。双重继承、数据访问和关注点分离

  •  5
  • BenAlabaster  · 技术社区  · 14 年前

    背景故事:

    因此,在过去的几天晚上,我一直被困在一个架构问题上,在一个我一直在玩弄的重构上。没什么重要的,但一直困扰着我。这其实是一种锻炼 DRY ,并试图将其发展到DAL体系结构这样一个极端 完全地

    代码部分基于 @JohnMacIntyre http://whileicompile.wordpress.com/2010/08/24/my-clean-code-experience-no-1/ . 我已经稍微修改了代码,就像我倾向于做的那样,为了让代码更进一步-通常,只是为了看看我能从一个概念中得到多少额外的里程数。。。不管怎样,我的理由基本上无关紧要。

    我的数据访问层的一部分基于以下体系结构:

    abstract public class AppCommandBase : IDisposable { }
    

    这包含一些基本的内容,比如创建一个command对象,以及在AppCommand被释放后的清理。我所有的命令库对象都是从这个派生的。

    abstract public class ReadCommandBase<T, ResultT> : AppCommandBase
    

    它包含影响所有read命令的基本内容—特别是在本例中,从表和视图中读取数据。不编辑,不更新,不保存。

    abstract public class ReadItemCommandBase<T, FilterT> : ReadCommandBase<T, T> { }
    

    public class MyTableReadItemCommand : ReadItemCommandBase<MyTableClass, Int?> { }
    

    它包含定义我的表名、表或视图中的字段列表、键字段的名称、将数据从IDataReader行解析到我的业务对象的方法以及启动整个流程的方法的特定属性。

    现在,我的阅读列表也有这个结构。。。

    abstract public ReadListCommandBase<T> : ReadCommandBase<T, IEnumerable<T>> { }
    public class MyTableReadListCommand : ReadListCommandBase<MyTableClass> { }
    

    不同之处在于,列表类包含与列表生成相关的属性(即PageStart、PageSize、Sort和返回IEnumerable)与返回单个数据对象(只需要标识唯一记录的筛选器)的属性。

    问题:

    我的第一个想法是双重继承可以很好地解决这个问题,尽管我同意双重继承通常是一种代码味道——但它可以很优雅地解决这个问题。那么,既然.NET不支持双重继承,我该怎么办呢?

    也许一个不同的重构更合适。。。但我在想如何避开这个问题时遇到了困难。

    如果有人需要一个完整的代码库来了解我在唠叨什么,我的DropBox上有一个原型解决方案 http://dl.dropbox.com/u/3029830/Prototypes/Prototype%20-%20DAL%20Refactor.zip . 有问题的代码在DataAccessLayer项目中。

    另外,这不是一个正在进行的活动项目的一部分,它更像是一个重构难题,我自己的娱乐。

    提前谢谢各位,我很感激。

    5 回复  |  直到 14 年前
        1
  •  4
  •   Neal    14 年前

    将结果处理与数据检索分开。您的继承层次结构在ReadCommandBase中已经足够深了。

    定义接口IDatabaseResultParser。实现ItemDatabaseResultParser和ListDatabaseResultParser,两者都使用ReadCommandBase类型的构造函数参数(也可以将其转换为接口)。

    当你打电话给我DatabaseResultParser.Value()它执行命令,解析结果并返回类型为T的结果。

    偏爱合成而不是继承。

    看了样品我会更进一步。。。

    1. 将查询生成与查询执行和结果解析分开—现在您可以大大简化查询执行实现,因为它要么是返回元组枚举的读取操作,要么是返回受影响行数的写入操作。
    2. 您的查询生成器都可以封装在一个类中,以包含分页/排序/筛选,但是围绕这些类构建某种形式的有限结构可能更容易,这样您就可以将分页、排序和筛选分离开来。如果我这样做的话,我就不用费心构建查询了,我只需在一个对象中编写sql,这个对象允许我传入一些参数(实际上是c#中的存储过程)。

        2
  •  1
  •   Chris Ammerman    14 年前

    我认为简单的答案是,在一个“为了保护你”而禁止多重继承的体系中,战略/授权是必要的 直接替代品。是的,您仍然会得到一些并行结构,例如委托对象的属性。但在语言的范围内,它被尽可能地最小化。

    另一个大的替代方法是重构更大的设计结构,这样就可以从本质上避免这种情况,即给定的类由继承树中其上的多个“兄弟”或“表亲”类的行为组合而成。简而言之,重构为继承 而不是遗产

    在采用我推荐的这种策略时,您将面临的挑战是,您已经在设计中做出了让步:您正在为“item”和“list”情况下的不同SQL进行优化。不管怎样,保持现状都会妨碍你,因为你给了他们平等的待遇,所以他们必然是兄弟姐妹。所以我想说,你想要摆脱这种“局部最大值”的设计优雅的第一步是回滚优化,并将单个项目视为它真正的样子:一个列表的特例,只有一个元素。您可以尝试稍后再次为单个项重新引入优化。但是等到你解决了目前困扰你的优雅问题。

    但您必须承认,除了优雅的C代码之外,任何优化都会给C代码的优雅设计设置障碍。这种权衡,就像算法设计的“内存空间”共轭一样,是编程本质的基础。

        3
  •  0
  •   x0n    14 年前

    正如柯克所提到的,这是授权模式。当我这样做时,我通常构造一个由委托者和委托类实现的接口。这减少了感知到的代码气味,至少对我来说是这样。

        4
  •  0
  •   Erik Funkenbusch    14 年前

    不那么简单的答案是,您可以使用代码生成工具、工具、代码dom和其他技术将所需的对象注入到所需的类中。它仍然会在内存中创建重复,但会简化源代码(代价是代码注入框架中增加了复杂性)。

    这看起来可能和其他解决方案一样不令人满意,但是如果你仔细想想,这就是支持MI的语言在幕后所做的,连接在源代码中看不到的委托系统。

    问题归根结底是,在简化源代码方面,您愿意付出多少努力。想想看,这是相当深刻的。

        5
  •  0
  •   Jordão    14 年前

    我没有深入研究过您的场景,但是我对C#中的双重层次问题有一些想法。为了在双重层次结构中共享代码,我们需要在语言中使用不同的构造:mixin、 trait (pdf) C# research -pdf )或者一个角色(比如 perl 6 ). C#使得用继承来共享代码非常容易(这不是代码重用的合适操作符),并且通过组合来共享代码非常困难(你知道,你必须手工编写所有的委托代码)。

    有很多方法可以获得 kind of mixin

    氧化剂( download )语言(用于.NET的对象Pascal)也有一个有趣的特性 interface delegation