我正在开发一个大型项目(对我来说),它有很多类,需要可扩展,但我不知道如何规划我的程序以及类需要如何进行交互。
我参加了一个学期的OOD课程并从中学到了很多东西;比如编写UML,并将需求文档翻译成对象和类。我们也学习了序列图,但不知怎的,我错过了讲座或其他什么,他们并没有真正坚持我。
在以前的项目中,我尝试过使用从课程中学到的方法,但通常最终会得到代码,只要我能说“是的,看起来像我的想法”,我不想透过muck添加新功能。
我有一份史蒂夫麦康奈尔的代码完整的副本,我不断听到,这里和其他地方都很棒。我阅读了有关设计的章节,似乎没有提供我正在寻找的信息。我知道他说这不是一个削减和干燥的过程,它主要是基于启发式的,但我似乎无法把他所有的信息都应用到我的项目中。
所以你在高级设计阶段(开始编程之前)做了什么,以确定你需要什么类(特别是那些不基于任何“真实世界对象”的类)以及它们将如何相互作用彼此?
具体来说,我对您使用的方法感兴趣?您遵循的流程是什么,通常会形成一个良好,干净的设计,能够代表最终产品?
答案 0 :(得分:186)
我用于初始设计(进入类图)的步骤是:
需求收集。与客户交谈并分析用例以定义软件应具备的功能。
撰写个别用例的叙述。
通过叙述并突出显示名词(人物,地点,事物),作为候选类和动词(动作),作为方法/行为。
丢弃重复的名词并排除常用功能。
创建一个类图。如果您是Java开发人员,Sun的NetBeans 6.7有一个UML模块,允许绘图和往返工程,它是免费的。 Eclipse(一个开源Java IDE)也有一个建模框架,但我没有使用它的经验。您可能还想尝试一种开源工具ArgoUML。
应用OOD原则来组织您的类(分解常用功能,构建层次结构等)。
答案 1 :(得分:64)
我还没有足够的声誉来发表评论(今天加入)或者我只是评论Scott Davies的回答。加上他不得不说的话:
在开始之前,请务必确定您的程序是什么。 你的程序是什么? 不会做什么?它试图解决什么问题?
您的第一组用例不应该是程序最终要做的所有事情的清单。从您可以提出的最小的一组用例开始,这些用例仍然可以捕获您的程序的精髓。例如,对于此网站,核心用例可能是登录,提出问题,回答问题和查看问题和答案。没有关于声誉,投票或社区维基的信息,只是你拍摄的原始本质。
当你提出潜在的课程时,不要只考虑它们所代表的名词,而应考虑它们有什么责任。我发现这是在程序执行期间弄清楚类如何相互关联的最大帮助。很容易想出“狗是动物”或“小狗有一个母亲”之类的关系。通常很难找出描述对象之间运行时交互的关系。你的程序算法至少和你的对象一样重要,如果你已经详细说明每个班级的工作,它们就更容易设计。
一旦获得了最小的用例和对象集,就开始编码。获得实际上尽快运行的东西,即使它没有做太多而且可能看起来像垃圾。这是一个起点,并将迫使您回答可能在纸上掩饰的问题。
现在回过头来选择更多用例,记录它们的工作方式,修改类模型,编写更多代码。就像你的第一次剪裁一样,尽可能少地拍摄,同时还要添加一些有意义的东西。冲洗并重复。
只是我的两分钱。希望它有用。
答案 2 :(得分:19)
当我有机会时,我通常会使用我称之为“三次迭代规则”。
在第一次迭代(或启动)中,我根据模型对象,算法和预期来设计应用程序的总体布局(真正预期,而不是可能强烈的>预期的)未来方向。我不编写设计文档,但如果我必须协调多个人,那么当然需要粗略描述该过程,同时分析依赖性和所需时间的猜测。如果像我一样,你更喜欢更敏捷的方法,尽量将这个阶段保持在最低限度。在某些情况下,需要一个强大的设计阶段,特别是当所有事情都已知并且对程序的逻辑真实,并且您计划在代码中的功能之间进行大量交互时。在这种情况下,提供的用例或用户故事是一个很好的高级想法,特别是对于GUI应用程序。对于命令行应用程序,特别是库,请尝试编写“程序故事”,在其中对您必须开发的库进行编码并检查其外观。完成后,这些程序将成为您图书馆的功能测试。
在第一次迭代之后,您将更好地了解事物如何相互作用,了解细节和粗糙点,用一个打耳塞的胶带贴片解决问题。您已准备好利用这些经验来改进,清理,抛光,划分过大的内容,合并过于分散的内容,定义和使用设计模式,分析性能瓶颈和非平凡的安全问题。通常,所有这些更改都会对您编写的单元测试产生巨大影响,但不会影响功能测试。
当你完成第二次迭代时,你会得到一颗小宝石,经过良好测试,记录良好,设计精良。现在你有经验和代码来做第三次迭代,扩展。您将添加新功能和用例以改进您的应用程序。你会发现粗糙的斑点,你最终会进入第四次迭代,类似于第二次迭代。冲洗并重复。
这是我对软件设计的一般方法。它类似于螺旋设计,短期,三个月的迭代,以及敏捷开发的元素,使您可以了解问题并了解您的软件及其应用领域。当然,这是一个可扩展性的问题,所以如果应用程序如此庞大以至于涉及数百名开发人员,事情会比这复杂得多,但最后我想这个想法总是一样的,划分 et impera 。
所以总结:
答案 3 :(得分:16)
我所知道的最有趣的来源是Bertrand Meyer的 Object Oriented Software Construction, 2nd Edition 的D部分。
D部分:面向对象的方法:很好地应用该方法
19:关于方法论, 20:设计 模式:多面板互动 系统, 21:继承案例研究: 在交互式系统中“撤消”, 22: 如何查找课程,23: 课堂设计原则,24:使用 继承好,25:有用 技巧,26:风格感,27: 面向对象分析,28:The 软件构建过程,29: 教授方法
有趣的是, 22. How to find the classes 这一章可以在线获取。
答案 4 :(得分:12)
重复但完全正确 - 了解您的数据。
对于OOP,您的课程应该描述重要的信息以及它们如何相互作用。
如果您的心智模型能够很好地描述数据的行为和生命周期,那么您可以轻松地安排课程。
这只是一个扩展:确切地知道你想要做什么。
答案 5 :(得分:10)
尝试使用行为驱动的开发。打破旧习惯很难,但我发现BDD在现实世界中发展时确实是最好的选择。
答案 6 :(得分:8)
大型项目的问题在于您无法监督组件之间的所有交互。因此,降低项目的复杂性非常重要。类和序列图对于此设计阶段来说过于详细。
首先尝试从更高的抽象级别进行思考。考虑主要组件及其职责(它们与其他组件的接口),看一些架构模式的灵感(不,不是设计模式,这些是太低级别!MVC和Multi-Tier是架构模式示例)。 对于相当大的项目,这样的视图应该有大约3-5个组件。
只有这样你才能放大某个组件并尝试设计它。现在我们处于设计模式和类图的层面。尝试关注项目的这一部分,如果您发现需要将责任添加到其他组件之一,只需将其添加到文档/待办事项列表中即可。不要浪费时间考虑其影响,在这一点上,他们的变化太快,在设计更加稳固时进行审核。
此时您不需要完全设计每个组件,尽管使用一段代码实现未实现的组件接口并生成简单但有用的响应可能是明智之举。这样,您可以一次开始开发(和设计)一个组件并对其进行合理的测试。
当然,当新组件完成后,您应该在继续之前测试它们如何(以及是否)彼此集成。
非常简短: 采用OO和信息隐藏原则,将其提升到另一个层次!
PS: 在设计时做了很多素描,就像真正的建筑一样!
PPS:尝试从不同的角度来处理这个问题,在盒子外面思考(虽然盒子可能是要走的路),与同行讨论对此非常有用......你有什么可以谈论的午餐。
答案 7 :(得分:7)
设计模式
创意设计模式
Singleton - 确保只创建一个类的实例并为该对象提供全局访问点。
Factory(工厂方法的简化版本) - 创建对象而不将实例化逻辑暴露给客户端,并通过公共接口引用新创建的对象。
工厂方法 - 定义用于创建对象的接口,但让子类决定实例化哪个类,并通过公共接口引用新创建的对象。
Abstract Factory - 提供用于创建相关对象族的接口,而无需显式指定其类。
Builder - 定义用于创建对象的实例,但让子类决定实例化哪个类,并允许更好地控制构造过程。
原型 - 使用原型实例指定要创建的对象类型,并通过复制此原型来创建新对象。
行为设计模式
责任链 - 它避免将请求的发送者附加到其接收者,这样其他对象也可以处理请求。 - 对象成为链的一部分,请求从一个对象发送到另一个对象,直到其中一个对象将处理它。
命令 - 在对象中封装请求,允许对具有不同请求的客户端进行参数化,并允许将请求保存在队列中。
解释器 - 给定一种语言,定义其语法的表示以及使用该表示来解释语言中的句子/将域映射到语言,将语言映射到语法,将语法映射到分层对象的解释器面向设计
迭代器 - 提供一种顺序访问聚合对象元素的方法,而不会暴露其底层表示。
Mediator - 定义一个封装一组对象如何交互的对象。 Mediator通过使对象明确地相互引用来促进松散耦合,并且它允许您独立地改变它们的交互。
Observer - 定义对象之间的一对多依赖关系,以便当一个对象更改状态时,将自动通知和更新其所有依赖项。
策略 - 定义一系列算法,封装每个算法,并使它们可互换。策略允许算法独立于使用它的客户端。
模板方法 - 在操作中定义算法的骨架,将一些步骤推迟到子类/模板方法让子类重新定义算法的某些步骤,而不让它们更改算法的结构。
访问者 - 表示要对对象结构的元素执行的操作/访问者允许您定义新操作而不更改其操作的元素的类。
空对象 - 提供一个对象作为缺少给定类型对象的代理。 / Null对象模式提供智能无效行为,隐藏其协作者的详细信息。
结构设计模式
适配器 - 将类的接口转换为客户期望的另一个接口。 / Adapter允许类一起工作,否则由于接口不兼容而无法使用。
Bridge - 将对象组合成树结构以表示部分整体层次结构。 / Composite允许客户端统一处理单个对象和对象组合。
Composite - 将对象组合成树结构以表示部分整体层次结构。 / Composite允许客户端统一处理单个对象和对象组合。
装饰器 - 动态地向对象添加其他职责。
Flyweight - 使用共享来支持大量具有部分内部状态的对象,其中状态的其他部分可能会有所不同。
Memento - 在不违反封装的情况下捕获对象的内部状态,从而提供在需要时将对象恢复到初始状态的方法。
代理 - 为对象提供“占位符”以控制对它的引用。
答案 8 :(得分:7)
我在真实项目中使用的技术取得了成功,是责任驱动设计,灵感来自Wirfs-Brock的书。
从顶级用户故事开始,与同事一起在白板上绘制他们暗示的高级别互动。这让您首先了解大模块是什么;和一两次高级CRC卡一样,您应该已经稳定了一系列主要组件,他们做了什么以及他们如何互动。
然后,如果任何责任是大的或复杂的,那么通过在模块内部为每个主要操作识别的交互中播放相互作用的东西,将这些模块向下细化,直到你拥有小而简单的东西作为对象。更高层次的互动。
知道何时停止是一个判断问题(只有经验才有)。
答案 9 :(得分:4)
我建议您使用BlueJ和ActiveWriter来学习并开发对对象的良好理解。推荐的这本书也是一个很好的资源。
来自Wikipedia:
BlueJ是一个综合发展 Java编程环境 语言,主要为 教育目的,但也 适用于小型软件 发展。
此外,它使用UML,对我来说,它是理解有关建模对象的几个方面的好资源。
alt text http://www.ryanknu.com/ryan/bluej.png
ActiveWriter是一个模拟实体和关系的工具,它还可以生成代码并且很容易进行更改。它将为您节省时间,并且敏捷开发非常适合。
(来源:altinoren.com)
答案 10 :(得分:4)
引用http://www.fysh.org/~katie/computing/methodologies.txt
RUP的核心是一个小区域,你必须使用OO设计 人才....如果你没有他们,就像有一个方法论 跑100米。
“第1步:写下跑步真的很快。 第2步:去绘制赛道计划。 第3步:去购买非常紧身的莱卡短裤。 第4步:真的,非常,非常快地运行。 第5步:首先跨行“
这是第4步,这是艰难的。但是如果你强调的话 在1,2,3和5上,没有人会注意到然后你可以 可能会花很多钱出售这种方法 那些认为有一个“秘密”成为100米跑者的运动员
答案 11 :(得分:4)
首先 - 设计应该来自你的灵魂。你必须用你的每一根纤维来感受它。我经常走两三个月才开始做任何事情,只是走在街上(真的)。思考。你知道,散步是一种很好的冥想。所以它可以让你集中注意力。
其次 - 仅在存在自然对象层次结构的地方使用OOP和类。不要人为地“搞砸”它。如果不存在严格的层次结构(就像在大多数业务应用程序中那样) - 转到过程/功能,或者至少只将对象用作具有隔离访问器的数据容器。
最后一点 - 尝试阅读:The Algorithm of Creative Thinking
答案 12 :(得分:4)
你问了许多作者用来写书的问题。有很多方法,你应该选择一个看起来“最漂亮”的方法。
我可以推荐Eric Evans的书"Domain Driven Design"。另外,请检查网站dddcommunity.org。
答案 13 :(得分:3)
我认为这里的答案应该是非常不同,这取决于这个家伙的实际经验。
如果您只有一到两年的工作经验,那么您必须明白:如何您能够确切地了解您的数据并准确理解您正在尝试的内容要做什么?
是的,如果您在现实世界中工作了5年以上,那么您可以选择任何一种软件开发流程模型或技术。
但是你没有通过阅读书籍获得经验。你应该通过在良好的领导下在一个优秀的团队中工作来学习。
如果那是不可能的,那么你应该自己做。开始迭代编写一个可能非常讨厌的代码片段,学习你的错误,倾倒所有代码,编写更好的代码等等。
您将学到很多关于代码库的知识。这些工具是工具,它们不会教你什么。
答案 14 :(得分:3)
如果您在该项目中拥有领域专业知识,那么您将开始工作,比如说银行业务。您可以轻松构建对象,并了解这些增强功能每隔一天会如何发生。
如果您没有这方面的专业知识与具有该专业知识的人合作并将这些想法转化为技术细节。
如果您对如何构建项目设计感到困惑。盲目跟随“实用程序员”一书。我之前处于同样的境地,试着阅读那本书中的一章。你会看到它的不同之处,它将改变你作为软件开发者的思维方式。
答案 15 :(得分:2)
我参加了一个OOD课程几个学期 回来并从中学到了很多东西;喜欢 编写UML和翻译 要求文件转化为对象 和课程。我们学习了序列 图也是,但不知怎的,我错过了 讲课或其他什么,他们没有 真的坚持我。
您知道第3步。您需要掌握它。我的意思是,通过大量的练习使它成为你的第二天性。那是因为你学到的方法完全违背了我们以前的方式。所以你需要真正掌握它。否则,你总会发现自己回到原来的做事方式。这有点像Test Driven Process,很多java开发人员经过几次尝试就放弃了。除非他们完全掌握它,否则它只是他们的负担
编写用例,尤其是替代课程。替代课程占我们开发时间的50%以上。通常,当您的PM为您分配任务时,例如,创建一个登录系统,他会认为它是直接的,您可以花1天时间完成它。但是他从不考虑你需要考虑的问题,1。如果用户输入错误的密码怎么办,2。如果用户输入错误密码3次,3,如果用户没有输入用户名等等怎么办?您需要将它们列出来,并将其显示给您的PM,让他重新安排截止日期。
答案 16 :(得分:2)
我担心这不是人们喜欢听的答案。无论如何,让我说出我的意见。
OOP应该被视为范式之一,而不是优越的范例。 OOP有助于解决某些问题,比如开发GUI库。它也适合大型软件公司通常遵循的软件开发风格 - 设计师或架构师的精英团队在UML图或其他类似介质中规划软件设计一个不那么开明的开发人员团队将该设计转化为源代码。如果您单独工作或与一群才华横溢的程序员合作,OOP几乎没有任何好处。然后,最好使用支持多种范例的语言,并帮助您快速提出原型。 Python,Ruby,Lisp / Scheme等都是不错的选择。原型是你的设计。然后你改进了。使用最能解决手头问题的范例。如果需要,使用C或其他系统语言编写的扩展名来优化热点。通过使用其中一种语言,您还可以免费获得扩展性,不仅可以在程序员级别,还可以在用户级别。像Lisp这样的语言可以动态生成和执行代码,这意味着您的用户可以通过编写小代码片段来扩展应用程序,使用软件本身编码的语言!或者,如果您选择使用C或C ++编写程序,请考虑为Lua这样的小语言嵌入解释器。将功能公开为以该语言编写的插件。
我认为,大多数时候OOP和OOD创建的软件都是过度设计的受害者。
总而言之,我编写软件的首选方式是:
最后一项功能使软件能够轻松适应特定用户(包括我自己!)的要求。
答案 17 :(得分:2)
答案 18 :(得分:2)
我使用测试驱动设计(TDD)。首先编写测试实际上有助于引导您进行干净和正确的设计。请参阅http://en.wikipedia.org/wiki/Test-driven_development。
答案 19 :(得分:1)
一种有用的技巧是将您独特的问题描述与您在现实世界中可以找到的内容联系起来。例如,您正在建立一个复杂的医疗保健系统,这个系统将风靡世界。你有什么例子可以随时打电话来模拟这个吗?
事实上。观察药房的运作方式或医生的房间。
将您的域名问题归咎于您可以理解的事情;你可以联系到的东西。
然后,一旦域内的“玩家”开始变得明显,并且您开始为您的代码建模,请选择“提供者 - 消费者”建模方法,即您的代码是模型的“提供者”,并且你是“消费者”。
与域名相关并在高层次上理解它是任何设计的关键部分。
答案 20 :(得分:1)
答案 21 :(得分:1)
在我设计类结构的冒险中,我注意到从编写一些伪代码开始是非常有帮助的。这意味着:我开始在最高级别“编写”应用程序代码的一些通用片段,使用它,并发现出现的元素 - 事实上,我作为程序员想要使用的元素。这是设计模块的一般结构及其相互作用的一个非常好的起点。经过几次迭代后,整个结构看起来更像是一个完整的类系统。这是设计代码部分的一种非常灵活的方式。您可以将其称为面向程序员的设计。
答案 22 :(得分:1)
老实说,一个很好的步骤将回过头来看流程图和序列图。有很多好的网站向您展示如何做到这一点。在查看我想如何将程序分解为类时,我发现它非常有用,因为我确切知道程序需要输入,计算和输出的内容,并且每个步骤都可以分解为程序的一部分。