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

时间:2009-04-23 23:28:10

标签: c# design-patterns solid-principles

我为这个问题的主观性而道歉,但我有点陷入困境,我希望以前能够处理这个问题的人提供一些指导和建议:

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

我对SOLID原则相当陌生,而且我是设计模式的忠实粉丝(但我仍处于可以实施它们的那个阶段,但还不足以知道何时< strong>使用他们 - 在不那么明显的情况下。)

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

5 个答案:

答案 0 :(得分:24)

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

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

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

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

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

当然,我的意见和理解可能不完整或错误。我建议向那些掌握了这些原则的人学习,比如鲍勃叔叔。对我而言,一个很好的起点是他的书Agile Principles, Patterns, and Practices in C#。另一个好资源是Uncle Bob on Hanselminutes

当然,作为Joel and Jeff pointed out,这些是原则,而不是规则。它们是帮助指导你的工具,而不是土地法则。

修改

我刚刚发现这些SOLID screencasts看起来非常有趣。每个约10-15分钟。

答案 1 :(得分:4)

Martin Fowler - Refactoring: Improving the Design of Existing Code.

有一本经典书籍

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

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

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

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

答案 2 :(得分:3)

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

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

更多信息

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

e.g。

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

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

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

答案 3 :(得分:2)

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

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

答案 4 :(得分:0)

固体

SOLID 是 OOD - 面向对象设计的一部分

单一职责原则 - SRP - 由鲍勃叔叔介绍。方法、类、模块只负责做一件事情(一个单一的任务)

开放/封闭原则 - OCP - 由 Bertrand Meyer 引入。方法、类、模块对扩展开放,对修改关闭。使用继承、抽象、多态的力量

[Liskov Substitution Principle] - LSP - 由 Barbara Liskov 和 Jeannette Wing 介绍。子类型可以替代超类型而没有副作用

接口隔离原则 - ISP - 由鲍勃叔叔介绍。你的界面应该尽可能小

[Dependency Inversion Principle(DIP)] - DIP - 由鲍勃叔叔介绍。内部类、层不应依赖于外部类、层。例如,当您有 aggregation[About] 依赖项时,您应该使用一些抽象/接口

[DIP vs DI vs IoC]