什么应该在一个有助于某人开发优秀的OO软件的清单上?

时间:2009-05-27 13:18:38

标签: c# oop

几年前我使用过OO编程语言和技术(主要是在C ++上),但是在这段时间内OO并没有做太多。

我开始在C#中创建一个小实用程序。我可以简单地对它进行编程而不使用良好的OO练习,但对我来说应用OO技术将是一个很好的复习。

与数据库规范化水平一样,我正在寻找一个清单,它会让我想起一个“好的”面向对象程序的各种经验法则 - 一个简明的是/否列表,我可以在设计和实现过程中偶尔阅读阻止我在程序上思考和工作。如果它包含适当的OO术语和概念,那么它将更加有用,以便任何检查项目都可以轻松搜索以获取更多信息。

有哪些内容可以帮助某人开发出优秀的OO软件?

相反,可以应用哪些'测试'来显示软件不是OO?

12 个答案:

答案 0 :(得分:36)

  • 对象事情。 (整个OOP中最重要的一点!)不要将它们视为“数据持有者” - 向他们发送消息来做某事。我班上应该有什么动词? “责任驱动设计”思想学派对此非常出色。 (参见对象设计:角色,职责和合作,Rebecca Wirfs-Brock和Alan McKean,Addison-Wesley 2003,ISBN 0201379430.)
  • 对于系统必须做的每件事,提出一系列具体方案,描述对象如何相互通信以完成工作。这意味着根据交互图进行思考并执行方法调用。 - 不要从类图开始 - 这是SQL思维而非OO思维。
  • 了解测试驱动的开发。 没有人预先获得他们的对象模型,但是如果你做TDD,你就要做好基础工作以确保你的对象模型做到了它需要什么,并在事情发生变化后重新安全。
  • 只针对您现有的要求进行构建 - 不要过于关注“重复使用”或“以后有用”的内容。如果你现在只构建你需要的东西,你就可以保留以后可以做的事情的设计空间。
  • 在对对象进行建模时忘记继承。这只是实现公共代码的一种方式。当您对对象建模时,只是假装您通过界面查看每个对象,该界面描述了可以被要求执行的操作。
  • 如果方法需要加载参数,或者如果需要重复调​​用一堆对象来获取大量数据,则该方法可能位于错误的类中。方法的最佳位置是紧挨着它在同一个类(或超类......)中使用的大多数字段
  • 阅读适合您语言的设计模式书。如果是C#,请尝试Steve Metsker撰写的“C#中的设计模式”。这将教你一系列技巧,你可以用来分割对象之间的工作。
  • 不要测试对象以查看它是什么类型,然后根据该类型采取行动 - 这是对象可能正在进行工作的代码味道。这是一个提示,你应该调用该对象并要求它完成工作。 (如果只有某些类型的对象可以完成这项工作,你可以简单地在某些对象中“无所事事”实现......这是合法的OOP。)
  • 将方法和数据放在正确的类中会使OO代码运行得更快(并为虚拟机提供更好的优化机会) - 这不仅仅是美学或理论上的。 Sharble和Cohen的研究指出了这一点out - 请参阅http://portal.acm.org/citation.cfm?doid=159420.155839(请参阅“每个方案执行的指令数量”的指标图表)

答案 1 :(得分:11)

听起来你想要一些基本的是/否问题来问自己。每个人都给了一些伟大的“做到这一点”和“这样思考”的列表,所以这里是我对一些简单的是/否的解析。

我可以对所有这些回答是吗?

  • 我的班级是否代表我所关注的名词?
  • 我的课程是否提供了可以执行的动作/动词的方法?

我可以回答所有这些问题吗?

  • 我是否可以将全局状态数据放入单例中或存储在与其一起使用的类实现中?
  • 我可以删除类上的任何公共方法并将它们添加到接口或将它们设置为private / protected以更好地封装行为吗?
  • 我应该使用接口将行为与其他接口或实现类分开吗?
  • 我是否有相关类之间重复的代码,我可以移动到基类中以获得更好的代码重用和抽象?
  • 我是否在测试某种类型的东西以决定要采取的措施?如果可以,这种行为可以包含在有问题的代码使用的基本类型或接口上,以便更有效地使用抽象,或者是否应该重构有关代码以使用更好的基类或接口?
  • 我是否反复检查一些上下文数据以确定要实例化的类型?如果是这样,可以将其抽象为工厂设计模式,以便更好地抽象逻辑和代码重用?
  • 是一个非常庞大的课程,有多个功能重点?如果是这样,我可以将它分成多个类,每个类都有自己的单一目的吗?
  • 我是否有从相同基类继承的不相关的类?如果是这样,我可以将基类划分为更好的抽象,还是可以使用合成来获取功能?
  • 我的继承层次结构是否变得可怕?如果是这样,我可以通过接口或拆分功能将其展平或分开吗?
  • 我对我的继承层次结构过分担心?
  • 当我向橡皮鸭解释设计时,我觉得愚蠢的设计还是愚蠢地和鸭子交谈?

只是快速的一些我的头顶。我希望它有所帮助,OOP会变得非常疯狂。我没有包含任何是/否的更高级的东西,通常是较大的应用程序的关注,如依赖注入或如果你应该将一些东西拆分成不同的服务/逻辑层/组件......当然,我希望你至少将您的UI与您的逻辑分开。

答案 2 :(得分:10)

聚集各种书籍,着名的C#程序员和一般建议(如果这些是我的,那就不多了;从某种意义上说,这些是我在开发过程中问自己的各种问题,但就是这样):

  • 结构或类?您正在创建自己的值的项目,使其成为结构。如果它是具有属性和子值,方法以及可能状态的“对象”,则将其作为对象。
  • 密封类:如果您要创建一个类,并且您没有明确需要能够继承它,请将其密封。 (我是为了假设的性能增益而做的)
  • 不要重复自己:如果你发现自己复制过去(a / e)代码那么你应该(但不总是)重新考虑你的设计最小化代码重复。
  • 如果您不需要为给定的抽象类提供基本实现,请将其转换为接口。
  • 专业化原则:您拥有的每个对象应该只做一件事。这有助于避免“上帝对象”。
  • 使用属性进行公共访问:这一直在争论,但这确实是最好的事情。属性允许您通过字段执行通常无法执行的操作,并且还允许您更好地控制对象的获取和设置方式。
  • 单身人士:另一个有争议的话题,这就是这个想法:只有当你绝对需要时才使用它们。大多数情况下,一堆静态方法可以用于单例的目的。 (尽管如果你绝对需要单身模式,请使用Jon Skeet's excellent one
  • 松散耦合:确保您的课程尽可能少地相互依赖;确保您的图书馆用户可以轻松地将图书馆的部分内容与其他人(或自定义部分)交换出来。这包括在必要时使用接口,封装(其他人已经提到过),以及本答案中的大多数其他原则。
  • 设计简单明了:与蛋糕结霜不同,现在设计简单的东西比现在设计复杂并稍后删除更容易。

如果我是这样的话,我可能会把一些或所有这些扔出门:

  • 撰写个人项目
  • 真的急于完成某件事(但我稍后会再回来......有时.....;))

这些原则有助于指导我的日常编码,并在某些方面大大提高了我的编码质量!希望有所帮助!

答案 3 :(得分:7)

Steve McConnell的Code Complete 2包含大量现成的清单,可用于良好的软件构建。

罗伯特·C·马丁的Agile Principles, Patterns, and Practices in C#包含很多优秀的OO设计原则。

两者都将为您提供坚实的基础。

答案 4 :(得分:7)

  • 数据属于在其上运行的代码(即属于同一类)。这提高了可维护性,因为许多字段和方法可以是私有的(封装),因此在查看组件之间的交互时,在某种程度上可以不考虑这些。
  • 使用多态而不是条件 - 每当你必须根据对象是什么类做不同的事情时,尝试将该行为放入一个不同类以不同方式实现的方法中,以便你所需要的只是做就是调用那个方法
  • 谨慎使用继承,更喜欢组合 - 继承是OO编程的一个显着特征,通常被视为OOP的“本质”。事实上,它被严重过度使用,应归类为最不重要的特征

答案 5 :(得分:7)

  • 我是否明确定义了 要求?可能没有必要提供正式的要求文档,但在开始编码之前,您应该有一个清晰的愿景。如果您不需要正式文档,思维导图工具和原型或设计草图可能是不错的选择。在软件过程中尽早与最终用户和利益相关者合作,以确保您实现他们所需的。

  • 我是否重新发明轮子?如果您正在编码以解决常见问题,请寻找已解决此问题的强大库。如果您认为您可能已经在代码中的其他地方解决了问题(或者同事可能已经解决了),请首先查看现有解决方案。

  • 我的对象是否具有明确的单一用途?遵循封装原则,对象应该与其操作的数据一起具有行为。一个对象应该只有一个主要责任。

  • 我可以编写接口吗?按合同设计是一种很好的方法,可以启用单元测试,记录详细的,类级别的需求,并鼓励代码重用。

    < / LI>
  • 我可以对我的代码进行测试吗?测试驱动开发(TDD)并不总是那么简单;但单元测试对于重构非常有用,并且在进行更改后验证回归行为。与Design By Contract合作。

  • 我是否过度设计?请勿尝试编写可重用组件的代码。不要试图预测未来的要求。这些东西看似违反直觉,但它们会带来更好的设计。第一次编写代码时,只需尽可能直接地实现它,并使其工作。第二次使用相同的逻辑,复制和粘贴。一旦你有两个具有通用逻辑的代码工作部分,你就可以轻松地重构而不试图预测未来的需求。

  • 我是否引入了还原剂代码?不要重复自己(DRY)是重构的最大驱动原则。仅使用复制和粘贴作为重构的第一步。不要在不同的地方编写相同的东西,这是一个维护的噩梦。

  • 这是一种常见的设计模式,反模式还是代码味道?熟悉OO设计问题的常见解决方案,并在编码时查找它们 - 但不要试图强迫问题适合某种模式。注意那些属于常见“不良实践”模式的代码。

  • 我的代码是否过于紧密耦合?松散耦合是一种尝试减少两个或多个类之间相互依赖关系的原则。一些依赖是必要的;但是你越依赖另一个班级,你在班级改变时就必须修复得越多。不要让一个类中的代码依赖于另一个类的实现细节 - 仅根据其合同使用对象。

  • 我是否暴露了太多信息?实践信息隐藏。如果方法或字段不需要公开,请将其设为私有。仅公开对象履行合同所需的最小API。不要让客户端对象可以访问实现细节。

  • 我是防御性编码吗?检查错误情况,并快速失败。不要害怕使用异常,让它们传播。如果您的程序达到意外状态,则中止操作,记录堆栈跟踪以供您使用,并避免下游代码中出现不可预测的行为要好得多。遵循清理资源的最佳做法,例如using() {}声明。

  • 我能在六个月内阅读此代码吗?良好的代码是可读的,文档很少。必要时发表评论;但也要编写直观的代码,并使用有意义的类,方法和变量名。练习良好的编码风格;如果您正在开展一个团队项目,团队的每个成员都应该编写看起来相同的代码。

  • 它仍然可以使用吗?提前测试,经常测试。引入新功能后,请返回并触摸可能受影响的任何现有行为。让其他团队成员进行同行评审并测试您的代码。在进行更改后重新运行单元测试,并使其保持最新。

答案 6 :(得分:6)

最好的消息来源之一是Martin Fowler的“重构”一书,其中包含面向对象代码气味的列表(和支持细节),您可能需要考虑重构。

我还推荐Robert Martin的“清洁代码”中的清单。

答案 7 :(得分:5)

  • SOLID
  • DRY
  • TDD
  • 继承的构成

答案 8 :(得分:3)

请务必阅读并理解以下内容

  • 封装
    • (确保只公开最小状态和功能以完成工作)
  • 多态性
    • (派生对象的行为能够像父母一样)
  • 和接口和抽象类之间的区别
    • (抽象类允许 要共享的功能和状态 用它的后代,一个界面 只是承诺了 功能将实施)

答案 9 :(得分:1)

我喜欢this列表,虽然它可能有点密集而无法用作清单。

答案 10 :(得分:1)

UML - 统一建模语言,用于对象建模和定义类的结构和关系

http://en.wikipedia.org/wiki/Unified_Modeling_Language

然后当然是OO的编程技术(大多数已经提到过)

  • 隐藏信息
  • 抽象
  • 接口
  • 封装
  • 继承/多态性

答案 11 :(得分:1)

有些规则与语言无关,有些规则因语言而异。

以下是一些与先前发布的规则相矛盾的规则和评论:

  • OO有4个原则: 抽象,封装,多态和继承 阅读它们并记住它们。

  • 建模 - 您的类应该为问题域中的实体建模:
    将问题划分为子域(包/命名空间/程序集)
    然后将每个子域中的实体划分为类 类应包含对该类型的对象进行建模的方法。

  • 使用UML,考虑需求,用例,然后是类图,它们是序列。 (主要适用于高级设计 - 主要类别和流程。)

  • 设计模式 - 任何语言的良好概念,语言之间的实现不同。

  • 结构与类 - 在C#中,这是按值或按引用传递数据的问题。

  • 接口与基类,是基类,是接口。

  • TDD - 这不是OO,事实上,它可能导致设计不足并导致大量重写。 (例如Scrum建议不要使用它,对于XP来说这是必须的)。

  • C#的反射在某些方面优先于OO(例如基于反射的序列化),但它对于高级框架是必要的。

  • 确保课程“不知道”其他课程,除非他们必须分成多个集会,并且缺少参考帮助。

  • AOP(面向方面​​编程)以一种极具革命性的方式增强OOP(例如参见PostSharp),至少你应该查看它并观看它们的剪辑。

  • 对于C#,请阅读MS指南(在VS的MSDN帮助索引中查找指南), 那里有很多有用的指导方针和惯例

  • 推荐书籍:
    框架设计指南:可重用.NET库的约定,惯用语和模式 C#3.0 in Nutshell