我最近刚从大学毕业,开始从事程序员工作。我发现解决“技术”问题或用我说有1个解决方案的东西进行调试并不困难。
但是似乎有一类问题没有一个明显的解决方案,例如软件体系结构。这些事情使我困惑,使我感到极大的困扰。
我花了数小时的时间来尝试确定如何“架构”我的程序和系统。例如-我是否将此逻辑划分为1或2个类,如何命名这些类,是否应该将其设为私有或公开类,等等。这些问题占用了我很多时间,这使我感到非常沮丧。我只想创建程序-该死的体系结构。
我如何才能更快地完成架构阶段并进入自己喜欢的编码和调试阶段?
我最近刚从大学毕业,开始从事程序员工作。我发现解决“技术”问题或用我说有1个解决方案的东西进行调试并不困难。
但是似乎有一类问题没有一个明显的解决方案,例如软件体系结构。这些事情使我困惑,使我感到极大的困扰。
我花了数小时的时间来尝试确定如何“架构”我的程序和系统。例如-我是否将此逻辑划分为1或2个类,如何命名这些类,是否应该将其设为私有或公开类,等等。这些问题占用了我很多时间,这使我感到非常沮丧。我只想创建程序-该死的体系结构。
我如何才能更快地完成架构阶段并进入自己喜欢的编码和调试阶段?
Answers:
完美是善的敌人。
也就是说,您不应该偷工减料。软件设计将产生更持久的影响,并在将来为您(和您的同行)节省大量时间和精力。正确处理将需要更长的时间。花费在编程上的大部分时间不是敲击键盘,而是通过白板弄清楚如何解决问题。
但是您也不必担心完美。如果两个设计都陷入僵局,则意味着它们可能具有相同的优点。随便一个。一旦弄清了设计中的缺陷,似乎就无法改变。
(希望一旦发现不只是一种调试/解决技术问题的方法,它也会对您有所帮助。)
对于简单的小型程序(例如,源代码少于一万行),可以在编写代码时对其进行架构。如果您采用迭代和增量开发方法,则将逐步做出体系结构决策:因此,编写几十行代码(添加一些单个微功能),对其进行改进,直到编译器没有警告返回,然后在您的调试器,然后重复。
我是否将这种逻辑分为1或2个类,如何命名这些类,应该将其设置为私有还是公共类,等等。这些问题占用了我很多时间
他们不应该。而且对于一个小型程序而言,它们无关紧要(因为小型,简单的程序易于改进,例如,更改名称等)。您只需要保持一致并优先考虑源代码的可读性即可。您可能会发现需要,不时,稍微重构你的程序的一些小零件(这是不是一个大问题)。
将此与许多免费软件项目(甚至大型项目,如Linux内核)进行比较。在早期阶段,开发人员并未花费大量精力进行“架构”。UML几乎从未在自由软件中使用过。此外,通过研究几个免费软件项目的源代码,您将学到很多东西。
作为新手,您可以在一个团队中从事大型软件项目,您可以在其中简单地信任高级开发人员(他们可以做出体系结构决策),也可以独自从事小型项目(通常少于几十万)源代码行)。在后一种情况下,您将做出增量的体系结构决策,不时地重构您的应用程序,然后“体系结构设计”将自然发展。
我怎样才能更快地完成构架阶段并进入我喜欢的编码和调试阶段?
对于耗时不到一年的小型软件项目,非常容易:不要做架构。在整个设计上花半个小时的时间进行头脑风暴。然后开始使用迭代和增量开发方法编写代码:编写几十行,对其进行编译(启用所有警告和调试信息,例如,g++ -Wall -Wextra -g使用GCC for C ++),直到没有警告(并将其传递给一些简单的静态源代码)代码分析器(如果您有一个代码分析器,例如clang-analyzer),请使用调试器测试该代码,然后将其提交给您的版本控制(例如git),冲洗并重复。但是,一定要避免技术债务:当有异味时,请进行工作(通过重构和重新实现)以加以改善。
另一方面,在团队环境中,架构工作需要进行初步讨论以定义每个团队成员的责任。该讨论由高级开发人员(不是新手)进行。阅读有关敏捷软件开发和The Mythical Man-Month的信息。
我只想创建程序,架构就可以了。
出色的直觉(至少对于小型项目而言)。因此,考虑一下您的程序,然后开始使用迭代和增量开发方法对其进行编码:对几十行进行编码,并确保它们运行良好,然后重复执行。在此之前,请研究类似的自由软件项目的源代码(并观察其体系结构),并且更一般地进行一些书目工作和研究。
在某些情况下,请考虑使用元编程方法:在某些情况下,您想生成一些“源文件”(示例包括使用诸如bison之类的解析器生成器,诸如SWIG,Google protobuf之类的粘合代码生成器,有时您可能想编写一个简单的脚本-或使用GPP等通用预处理器-发出一些C ++或Java代码,以避免重复编码)。
PS。我是一名研究工程师,拥有计算机科学博士学位,并且拥有40年的经验,并且在成功完成多个中型项目和几个大型项目(GCC编译器本身)时,我从未像您的问题所建议的那样进行“架构”研究)。对我来说,“架构”只是接下来几天或几周工作的计划阶段(而且我通常在做梦或睡觉的时候这样做,当然也没有电脑,甚至没有铅笔)。另外,在撰写研究经费时,我不知何故不完整地设计了一个体系结构。
注意:某些软件项目比其他软件需要更多的体系结构。例如,如果您编写人工心脏或神经外科手术机器人的控制系统,则将无法以与编写普通手机应用程序相同的方式工作。另请参见Norvig的“ 十年内自学编程”页面。
我要牢记三个座右铭。
“一切都应该尽可能简单,但不要更简单”
以您的“一两个班级?”为例,我想问“哪个是更简单的解决方案?”
“没有明显的错误”与“显然没有错误”
后者是可取的!
这就是为什么它需要简单的原因,即您可以对此进行推理。一个大类可能会(或可能变得)太大而又太复杂而无法推理,在这种情况下,您将其分成几个较小的类,您可以在其中说:“每个类都很小,并且会做它会做的-而且它们的界面很简单,并且以正确的方式结合在一起。”
但是,新手有时并不理会第1步,即在脑海中运行它(例如,因为它太复杂了),但在这种情况下,它只是“偶然”运行,而不是“理论上”运行,可能是因为您没有对其进行足够的测试以发现非显而易见的错误。
这就是“重构”。
实际上,这意味着:
添加新功能
...并如上所述重复
这符合YAGNI之类的座右铭,即在您需要...之前无需重构(担心架构),而是及时创建正确的架构,即当您出于某些特定目的需要它时。
您可以做的就是从所需的最少抽象量开始。例如,在一个文件中的一个Person类。现在,当您继续添加代码和功能时,您开始看到需要移至其他抽象的事物。例如,单一职责原则(SOLID)告诉您Person类中没有与地址解析有关的方法。因此,您现在知道需要一个Address类。
但是,花一些时间思考系统的“最小抽象数”看起来总是很好的。从足够好的体系结构开始,然后逐步进行改进。
编辑:@Basile答案给出了有关如何迭代和改进最小体系结构的示例。
花时间思考系统的架构不会浪费时间。
我相信您的问题可以改写为“我如何才能更有效地进行体系结构决策?”。
我对此的简短回答是:您需要发现将使您能够可靠,有效地进行决策的核心原则,然后您需要实际走出去并构建一个真实的软件。这将是寻找知识,反复试验和个人发展的漫长旅程。
-
还有更长的答案...
首先,我要澄清一下概念:当我使用流程,服务,API和数据库时,我使用体系结构一词来描述复杂软件系统的结构。当我使用类,函数和库时,我用“ 设计 ”一词来描述一个较复杂的系统中只有一个的结构。这些是我的定义,有些人有不同的定义。但是在这种情况下,我相信您正在谈论设计。
我认为在讨论此主题时,有3件重要的事情要牢记:
存在架构和设计时,没有通过图表或文档对其进行明确描述,也没有由团队或个人(架构师)来维护它们。任何系统都具有可以在事后描述的固有架构和固有设计。
软件开发不是编程,而是随着时间的推移编程。我之所以要与众不同,是因为我认为这是业内人士(包括我本人在内)最大的盲点之一。这意味着,与大学项目或个人副项目相比,在现实世界的软件系统上工作要成倍增加,因为任何架构决策都会随着时间的推移对系统的开发产生重大影响。现在,您的决定将再次出现,这肯定会困扰您。
因为架构和设计本质上是存在的,并且因为代码库是随时间变化的生命,所以架构和设计也需要发展。它们要么通过在核心时间做出的有意识的决定而以受控的方式进化,要么在编码的驱动下混乱地进化。了解这一点至关重要,因为这意味着传统的“先架构,后写代码”的方法是有缺陷的。当然,当从头开始一个项目时,需要先进行一些架构和设计工作。但是除此之外,在开发系统时还需要做出许多架构和设计决策。
提炼上面进一步,需要注意的事实是,你是非常重要的将被设计决策在编写代码时,有意或无意地。您应该努力自觉和批判地做出尽可能多的决策,因为任何轻率的决策都会对以后的工作产生重大影响(这种影响通常表现在代码库中,很难修正以修复错误或实现功能)。罗伯特·C·马丁(Robert C. Martin)在他的《清洁架构》(我强烈建议)中用数据很好地说明了这一点。
那么,既然我们知道为什么架构和设计很重要,那么哪些核心原则可以为我们提供良好的决策框架?我在职业生涯的早期就有这个问题,我觉得我的工具集中缺少一些东西,但不知道是什么,也不知道如何描述或寻找它。我将分享我随着时间的推移发现的一些原则,并希望它们会使您的生活更轻松:
通过阅读Martin Fowler的书“重构:改进现有代码的设计”,可以了解一组非常简单但功能强大的编码技巧。这里有太多要列出的内容,但是这些是非常底层的编码时决策,您可以做出这些决策来极大地改善代码结构并帮助您做出设计决策。本书还为将单元测试集成到您的个人工作流程以及如何编写可测试的代码提供了很好的案例。
专门针对OOP,您应该查看SOLID原则。它们有点抽象,一开始很难让人想起,但功能非常强大。我建议您从前两个开始,以快速获得最大收益:
单一职责原则:一个类应仅具有一个职责(即,仅对软件规范的一部分进行更改才能影响该类的规范)。
开放/封闭原则:“软件实体…应该开放以进行扩展,但封闭以进行修改。”
类应通过其组成(通过包含实现所需功能的其他类的实例)来实现多态行为和代码重用的原理,而不是从基类或父类继承。
当然,这些只是概念,不是规则。第一步是了解它们并意识到它们。接下来便是在实践中实际使用它们,并在何时应遵循和不应当遵循时建立经验。然后,您需要不断地完善您对这些概念,它们的消极面以及彼此之间复杂的相互作用的理解。
我认为我能给您的最有价值的建议是:对自己有耐心。您刚刚走了一条漫长而充实的道路。继续练习和尝试,注意什么有效,什么无效,随着时间的流逝,您只会变得更好。
您描述的大部分内容并不是真正的(重要的)架构-好的命名和好的类设计是您应该具备的本性。您编写的代码越多,效果就会越好。对于此类问题,最有用的通常是结对编程-它有助于弄清此类问题,并帮助您有效地学习如何解决这些问题。
在项目之前需要进行架构的地方:
收集确切的需求和非功能性需求(我需要支持多少请求/秒?)。此阶段中的任何不匹配都将导致编码陷入困境-在事实既浪费时间,烦人又有时甚至是不可能的情况下,整合错过的想法。我知道这并不像编写代码那样有趣,但是尝试使代码执行原本不是为设计目的而做的事情就显得没那么有趣了。
如果合适,请定义系统的有界上下文,并确保您的词汇完整。例如,如果企业谈论“ Frobbels”,请确保使用“ *** Frobbels”命名类/接口等。听起来很琐碎,但是如果您谈论工作流,而企业则谈论操作,那么翻译变得非常烦人。
如果您与多个人/团队合作,请尽早描述您的界面,并确保所有人都理解所有假设和问题-如果您没有共享的上下文,那么集成将是“有趣的”。例如,您构建了一个香蕉图片生成器,但是您的front-dev需要一个苹果图片生成器。或者您构建的东西可以每秒响应100个请求,但是需要10000 r / sec。
注意:这在很大程度上受我在微服务架构上的工作影响。服务是如何在内部构建的,也可以进行架构设计-但是在大多数情况下,它不如正确构建全局重要。
我不会给您带来很多术语和缩写(大多数编码人员/软件工程师几乎都没有同意这些术语和缩写)。相反,请考虑以下事项:
您正在学习-您不是在浪费时间,而是在尝试不同的方法并了解有效的方法。您可以通过在想到的第一个解决方案中解决一个问题并在无法解决问题时(或在无法解决问题时)进行更改,而无需进行大量规划。如果运作良好,那就太好了!您已经找到解决问题的简单方法。简单的解决方案只要运行良好就可以,有时它们也足够好。
一切都是权衡之举-您可以通过许多不同的方式来设计同一系统,可以权衡时间和空间,复杂性和灵活性,抽象性和可读性,或者可以进行多种权衡中的任何一种。在软件工程中,任何解决方案都不是完美的,规则也不例外。否则,告诉您的人要么天真,要么卖东西。
作为一名应届毕业生,编码和调试可能会非常令人兴奋,但是随着时间的流逝,它会逐渐消失,并且您正在学习的技能将在您工作时为您提供良好的服务。
我认为构建软件比工程更像艺术品/工艺。伟大的艺术不仅关乎个人的笔触,而且关乎艺术家/工匠的高层决策和权衡。
我将尝试从Web开发的角度回答这个问题(意思是:来自人们对架构苦恼不已的领域)。首先,我将解释为什么人们关心建筑,然后概述如何更快地通过建筑部分。
通过为您提供可以识别并用于浏览代码的约定,代码样式使读取代码的特定部分变得更加容易。同样,良好的体系结构可以帮助您确定在哪里可以找到处理特定功能的代码。例如,在大多数Web项目中,体系结构与文件夹和文件的排序方式密切相关。另一方面,好的架构实际上应该帮助您减少对代码的思考,因为它应该已经在任何代码所属的地方都具有直观的位置。
另外,良好的体系结构可以避免许多容易使您的代码无法轻松使用的陷阱。同样,如果您做出体系结构决策,则应建立一个约定,以帮助您减少有关如何编写代码的思考。
您可以更快地完成体系结构部分的操作:
已经指出了许多答案。首先问问自己是否真的需要架构。如果您没有太多的代码(并且可以合理地确定该项目在不久的将来不会增长),则可以跳过体系结构部分,将一些可以正常工作的内容拼凑在一起。但是,如果您还处于职业生涯的早期,我将利用这个机会尽可能地进行练习。在某个时候,您将进行更大的项目,到那时,可能学习到很晚了。
有了这些,您可以采取什么措施来减轻架构的痛苦:
确定体系结构应该是计划过程的早期部分。一旦确定要制作哪种类型的应用程序/程序/网站,就应该考虑采用哪种架构。
现在该是无情地偷东西了。关于如何正确设置程序体系结构,有很多文献,而这些现有体系结构原型涵盖了惊人的用例。即使您不知道如何实现它们,也应该大致了解那里存在什么样的体系结构。
如果您决定采用某种架构,请坚持使用。在大多数情况下,架构决策应该是直观的,并且仅在初始设置后几秒钟即可完成。这很多归结为经验。
最后,不要想太多。您举了一个例子,说明什么东西应该是公共的还是私人的,事实是,是否将所有内容都公开都是无关紧要的。是的,您不应该这样操作,一段时间之后,很多小错误都会堆积起来,但是到最后,它也可能不会杀死您的项目。首先是创建工作软件!
(PS:最后一句话不是懒惰的借口。优先考虑工作软件并不意味着您有一天不必学习良好的编码。)
我怎样才能更快地完成构架阶段并进入我喜欢的编码和调试阶段?
通过将这项任务委派给您经验丰富的同事(或向他们寻求帮助)。
您只是缺乏快速做出此类决策所需的经验。Uni为您提供了很好的理论背景,但它只会使您进入起点。判断给定架构在给定情况下的其他方法就是知道过去在类似情况下的相似表现。
与工作能力强于您的人一起学习是最快的学习方法。如果没有人可以求助,那么您需要更好的工作。“更好”就像“更好地满足您的需求”。困境已证明,目前最需要的是知识和经验。您喜欢编码和调试阶段吗?听起来像个初中生。但是,大三需要大四的指导。那就是这些工作描述的重点。到目前为止,互联网上的陌生人只能为您提供帮助,您需要一名导师。
我看到这个问题有一些严重的问题。开始吧。
如何停止浪费时间设计建筑
这个问题颇为繁重。另外,您不设计架构。你是建筑师。体系结构和设计是互补的和相关的活动,但是即使它们可能重叠,也不相同。
同样,以相同的方式可以浪费时间进行架构(通过过度架构),也可以浪费时间进行过度设计和过度编码(通过以比必要的方式复杂得多的方式对内容进行编码,或者通过失败来完成)所需内容的代码。)
适当的体系结构旨在防止编码浪费。它通过限制,缩小和记录复杂系统的可能方式来做到这一点:1)设计,2)编码和测试,3)交付,4)维护,5)从故障中恢复以及6)最终退役。
我的经验是,那些只喜欢编码的人,他们只是编码而没有考虑系统的长期运行和维护方式,而是转到下一个炙手可热的土豆,留下了一些可怜的灵魂来维护丑陋的魔像。
但是我离题了...
这就是事实:对于足够简单的系统,体系结构是不言而喻的,源于合理的设计和实施实践。
它仅适用于涉及大量人员或系统级软件的大型系统,这些系统或系统级软件执行需要显式架构的非常复杂的工作。
我最近从uni毕业,开始从事程序员工作。我认为解决“技术性”问题或进行调试并不困难,我认为这只有一种解决方案。
这是该专业的最低要求,我很高兴您在做这些职业时没有问题(如果您这样做,我会很担心。)
但是似乎有一类问题没有一个解决方案
这些就是我们职业的基础,这是雇主愿意为我们支付(通常)高于平均水平的薪水的这类问题。
实际上,值得解决的问题是可以解决多个问题的问题。现实世界中的问题就是这样。作为软件开发人员,世界需要我们的专业知识来做出可接受的权衡。
-诸如软件架构之类的东西。
事物的体系结构是复杂系统的必然特征,无论是虚拟/软件还是在具体的世界中。每个运行,需要输入并产生输出的系统都将是复杂的并且具有体系结构。
当我们开发用于此类系统(银行系统,电力监控系统,门票销售系统等)的软件时,我们的目标是生产一款模仿该系统功能和要求的软件。
我们只是不能简单地为其添加翅膀并为其编码牛仔风格。我们需要某种架构。如果项目需要数十名工程师(甚至更多),则尤其如此。
这些事情使我困惑,使我感到极大的困扰。
那没问题。这不是一个容易学习或教导的主题,并非没有大量实践。
我花了数小时的时间来尝试确定如何“架构”我的程序和系统。例如,我是否将此逻辑划分为1或2个类,如何命名这些类,是否应将其设为私有或公开类,等等。这些问题占用了我很多时间,这使我感到非常沮丧。我只想创建程序,该死的架构。
不幸的是,那不是软件架构。
它甚至不是设计,而只是编码。我将在本文的底部提供一些建议。
我怎样才能更快地完成构架阶段并进入我喜欢的编码和调试阶段?
我很难找到答案的方法,因为这很令人激动。
我们是在努力完成工作,还是只是在享受练习?两者相同时很棒,但是在现实生活中,很多时候都不一样。
做我们喜欢的事情很棒,但是在像我们这样复杂的职业中,只专注于我们喜欢的事情,这对取得丰硕的职业不利。
您不会进步,不会成熟或不会获得新知识。
陆军中有这样一句话,“拥抱吸吮者”。
其他短语也有类似的建议。“如果不吮吸,那是不值得的”,而我最喜欢的是,“如果它吮吸(而且很重要),就做,直到它不再吮吸为止。”
在我看来,您仍在努力了解两者之间的区别
编码(如何编码您的类,模块或不编码,命名约定,访问可见性,范围等),
设计(多少层,前端/后端/ db,每层如何通信,去向何处)以及来自简单系统设计的隐式架构决策,
体系结构(在需要数千甚至数十万工时的复杂系统中发现)
因此,我建议您深入研究第一个主题(编码)以将其提升到一个新的水平。
罗伯特·“鲍伯叔叔”·马丁的《清洁法》是一个很好的起点。
另外,我建议您熟悉称为LCOM或LCOM4的特定的面向对象软件指标。
它可能是相当数学的,并且不是防弹的,但是您的目标应该是从经验上理解和检测(如果愿意,或者说是眼球)类是否具有内聚性或缺乏内聚性。
http://www.aivosto.com/project/help/pm-oo-cohesion.html#LCOM4 https://www.computing.dcu.ie/~renaat/ca421/LCOM.html
这与我们都应该熟悉的“单一责任原则”或SRY 紧密相关。如果要精通编码,SRY是我们都需要熟悉的5种“固体”之一。
随着我们逐步了解SOLID原则,我们还需要熟悉“ GRASP”原则,该原则支配或指导我们如何编写类。
最后,我还建议以下几点:
马丁·福勒(Martin Fowler)和肯·贝克(Ken Beck)的“重构”将是我在此列表中阅读的下一本书。
理查德·米切尔(Richard Mitchell),吉姆·麦金(Jim McKim)和伯特兰·迈尔(Bertrand Meyer)(埃菲尔(Eiffel)的名望的后来者)撰写的“按合同设计,以示例为单位”。这本书已经绝版,但您可以在亚马逊上找到便宜的二手书。
这样,您应该掌握如何开始编码和设计,并通过实践来掌握和迁移(或至少掌握)软件体系结构。
我相信会有其他专业人士会加,减或反对这些建议。他们将提出其他建议,可能会根据自己的经验得到验证。
我只能说的是-没有捷径。
祝一切顺利。
坦率地说,这里有很多信息。我认为人们在尝试学习如何设计系统时犯了一个主要错误:他们尝试按照完成工作的顺序来思考它。相反,您需要向后工作。也就是说,设计/体系结构的主要目标是确定最终结果。
打个比方,考虑房屋的建筑。建筑师并没有开始问自己这样的问题:“这所房子应该有几扇窗户?”,“应该在哪里放置第一块砖?”。这些实现细节不是设计,而是从设计中派生的。架构始于愿景,也许是成品房屋外观的草图。它是单户住宅还是复式住宅?它是豪华住宅还是负担得起的住宅?同样,变量是否为私有变量以及是否拆分类与体系结构关系不大。
首先要弄清楚设计的目标是什么。例如,这是一次性解决方案吗?它会在几十年内进行扩展,修订和维护吗?答案将表明完全不同的设计,这就是体系结构的重点。一旦确定了需要做什么,设计的细节就会自然而然地出现。这些细节并不是显而易见的或容易的,而是这些选择所基于的高级计划。
在获得某种形式的写-编译-测试循环之前,如何判断应该花多少时间来架构任何软件是非常简单的:足够的信息适合您的头脑,仅此而已。除非您从事的项目要求采用更严格的方法。在这种情况下,作为初学者,您很可能应该阅读架构文档,而不是编写文档。
至于命名,对我来说,这是“写作”的一部分,但这无疑是编程中非常重要的一部分:不要犹豫,认真思考如何命名事物,而在命名范围越广,思考就越努力。
找到正确的名称,正确的体系结构,正确的模块化和正确的抽象是您从错误中获得的经验的一部分。多年来,我编写了一个程序执行相同的操作大约五次,并且每次的代码都非常不同,因为过去的每次迭代都为我提供了更好的设计提示。