如何规划我的软件以避免过多的重写和相互依赖性

时间:2010-08-12 22:06:24

标签: architecture embedded 8-bit

我正在编写一个带有几个接口(按钮,蓝牙,触觉旋钮)的电机控制器,这是一项稳步增长的任务,比我想象的更大。我试图通过从低级模块开始(例如,在I2C总线上编写代码来进行通信),然后再使用低级模块(代码与I2C上的特定器件进行通信)。公共汽车...),但我经常需要潜入我的下层模块来处理我无法容纳的怪癖。这需要很长时间,否则我会得到真正的黑客代码。

我的目标是一个8位MCU,因此自下而上似乎可以更好地利用硬件。如果我自上而下,我没有任何构建或测试/调试。

我已经尝试为特定级别/驱动程序绘制一些整体图表和一些图表,但我不确定如何构造它们以便我可以非常系统化并避免错过需要通过的奇怪信号2-3层。

我猜这是CS学位的原因?我是电气工程师:P

7 个答案:

答案 0 :(得分:3)

在使用多层代码时,如果API无法让您完全按照自己的意愿行事,那么很容易潜入较低层。在同时编写多层时,这尤其困难。

以下是一些建议:

  • 处理除您正在处理的所有其他层之外的其他层,就好像它们是密封的一样。由另一家公司,开发商等创建。抵制修改另一层以解决当前层中的问题的冲动。
  • 为您正在处理的人创建“兄弟级别”。这很难用抽象的意义来描述,但是假设您的下层是业务层,而更高层是UI,为不同的应用程序创建另一个UI层。现在有两个相同API的消费者可以帮助指出每个层中应该包含的内容。
  • 尝试交替处理层级的顺序。例如,在某些应用程序中,我发现首先设计UI更有用,然后逐步使用业务层/数据库,使UI按设计工作。其他时候,使用数据模型统计数据并使用UI更有意义。但关键是,在这两种情况下,您“思考”API的方式不同。从两个角度看多层代码都有帮助。
  • 经验值得。有时只是犯了过度耦合代码的错误是真正学会避免它的唯一方法。而不是计划您的应用程序是完美的,计划它是不完美的。我的意思是首先建立一个快速开发/测试/重构循环,这样你就可以快速适应你在制作它们之前不会看到的错误。这也是“一次性原型制作”派上用场的一个领域。做一个简单的草稿,从中学习,扔掉它。扔掉的部分很重要。即使它很神奇,也要从头开始构建另一个。根据你从原型中学到的东西,你将不可避免地让它变得更好(并且在我的经验中,更有条理)。

答案 1 :(得分:3)

听起来你走在正确的轨道上。有时,没有多少计划可以阻止您在以后重新设计或重构系统的某些部分。尝试以下一些提示:

  • 将代码保存在由逻辑函数分隔的模块中。
  • 不要复制代码,而是为共享功能设计可重用的方法。
  • 尽量避免为特殊情况添加黑客的诱惑。最终这将变得无法维持。相反,请尽快调整和重构小部分。试图在最后进行大规模的重新设计会更加困难。
  • 不要试图从一开始就过度设计系统,因为你可能只是在实际实施时浪费时间。
  • 保持较低级别尽可能简单,然后在顶部构建更多高级功能。
  • 记录您的函数并编写一些单元测试,尤其是在添加复杂的条件语句之后。
  • 尝试捕获尽可能高的堆栈错误。例如,进行输入验证和检查返回值。这将使调试更容易。

答案 2 :(得分:2)

这实际上更多的是经验而不是你的学位。如果您仍在学习如何控制硬件,那么您的代码当然会发生变化。我不会为此感到痛苦。但是,由于您实际所做的是原型设计,因此您应该准备好在代码完成后重构代码。删除冗余,划分数据和功能,并组织您的界面,使其有意义。

我的经验是设备驱动程序代码需要自上而下和自下而上的设计/实现,我称之为外向内。您知道用户想要做什么,您可以编写这些接口,并且您知道低级驱动程序需要做什么并且您编写它。如果他们在中间不合适,请重新考虑您的模块布局。

为了改进,请让具有更多经验的人对您的设计和代码进行审核。让自我离开它只是得到他们对问题的看法。您可能还会阅读一本关于面向对象分析和设计的书(我曾经喜欢Peter Coad的书。我不知道现在是谁写的)。一个好的例子将展示如何将问题划分为具有明确角色和职责的对象的示例。

另一件事,一旦你完成了驱动程序的原型设计并知道如何控制硬件,就是确保你有详细的要求。在你写作的过程中,没有什么比发现要求更能扭曲代码了。在编写代码之前,您也可以尝试学习UML并使用图表进行设计。这对每个人都不起作用。另请注意,您无需使用支持面向对象的构造的语言进行编码即可使用OOD。

答案 3 :(得分:2)

如果您的问题是如何构建正确的抽象,这似乎是这种情况,我认为您可以做的最重要的事情(除了要求设计代码审查/阅读书籍/阅读代码)在开始编写代码之前要认为很难

通常情况下,您会大致了解自己想要什么以及应该如何完成,然后继续编写代码。后来你发现你没有想到事情,你现在有几个漏洞,因为你花了很多时间编写代码,很难修补,这导致浪费时间或黑客代码。

认真思考如何创建可轻松应对变化的设计。例如,封装需要在层之间传输的数据,因此如果您以后发现错过了一个关键参数,则可以轻松地将其添加到结构中,而无需在任何地方修改代码。

尝试“在脑中运行设计”,几次,直到你确定你已经考虑了最重要的细节并且你可以告诉(这是最重要的部分,因为你总是会错过任何事情或要求会改变)如果你错过了什么,你可以相对容易地调整模块。

UML可以帮助您构建思考设计的方式。它当然不是灵丹妙药,但它显示了在创建软件设计时要考虑的不同标准。

这有点像经典的国际象棋老师的建议:“坐在你的手上”: - )

答案 4 :(得分:1)

驱动程序适合图层方法。

您的司机可能有几个“班级”:

  • 仅输入
  • 仅输出
  • 我和O。

他们应该有一个标准化的界面,例如:

GetKnobLevel()
GetNextKeyboardButton

或者,另一种方法是使用类似

的方法
syscall(DRIVERNUMBER, command)

将参数/结果放入指定的寄存器。

这是一种简单易用的方法。更复杂的变体可能是在硬件通信代码和软件通信代码之间使用循环队列,而不是寄存器。

这是我正在使用的心理模型:

---
Application
---
OS
---
Driver communicators
---
drivers
---
hardware

每一层之间都有一个严格定义的,无变化的界面(我一直想象层层之间有厚厚的结霜......)。当然,操作系统可能不存在。

如果您的MCU支持x86 CPU等软件和硬件中断,您可以使用它们将驱动程序与驱动程序通信器隔离开来。

说实话,这是一种“过度工作”的解决方案。但是,在您的复杂性变得越来越重要的情况下,拥有严格的工程设计比松散的工程设计更容易。

如果您在图层之间进行通信,则可以为每个通信“频道”使用全局变量,并以规范的方式访问它,仅使用函数来访问它。

通常,在真正开始编写项目代码之前,您需要在某种程度上进行纸张设计,一些探索性工作和重新设计。流程图和总线转换图在此处运行良好。

这是我在嵌入式系统工作中所青睐的方法,对我来说效果很好。

此外 - 传统的计算机科学课程没有很好地探索这个问题空间。它比网络或现代操作系统更不宽容。

答案 5 :(得分:0)

在我看来,构建良好的代码最重要的方面是低度耦合从状态分离副作用。

此外 - 代码是一种工艺。不要以为你可以从一开始就做出完美的解决方案。准备好在学习新东西时更改代码。事实上 - 确保你拥抱自我改变作为你工作的一部分。

答案 6 :(得分:0)

不要过于狡猾,但是来自 The Mythical Man-Month 的引用浮现在脑海中:“计划扔掉一个;你无论如何都会。”

其必然结果是“让它发挥作用。做对。快点。”

我认为这使我成为预先做一些设计的倡导者,但不会因此而瘫痪。第一次通过时不必完美。计划重构。希望你能用这样的方式写东西,以至于你并没有真正抛出太多代码,而是以更令人愉悦的方式重新安排事情。