您如何设计面向对象的项目?[关闭]


231

我正在为一个大型项目工作(对我来说),该项目将有许多类并且需要可扩展,但是我不确定如何计划程序以及类之间如何交互。

几个学期后,我参加了OOD课程,并从中学到了很多东西。例如编写UML,以及将需求文档转换为对象和类。我们也学习了顺序图,但是以某种方式我错过了讲座或其他内容,但它们并没有真正与我保持联系。

在以前的项目中,我尝试使用从本课程中学到的方法,但是通常最终得到的代码是,只要我能说“是的,看起来像我的想法”,我就不希望深入研究添加新功能。

我得到了史蒂夫·麦康奈尔(Steve McConnell)的《代码完成》的副本,在这里和其他地方,我一直听到我很惊讶。我阅读了有关设计的章节,但似乎没有得到我所寻找的信息。我知道他说这不是一成不变的过程,它主要是基于启发法,但是我似乎无法吸收他的所有信息并将其应用于我的项目。

那么,在高级设计阶段(开始编程之前)要做什么,以确定需要哪些类(尤其是那些不基于任何“现实世界对象”的类),以及它们将如何相互影响

我特别对您使用的方法感兴趣?您遵循的过程通常会形成一个良好,干净的设计来紧密代表最终产品的流程是什么?


2
我认为Code Complete在这个主题上非常有帮助-特别是第5.3和5.4章(有一些更具体的建议)以及第6章的整个章节。但是,我实际上并未为大型项目进行任何代码设计。
Paul D. Waite,

1
我可以建议您学习Java的面向对象设计课程。在UDEMY udemy.com/mastering-object-directional-design-in-java / ...上有一篇很棒的文章。我认为肯定可以为您提供帮助。另一个很棒的资源是尝试ATM面向对象的问题。你可以谷歌。
马之音

我建议大家在实际进行设计时回到这个问题。这里有很多内容。这样,您就可以在进行实际设计时微调记忆。
凯文·惠勒

Answers:


199

我用于初始设计的步骤(进入类图)是:

  1. 需求收集。与客户交谈并提出用例,以定义软件应具有的功能。

  2. 撰写各个用例的叙述。

  3. 遍历叙述并突出名词(人,地点,事物),作为候选类和动词(动作),作为方法/行为。

  4. 丢弃重复的名词并排除常见功能。

  5. 创建一个类图。如果您是Java开发人员,那么Sun的NetBeans 6.7具有UML模块,该模块允许进行图表绘制以及双向工程,并且它是免费的。Eclipse(一个开放源代码Java IDE)也有一个建模框架,但是我没有经验。您可能还需要试用开放源代码工具ArgoUML。

  6. 应用OOD原则来组织您的类(分解出通用功能,构建层次结构等)


6
这确实是一种有用的技术,尤其是当您对问题域没有真正的了解时。但是,它很少产生最佳架构。
NomeN

1
我可以建议您学习Java的面向对象设计课程。在UDEMY udemy.com/mastering-object-directional-design-in-java / ...上刊登了一篇很棒的文章。我认为肯定可以为您提供帮助。另一个很棒的资源是尝试ATM面向对象的问题。你可以谷歌。
马之声

1
您在哪里学习?您能提供其来源吗?
Kanagavelu Sugumar

68

斯科特·戴维斯(Scott Davies)不得不说:

  1. 在开始之前,请绝对确保您了解程序的全部内容。你的程序什么?它不会做什么?它试图解决什么问题?

  2. 您的第一组用例不应是程序最终将要完成的所有工作的清单。从您可以想到的最小的用例开始,仍然可以捕捉到程序的本质。例如,对于该网站,核心用例可能是登录提出问题回答问题以及查看问题和答案。与声誉,投票或社区Wiki无关,这仅仅是您要拍摄的内容的原始内容。

  3. 当您提出潜在的类时,不要仅根据它们代表什么名词,而是要承担什么责任来考虑它们。我发现这对于弄清楚类在程序执行过程中如何相互关联是最大的帮助。很容易想到诸如“狗是动物”或“小狗有一个母亲”这样的关系。通常很难找出描述对象之间运行时交互的关系。程序的算法至少与对象一样重要,而且如果您清楚地说明了每个班级的工作,它们的设计就容易得多。

  4. 一旦有了最小的用例和对象集,就开始编码。尽早获得实际上可以运行的内容,即使它执行的工作不多且可能看起来很糟糕。这是一个起点,将迫使您回答您可能会在纸上忽略的问题。

  5. 现在返回并选择更多用例,写下它们的工作方式,修改您的类模型,并编写更多代码。就像第一次切割一样,一次添加尽可能少的东西,同时仍然添加一些有意义的东西。冲洗并重复。

只是我的两分钱。希望它是有用的。


19

如果有机会,我通常会使用所谓的“三个迭代规则”。

在第一次迭代(或启动)中,我根据模型对象,算法和预期的(实际上是预期的,也许不是预期的)未来方向。我没有写设计文档,但是如果我必须协调多个人,那么当然需要对过程进行粗略的概述,同时还要对依赖关系进行分析并估算所需的时间。如果像我一样,您更喜欢敏捷的方法,请尝试将此阶段保持在最低限度。在某些情况下,需要一个强大的设计阶段,特别是当您对程序逻辑的所有知识都是正确的,并且计划在代码中的功能之间进行大量交互时。在这种情况下,用例或用户案例提供的是一个很好的高级概念,尤其是对于GUI应用程序。对于命令行应用程序,尤其是库,请尝试编写“程序故事”,在其中针对必须开发的库进行编码并检查其外观。

第一次迭代之后,您将更好地了解事物之间的交互方式,弄清细节和粗糙点,并用拍打过的胶带解决了问题。您已准备好利用这些经验来改进,清理,抛光,划分太大的内容,合并过于分散的内容,定义和使用设计模式,分析性能瓶颈和不重要的安全性问题。通常,所有这些更改将对您编写的单元测试产生巨大影响,而对功能测试则没有影响。

当您完成第二次迭代时,您将获得一些珠宝,经过良好测试,文件记录和设计良好。现在,您已经拥有进行第三次迭代的经验和代码,可以扩展。您将添加新功能和用例以改进您的应用程序。您将发现粗糙点,最终将输入类似于第二次迭代的第四次迭代。冲洗并重复。

这是我进行软件设计的一般方法。它与螺旋式设计相似,具有短短的三个月迭代和敏捷开发的要素,使您可以了解问题并了解软件及其应用领域。当然,这是可伸缩性的问题,因此,如果应用程序如此之大,需要数百名开发人员参与,那么事情会比这复杂得多,但是最后,我想想法总是一样的,divide et impera

总结一下:

  1. 在第一个迭代中,您会体会到它,并学习
  2. 在第二次迭代中,您将清理产品并为将来做准备
  3. 在第三版中,您添加了新功能并了解更多
  4. 转到2

16

关于这一点,我所知道的最有趣的资料是Bertrand Meyer 第2版的《面向对象的软件构造》的 D部分。

D部分:面向对象的方法:很好地应用该方法

19:关于方法论,20:设计模式:多面板交互系统,21:继承案例研究:在交互系统中“撤消”,22: 如何查找类,23:类设计原理,24:很好地使用继承,25:有用的技巧,26:风格感,27:面向对象的分析,28:软件构建过程,29:讲授方法

有趣的是,第22如何在线查找课程


12

它经常重复,但完全正确-了解您的数据。

对于OOP,您的班级应该描述重要的信息及其相互作用。

如果您的思维模型能够很好地描述数据的行为和生存期,那么您将可以轻松地布置课程。

这仅仅是对以下内容的扩展:确切了解您要执行的操作。


12
对象行为比数据更重要。这是封装的直接结果:面向对象编程的核心。数据暴露(来自C和Pascal之类的语言)导致系统难以维护(增强和调试),因为您根本不知道系统中的其他位置会更改数据。OOP与数据无关。OOP与行为有关。这是一个重要的区别。
戴夫·贾维斯

这是一个重要的区别,但这并不意味着行为比数据更重要。
约翰尼

对于OOD,我通常在明确要求后从模型设计开始,这使我有了基本的想法,即应如何组织实体之间的关系。同时,我对每个实体可能发生的操作有一个基本的了解。在绘制了有关模型的图片后,我们可以查看需求并检查是否遗漏了一些东西。然后,返回到类级别和控制器级别会更容易。
约书亚记

10

尝试使用行为驱动的开发。很难打破您的旧习惯,但是我发现BDD确实是您在现实世界中发展的最佳选择。

http://behaviour-driven.org/


1
+1使用测试/行为/域驱动的开发,可以随时随地创建类,因此可以避免出现问题大的前端设计瀑布方法。
哈佛

8

大型项目的问题在于您无法监督组件之间的所有交互。因此,降低项目的复杂性很重要。类和序列图对于此设计阶段来说太详细了。

首先尝试从更高的抽象级别进行思考。考虑一下主要组件及其职责(它们与其他组件的接口),看一下一些启发性的架构模式(不,不是设计模式,这些级别太低了!MVC和Multi-Tier是架构模式的示例)。对于相当大的项目,这种视图应包含约3-5个组成部分。

只有这样,您才能放大某个组件并尝试进行设计。现在我们处于设计模式和类图的级别。尝试着重于项目的这一部分,如果您发现需要向其他组件之一添加责任,只需将其添加到文档/待办事项列表即可。此时不要浪费时间思考影响的变化,因为它们变化得太快了,请查看设计更可靠的时间。

尽管拥有一段实现未实现的组件接口并生成简单但有用的响应的代码可能是明智的,但此时您无需完全设计每个组件。这样,您可以一次开始开发(和设计)一个组件并对其进行合理程度的测试。

当然,完成新组件后,您应该在继续之前测试它们如何(以及是否)相互集成。

简而言之:遵循面向对象和信息隐藏原则,并将其提升到另一个层次!


PS:在设计时要进行很多草图绘制,就像真实的建筑一样!

PPS:尝试从不同角度审视此事,跳出框框思考(尽管可能会走出框框),与同行讨论对此非常有用……而您在午餐时间也有话要说。


7

我在实际项目中成功使用的技术是受Wirfs-Brock的书启发的“责任驱动设计”。

从顶级用户故事开始,在白板上与同事一起,勾勒出他们所暗示的高层交互。这使您首先了解什么是大模块;以及一两个或多个高阶CRC卡(例如游戏)的迭代,您应该已经稳定了一系列主要组件,它们的功能以及它们之间的交互方式。

然后,如果任何职责是大的或复杂的,则通过为更高级别的交互所标识的每个主要操作播放模块内部的交互,来精炼那些模块,直到您拥有足够小而简单的东西成为对象为止。

知道何时停止是一个判断的问题(这仅取决于经验)。


+1白板,出色的事情:DI解决了坐在白板前的80%的问题,只是看着它并思考“什么是最好的?”
usoban

7

设计模式

创新设计模式

单例-确保仅创建一个类的实例,并提供对该对象的全局访问点。

Factory(Factory方法的简化版本)-创建对象而不将实例化逻辑暴露给客户端,并通过公共接口引用新创建的对象。

工厂方法-定义用于创建对象的接口,但让子类决定要实例化的类,并通过通用接口引用新创建的对象。

抽象工厂-提供用于创建相关对象族的接口,而无需显式指定其类。

构建器-定义用于创建对象的实例,但让子类决定实例化哪个类,并允许对构建过程进行更好的控制。

原型-指定要使用原型实例创建的对象的种类,并通过复制此原型来创建新对象。

行为设计模式

责任链-避免将请求的发送方附加到其接收方,从而使其他对象也可以处理请求。-对象成为链的一部分,并且请求从一个对象通过链发送到另一个,直到其中一个对象处理该请求。

命令-将请求封装在对象中,允许对具有不同请求的客户端进行参数化,并允许将请求保存在队列中。

解释器-给定一种语言,请定义其语法的表示形式,以及使用该表示形式来解释该语言中的句子的解释器/将域映射为语言,将语言映射为语法,并将语法映射为面向对象的分层设计

迭代器-提供一种在不暴露其基础表示的情况下顺序访问聚合对象的元素的方法。

介体-定义一个对象,该对象封装了一组对象之间的交互方式。介体通过防止对象之间显式地相互引用来促进松散耦合,并且它使您可以独立地更改它们之间的交互。

观察者-定义对象之间的一对多依赖关系,以便当一个对象更改状态时,将自动通知和更新其所有依赖关系。

策略-定义一系列算法,封装每个算法,并使它们可互换。策略使算法独立于使用该算法的客户端而变化。

模板方法-定义操作中算法的框架,将某些步骤推迟到子类中/模板方法可让子类重新定义算法的某些步骤,而无需让他们更改算法的结构。

访客(Visitor)-表示要在对象结构的元素上执行的操作/访客可以在不更改操作对象的元素类别的情况下定义新操作。

空对象-提供一个对象来代替缺少给定类型的对象。/空对象模式提供了智能的“不执行任何操作”行为,向其协作者隐藏了细节。

结构设计模式

适配器-将类的接口转换为客户端期望的另一个接口。/ Adapter允许类一起工作,否则由于接口不兼容而无法进行工作。

桥-将对象组合成树结构以表示部分整个层次结构。/ Composite使客户可以统一对待单个对象和对象组成。

复合-将对象组合成树形结构,以表示部分整个层次结构。/ Composite使客户可以统一对待单个对象和对象组成。

装饰器-将其他职责动态添加到对象。

Flyweight-使用共享来支持大量对象,这些对象的一部分内部状态是相同的,而另一部分状态可能会有所不同。

记忆-在不破坏封装的情况下捕获对象的内部状态,从而为在需要时将对象恢复为初始状态提供了一种手段。

代理-为对象提供“占位符”以控制对其的引用。


2
模式对某些人很有用。我认为要了解需求中的模式需要大量的经验。您可能需要记录它们。我倾向于认为模式只不过是抽象组件库。
Cyber​​Fonic

5

我建议您使用BlueJ,也ActiveWriter学习,也养成了良好的物体上的理解。推荐的书也是不错的参考资料。

维基百科

替代文字

BlueJ是Java编程语言的集成开发环境,主要用于教育目的而开发,但也适合于小型软件开发。

此外,它使用UML,对我而言,它是理解对象建模方面的好资源。

替代文字http://www.ryanknu.com/ryan/bluej.png

ActiveWriter是建模实体和关系的工具,它还生成代码并且易于更改。这将节省您的时间,对于敏捷开发非常适合。

替代文字
(来源:altinoren.com


1
我使用过蓝色J ...绝对有用,但是它如何帮助我设计类及其关系?
维克多

1
我认为让我很清楚地看到了如何识别类以及如何在视觉上关联它们的全貌,在第一步中,我尝试了对象的代码表示方式以及如何理解对象中的思维。我记得当我花时间弄清楚“是”和“有”时,UML是一个很好的帮助。从那时起,我就使用该可视化工具来设计对象,当我发现ActiveWriter时,我感到非常高兴,因为BlueJ没有代码生成,而该工具却可以生成代码,只是为了强化我的观点,我认为在视觉上您可以采用更广泛的解决方案。
尼尔森·米兰达

4

首先-设计应该来自您的灵魂。您必须用每一种纤维来感受它。在开始做任何事情之前,我通常会走两三个月,然后(真的)走在街上。和思考。走路是一种很好的冥想。因此,它可以让您集中精力。

其次-仅在存在自然对象层次结构的地方使用OOP和类。请勿人为地将其“拧入”。如果不存在严格的层次结构(如在大多数业务应用程序中一样)-进行过程/功能,或者至少将对象仅用作具有隔离访问器的数据容器。

最后-尝试阅读以下内容:创造性思维的算法


4

只是引用 http://www.fysh.org/~katie/computing/methodologies.txt

RUP的核心是一小块区域,您必须在其中使用OO设计人才....如果您没有,那就像有一种运行100m的方法。

“第1步:编写关于非常快地跑步的文章。第2步:开始制定赛道的计划。第3步:购买真正紧身的莱卡短裤。第4步:非常,非常,非常快地跑步。第5步:先越线”

步骤4是艰难的一步。但是,如果您将重点放在1,2,3和5上,那么可能没人会注意到,那么您可能会从销售该方法的方法中赚到很多钱,从而使某些运动员认为100m有点“秘密”亚军



3

我认为这里的答案应该有所不同,具体取决于询问该人的实际经验。

如果您只有一两年的工作经验,那么必须要指出的重点是:如何达到您真正了解数据并确切了解您要做什么的目的?

是的,如果您在现实世界中工作了5年以上,那么您可以在许多软件开发过程模型或技术中进行选择。

但是,您不会仅通过读书获得经验。您应该通过在一个好的领导下的一个好的团队中学习来学习。

如果那不可能,那么您应该自己做。通过编码可能非常讨厌的一段代码开始迭代,学习您的错误,将其全部转储,编写更好的代码,依此类推。

您将学到很多有关您的代码库的知识。工具就是工具,它们不会教您什么。


3

如果您在该项目上具有领域专业知识,那么您将像银行一样从事工作。构造对象很容易,而且您知道这些增强是如何隔日出现的。

如果您没有该专业知识,请与具有该专业知识的人员合作,然后将这些想法转换为技术细节。

如果您对如何组织项目设计感到困惑。盲目遵循“实用程序员”一书。之前我也处于同样的情况,请尝试阅读那本书中的一章。您会看到差异,它将改变您作为软件开发人员的思维方式。


2
  1. 学习并掌握设计模式。
  2. 接下来,了解域驱动设计
  3. 之后,学习需求收集

几个学期后,我参加了OOD课程,并从中学到了很多东西。例如编写UML,以及将需求文档转换为对象和类。我们也学习了顺序图,但是以某种方式我错过了讲座或其他内容,但它们并没有真正与我保持联系。

  1. 您了解步骤3。您需要掌握它。我的意思是,通过大量练习,使其成为您的第二天性。那是因为您学习的方法与我们过去的方法完全相反。因此,您需要真正掌握它。否则,您将总是发现自己回到了原来的做事方式。就像测试驱动流程一样,许多Java开发人员经过几次尝试就放弃了它。除非他们完全掌握它,否则这只是他们的负担

  2. 编写用例,尤其是替代课程。替代课程占据了我们开发时间的50%以上。通常,当您的PM为您分配任务时,例如创建一个登录系统,他会认为这很简单,您可以花1天的时间完成它。但是他从来没有考虑过您需要考虑的问题:1.如果用户键入错误的密码,该怎么办?2.如果用户键入错误的密码3次,该怎么办?3.如果用户未键入用户名,该怎么办?您需要将它们列出来,并显示给您的PM,请他重新安排截止日期。


2

恐怕这不是人们喜欢听到的答案。无论如何,让我发表我的看法。

OOP应该被视为范式之一,而不是上级范式。OOP非常适合解决某些类型的问题,例如开发GUI库。它也适合大型软件公司通常采用的软件开发风格-一支由设计师架构师组成的精英团队,以UML图表或其他类似媒介来组织软件设计,而对LED的了解较少的团队开发人员视而不见将设计转换为源代码。如果您是一个人工作或与一小组才华横溢的程序员一起工作,OOP几乎不会带来任何好处。然后,最好使用支持多种范例的语言,这将帮助您快速提出原型。Python,Ruby,Lisp / Scheme等都是不错的选择。原型就是您的设计。然后,您可以对此进行改进。使用最能解决当前问题的范例。如果需要,可以使用以C或其他系统语言编写的扩展名优化热点。通过使用其中一种语言,您还将获得可扩展性免费,不仅在程序员级别,而且在用户级别。像Lisp这样的语言可以动态生成和执行代码,这意味着您的用户可以通过编写小的代码段(使用软件本身编码的语言)来扩展应用程序!或者,如果您选择用C或C ++编写程序,请考虑为诸如Lua之类的小型语言嵌入解释器。将功能公开为使用该语言编写的插件

我认为,大多数时候OOP和OOD都会创建过度设计的受害者的软件。

总而言之,我编写软件的首选方式是:

  1. 使用动态语言。
  2. 用该语言本身编写设计(原型)。
  3. 如有必要,请使用C / C ++优化某些区域。
  4. 通过实现语言本身的解释器提供可扩展性。

最后一个功能使软件可以轻松适应特定用户(包括我自己!)的要求。


这几乎不建议如何设计
NomeN

2
我使用类似的方法。为了避免被复杂性淹没,请从直升机视图开始。我喜欢具有8-20个功能的草图。如果我开始获得更多的东西,那么我将研究如何划分为子系统。一旦获得了高级视图,我就将每个功能分解为8-20个子功能,依此类推。通过查看这些功能的操作方式,我得到了顶级类。那就是我开始用Python(又名可执行伪代码)布置骨骼系统的时候。连同注释框一起是我的“可执行规范”,然后逐步完善。
Cyber​​Fonic


2

学习设计模式。过去两年中,这是我个人经历的有关OOP的革命。拿一本书。我会向您推荐一个:

头先设计模式

它是Java语言,但可以扩展为任何语言。


1

坦白地说,一个好的步骤是返回并查看流程图和序列图。有很多好的网站向您展示如何做到这一点。当我了解如何将程序分解为类时,我发现它非常有价值,因为我确切地知道该程序需要输入,计算和输出的内容,并且每个步骤都可以分解为该程序的一部分。


1
我喜欢遇到问题时的流程图。有时它可以帮助我以不同的方式思考问题。
2009年

数据流程图(DFD)流程图的替代品,或者说是流程图,它更高级:它们更像是UML部署图,并且适合深入了解系统功能(例如,系统的数据输入和数据输出,内部和外部数据存储以及数据处理)和体系结构。流程图(IMHO)在范围上更接近于使用if-then-else语句为单个函数建模。
ChrisW

是的,通常我大部分时间都使用流程图,流程图主要用于我试图找出特定问题的地方。
user133018

使用泳道解决了流程图中的许多问题。我发现每种情况使用一个序列图效果最好。每种情况都覆盖了决策树中的一条特定路径,因此流程中没有IF。如果您想要一个包含所有流程的图表,则必须包括决策点,但是决策点会很快变得混乱,尤其是当您要包括职责分配时。
凯利·S·


1

一种有用的技术是将您独特的问题描述与您在现实世界中可以找到的东西联系起来。例如,您正在建模一个复杂的医疗保健系统,它将席卷全球。您是否可以直接调用任何示例来对此建模?

确实。观察副药房或医生室的运作方式。

将您的域名问题归结为您可以理解的问题;您可以关联的东西。

然后,一旦域内的“玩家”开始明显出现,并且开始对代码进行建模,请选择“提供商-消费者”建模方法,即您的代码是模型的“提供商”,而就是“消费者” ”。

与领域相关并从高层次理解它是任何设计的关键部分。


1

在设计类结构的过程中,我注意到从编写一些伪代码开始非常有帮助。这意味着:我首先在最高级别上“编写”应用程序代码的一些常规片段,进行使用,并发现正在出现的元素,实际上,我作为程序员想要使用的元素。这是设计模块的一般结构及其交互的一个很好的起点。经过几次迭代,整个结构开始看起来更像是一个完整的类系统。这是设计代码部分的非常灵活的方法。您可以将其称为面向程序员的设计。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.