如何将SOLID原则实现到现有项目中

我为这个问题的主观性而道歉,但我有点卡住了,我会感谢任何以前必须处理这个问题的人的指导和建议:

我有(成为什么)一个用C#2.0编写的非常大的RESTful API项目,我的一些类变得非常可怕。 我的主要API类就是一个例子 – 有几十个成员和方法(可能接近数百个)。 你可以想象,它变成了一个小噩梦,不仅仅是维护这段代码,甚至只是导航代码已经成为一件苦差事。

我是SOLID原则的新手,我是设计模式的忠实粉丝(但我仍处于可以实现它们的那个阶段,但还不足以知道何时使用它们 – 在不太明显的情况下) 。

我需要打破我的class级规模,但我不知道如何最好地去做。 我的StackOverflower伙伴能否建议他们采用现有的代码单块并将其缩小到适当的尺寸?

单一责任原则 – 一个class级应该只有一个改变的理由。 如果你有一个单一的类,那么它可能有不止一个改变的理由。 只需定义您改变的一个原因,并尽可能合理 。 我建议开始“大”。 将三分之一的代码重构为另一个类。 一旦你有了,那么重新开始你的新课程。 直接从一个class级到20个class级太令人生畏了。

开放/封闭原则 – 课程应该开放以进行扩展,但是关闭以进行更改。 在合理的情况下,将您的成员和方法标记为虚拟或抽象。 每个项目本质上应该相对较小,并为您提供一些基本function或行为定义。 但是,如果您以后需要更改function,则可以添加代码,而不是更改代码以引入新的/不同的function。

Liskov替换原则 – 一个类应该可以替代它的基类。 在我看来,这里的关键是正确地进行inheritance。 如果你有一个巨大的case语句,或两个if语句检查对象的派生类型,那么你违反了这个原则,需要重新考虑你的方法。

界面隔离原则 – 在我看来,这个原则非常类似于单一责任原则。 它只适用于高级(或成熟)类/接口。 在大类中使用此原则的一种方法是使您的类实现一个接口。 接下来,将使用您的类的所有类型更改为接口的类型。 这会破坏你的代码。 但是,它会指出你如何消耗你的课程。 如果您有三个实例,每个实例都使用自己的方法和属性子集,那么您现在知道需要三个不同的接口。 每个接口代表一组集合的function,以及一个改变的原因。

依赖倒置原则 – 父母/子女的寓言让我理解这一点。 想想一个父类。 它定义了行为,但不关心脏的细节。 这是可靠的。 然而,子类是关于细节的,并且不能依赖它,因为它经常变化。 你总是希望依赖父母,负责任的课程,而不是相反。 如果您有一个父类,具体取决于子类,当您更改某些内容时,您将遇到意外行为。 在我看来,这与SOA的思维方式相同。 服务合同定义了输入,输出和行为,没有任何细节。

当然,我的观点和理解可能是不完整或错误的。 我建议向那些掌握了这些原则的人学习,比如鲍勃叔叔。 对我来说,一个很好的起点是他的书,即C#中的敏捷原则,模式和实践 。 另一个很好的资源是Hanselminutes上的Uncle Bob 。

当然,正如乔尔和杰夫指出的那样 ,这些是原则,而不是规则。 它们是帮助指导你的工具,而不是土地法。

编辑:

我刚刚发现这些看起来非常有趣的SOLID截屏video 。 每个约10-15分钟长。

Martin Fowler有一本经典着作- 重构:改进现有代码的设计。

在那里,他提供了一系列设计技术和决策示例,以使您现有的代码库更易于管理和维护(以及SOLID主体的全部内容)。 即使在重构中存在一些标准例程,它也是一个非常自定义的过程,并且一个解决方案无法应用于所有项目。

unit testing是此过程成功的角落支柱之一。 您需要使用足够的代码覆盖率来覆盖现有的代码库,这样您就可以确保在更改代码时不会破坏内容。 实际上,使用具有嘲弄支持的现代unit testing框架将引导您更好地设计。

有像ReSharper(我最喜欢的)和CodeRush这样的工具可以帮助进行繁琐的代码更改。 但这些通常是微不足道的机械设备,使得设计决策过程要复杂得多,并且没有那么多的工具支持。 使用类图和UML有帮助。 实际上,我会从这开始。 尝试弄清已经存在的东西并为其带来一些结构。 然后,您可以从那里决定不同组件之间的分解和关系,并相应地更改您的代码。

希望这有助于快乐的重构!

这将是一个耗时的过程。 您需要阅读代码并识别不符合SOLID原则的部分并重构为新类。 使用像Resharper( http://www.jetbrains.com )这样的VS加载项将有助于重构过程。

理想情况下,您将自动覆盖自动化unit testing,以确保您的更改不会引入代码问题。

更多信息

在主API类中,您需要识别彼此相关的方法,并创建一个更具体地表示方法执行的操作的类。

例如

假设我有一个Address类,其中包含街道号,名称等的单独变量。此类负责插入,更新,删除等。如果我还需要为邮政地址格式化地址,我可以拥有一个名为GetFormattedPostalAddress()的方法,它返回格式化的地址。

或者,我可以将此方法重构为一个名为AddressFormatter的类,该类在其构造函数中使用Address,并具有一个名为PostalAddress的Get属性,该属性返回格式化的地址。

我们的想法是将不同的职责分成不同的类。

当我遇到这种类型的东西时我已经做了什么(我很乐意承认我之前没有使用过SOLID原理,但从我对它们的了解很少,它们听起来不错)就是从现有的代码库中查看连通性的观点。 从本质上讲,通过查看系统,您应该能够找到内部高度耦合(许多频繁交互)但外部松散耦合(很少偶发交互)的一些function子集。 通常,在任何大型代码库中都有一些这样的部分; 他们是切除的候选人。 基本上,一旦你确定了你的候选人,你就必须列举他们作为一个整体从外部耦合到系统的点。 这应该让您了解所涉及的相互依赖程度。 通常存在相当多的相互依赖性。 评估子集及其连接点以进行重构; 经常(但不总是),最终会有一些清晰的结构重构,可以增加去耦。 着眼于那些重构,使用现有的耦合来定义允许子系统与系统其余部分一起工作所需的最小接口。 寻找这些界面的共性(通常,你发现的比你期望的更多!)。 最后,实现您已识别的这些更改。

这个过程听起来很糟糕,但在实践中,它实际上非常简单。 请注意,这不是一个完全完美设计系统的路线图(为此,您需要从头开始),但它肯定会降低整个系统的复杂性并增加代码的可理解性。