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

发布于 2024-09-14 03:20:15 字数 377 浏览 4 评论 0原文

我正在编写一个具有几个接口(按钮、蓝牙、触觉旋钮)的电机控制器,这是一项正在稳步增长的任务,其规模比我想象的要大。我尝试从低级模块开始(例如编写在 I2C 总线上通信的代码),然后是上面的模块(与 I2C 上的特定设备通信的代码)巴士...),但我常常不得不回到较低的模块来处理我无法适应的怪癖。这要么需要很长时间,要么我得到的代码非常糟糕。

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

我尝试绘制一些总体图和特定级别/驱动程序的图,但我不确定如何构建它们,这样我就可以非常系统地了解它,并避免错过需要经过 2-3 层的奇怪信号层。

我想这就是获得 CS 学位的原因吗?我是一名电气工程师 :P

I'm writing a motor controller that has a couple of interfaces (buttons, Bluetooth, haptic knobs) which is a task that is steadily growing to be a larger than I figured. I've tried to just go at it by starting with low-level modules (e.g. write code to talk on the I2C bus), then ones above that (code to talk to a particular device on the I2C bus...), but all too often I have to dive back down to my lower modules to handle quirks I didn't accommodate. This either takes a long time or I get really hack-ish code.

My target is an 8-bit MCU, so bottom-up seems like I can exploit the hardware better. If I go top-down I don't have any structure to build on or test/debug.

I've tried drawing up some overall diagrams and ones for particular levels/drivers, but I'm not sure how to structure them so I can be very systematic about it and avoid missing the odd signal that needs to go up through 2-3 layers.

I guess this is the reason for a CS degree? I'm an electrical engineer :P

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

鯉魚旗 2024-09-21 03:20:15

听起来你走在正确的轨道上。有时,再多的计划也无法阻止您日后重新设计或重构系统的某些部分。尝试以下一些技巧:

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

It sounds like you are on the right track. Sometimes no amount of planning is going to prevent you from having to redesign or refactor parts of a system at a later date. Try some of the following tips:

  • Keep your code in modules separated by logical functions.
  • Do not duplicate code, instead design reusable methods for shared functionality.
  • Try to avoid the temptation to add hacks for special cases. Eventually this will become unmaintainable. Instead, adjust and refactor small sections as soon as possible. Trying to do a large re-design at the end will be more difficult.
  • Don't try to over-design the system from the start, as you might just be wasting your time when you get to the actual implementation.
  • Keep the lower levels as simple as possible, then build more advanced capabilities on top.
  • Document your functions and write a some unit tests, especially after adding complex conditional statements.
  • Try to catch errors as high up the stack as possible. For example doing input validation and checking return values. This will make debugging easier.
青衫负雪 2024-09-21 03:20:15

在处理多层代码时,当 API 不允许您完全按照您的意愿行事时,您很容易会陷入较低层。当同时编写多层时,这尤其困难。

这里有一些建议:

  • 将除您正在处理的层之外的所有其他层视为已密封的层。由另一家公司、开发人员等创建。请抵制修改另一层以解决当前层中的问题的冲动。
  • 为您正在处理的层创建一个“兄弟层”。这很难从抽象意义上描述,但可以说您的较低层是业务层,较高层是 UI,为不同的应用程序创建另一个 UI 层。现在,拥有同一 API 的两个使用者可以帮助指出每一层中应该包含哪些内容。
  • 尝试交替处理各层的顺序。例如,在某些应用程序中,我发现首先设计 UI,然后深入到业务层/数据库以使 UI 按设计工作更有用。其他时候,使用数据模型进行统计并处理 UI 更有意义。但重点是,在这两种情况下,您对 API 的“思考”是不同的。从两个角度查看多层代码会有所帮助。
  • 经验很重要。有时,犯下过度耦合代码的错误是真正学会避免这种错误的唯一方法。不要计划你的应用程序是完美的,而是计划它是不完美的。我的意思是,首先建立一个快速的开发/测试/重构周期,这样你就可以快速适应错误,直到你犯了错误之后才发现这些错误。这也是“一次性原型设计”派上用场的领域。做一个简单的草稿,从中学习,然后扔掉。丢弃的部分很重要。即使它令人惊叹,也要从头开始构建另一个。根据您从原型中学到的知识,您将不可避免地使其变得更好(并且根据我的经验,更有条理)。

When working in multi-tiered code, it's tempting to dive into a lower tier when the API doesn't let you do exactly what you want it to do. This is especially difficult when writting mulitple tiers at the same time.

Here's a few suggestions:

  • Treat all other tiers than the one you are working on as if they are sealed. Created by another company, developer, etc. Resist the urge to modify another tier to solve a problem in your current tier.
  • Create a "sibling tier" to the one you are working on. This is hard to describe in abstract sense, but lets say your lower tier is a business layer, and the higher tier is a UI, create ANOTHER UI layer for a different application. Now having two consumers of the same API can help point out what should be in each layer.
  • Try alternating the order in which you work on tiers. For example, in some apps I find it more useful to design the UI first, then work my way down to the business layer/database to make that UI work as designed. Other times, it makes better sense to stat with the data model and work up to a UI. But the point is, you "think" of an API differently in these two scenarios. And having looked at multi tiered code from both angles helps.
  • Experience counts. Sometimes just making the mistake of overly coupled code is the only way to really learn to avoid it. Instead of planning on your app being perfect, plan on it being imperfect. By that I mean first set up a rapid develop/test/refactor cycle, so that you can quickly adapt to mistakes that you wont see until after you've made them. This is also an area where "throwaway prototyping" comes in handy. Make a simple rough draft, learn from it, and throw it away. The throw away part is important. Even if its amazing, start building another one from scratch. You will inevitable make it better (and in my expirience, more organized) based on what you learned from the prototype.
故事未完 2024-09-21 03:20:15

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

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

为了改进,请将您的设计和代码交给有更多经验的人来审查。把自我排除在外,了解他们对问题的看法。你还可以读一本关于面向对象分析和设计的书(我以前很喜欢Peter Coad的书,现在不知道是谁写的了)。一个好的例子将展示如何将问题划分为具有明确角色和职责的对象的示例。

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

It's really more a matter of experience than your degree. If you are still learning things about how to control the hardware, then of course your code is going to change. I wouldn't agonize over that. However, since what you are really doing is prototyping, you should be ready to refactor the code once you have it working. Remove redundancies, compartmentalize data and functionality, and organize your interfaces so that it makes sense.

My experience is that device driver code needs top-down and bottom-up design/implementation, what I call outside-in. You know what the user is going to want to do and you can write those interfaces, and you know what the low-level driver needs to do and you write that. If they don't meet well in the middle, rethink your module layout.

For improvement, subject your design and code to review by people with more experience. Leave ego out of it and just get their take on the problem. You might also read a book on object-oriented analysis and design (I used to like Peter Coad's books. I don't know who's writing them now). A good one will show examples of how to partition a problem into objects with clear roles and responsibilities.

The other piece, once you have finished prototyping the drivers and know how to control the hardware, is to make sure you have detailed requirements. Nothing twists code more than discovering requirements while you're writing. You might also try learning UML and designing with diagrams before writing code. This doesn't work for everyone. Also note that you don't need to be coding in a language that supports object-oriented constructs to use OOD.

难如初 2024-09-21 03:20:15

如果您的问题是如何构建正确的抽象(情况似乎如此),我认为您可以做的最重要的学习事情(除了要求设计代码审查/阅读书籍/阅读代码之外)是思考在开始编写代码之前先努力一下

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

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

尝试多次“在头脑中运行设计”,直到您非常确定您已经考虑了最重要的细节并且您可以分辨出来(这是最重要的部分,因为您总是会错过一些事情,或者需求会发生变化)如果您错过了某些内容,您可以相对轻松地调整模块。

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

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

If your problem is how to build proper abstractions, which seems to be the case, I think that the most important thing you can do to learn (besides asking for design-code reviews/read books/read code) is TO THINK HARD BEFORE YOU START WRITING CODE.

It usually happens that you start with a rough idea of what you want and how it should be done and then go on to write code. Later on you discover that you didn't think things through and you have several gaping holes which now, because you have invested time in writing the code, are difficult to patch, which leads to either wasted time or hacky code.

Think hard about how to create a design that can easily handle changes. For example, encapsulate the data that needs to travel between layers, so if you later discover you missed a crucial parameter you can easily add it to the structure without having to modify code everywhere.

Try to "run the design in your head", several times, until you are reasoanly sure that you have considered most important details and you can tell (this is the most important part, because you'll always miss things or requirements will change) that if you missed something you can tweak the modules with relative ease.

UML can help you structure the way to think about the design. It's certainly no panacea but it shows the different criteria to consider when creating a software design.

It's a bit like the classic chess teacher advice: "sit on your hands" :-)

流年里的时光 2024-09-21 03:20:15

驱动程序适合分层方法。

您的驱动程序可以有几个“类”:

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

它们应该具有标准化接口,例如:

GetKnobLevel()
GetNextKeyboardButton

或者,另一种方法是

syscall(DRIVERNUMBER, command)

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

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

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

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

每层之间都有一个严格定义的、无差异的界面(我一直想象一个层间蛋糕,层之间有厚厚的糖霜......)。当然,您可能不存在该操作系统。

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

老实说,这有点“过度设计”的解决方案。但是,在复杂性变得越来越大的情况下,采用紧密的工程比采用松散的工程更容易。

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

通常,在真正开始对项目进行编码之前,您需要在某种程度上进行纸张设计、一些探索性工作并重新设计。流程图和总线转换图在这里效果很好。

这是我在嵌入式系统工作中喜欢的方法,并且对我来说效果很好。

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

Drivers are amenable to a layer approach.

Your drivers could have several "classes":

  • Input only
  • Output only
  • both I and O.

They should have a standardized interface, e.g.:

GetKnobLevel()
GetNextKeyboardButton

Or, another approach is to have something like

syscall(DRIVERNUMBER, command)

putting parameters/results in specified registers.

It is a simple, usable approach. A more complex variant might be to have circular queues between your hardware communicating code and your software communicating code, instead of registers.

Here is the mental model I am using:

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

There is a tightly defined, no-variance interface between each layer (I keep imagining a layer cake with thick frosting between layers...). The OS may not exist for you, of course.

If your MCU supports software and hardware interrupts like the x86 CPU, you can use those to isolate the drivers from the driver communicators.

This is a bit of an "overengineery" solution, to be honest. But, in a situation where your complexity is getting significant, it's easier to have tight engineering than to have loose engineering.

If you are communicating between layers, you can use a global variable for each communication "channel", and access it in a disciplined fashion, using only functions to access it.

Typically you will want to do paper designs at some level, some exploratory work, and redesign before you really set out to code your project. Flowcharts and bus-transition diagrams work well here.

This is the approach I've favored in my embedded systems work and it works well for me.

Also - this problem space isn't well explored by traditional computer science curricula. It's much less forgiving than the Web or modern operating systems.

小女人ら 2024-09-21 03:20:15

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

另外——代码是一门手艺。不要以为从一开始就能制定出完美的解决方案。当您学习新事物时,准备好更改您的代码。事实上 - 确保您将自我改变作为工作的一部分。

In my opinion, the most important aspects of well architectured code are low degree of coupling and separation of side-effects from state.

Also - code is a craft. Don't think you can make the perfect solution from the beginning. Be prepared to change your code as you learn new things. In fact - Make sure that you embrace change it self as part of your work.

半衬遮猫 2024-09-21 03:20:15

不要太油嘴滑舌,但我想起了《人月神话》中的一句话:“计划扔掉一个;无论如何你都会扔掉。”

其必然结果是“让它发挥作用。让它正确。让它快速。”

我想这让我提倡提前进行一些设计,但不要因此而陷入瘫痪。第一次不一定是完美的。计划重构。希望您所编写的内容不会真正丢弃太多代码,而是以更令人愉快的方式重新排列内容。

Not to be too glib, but a quote from The Mythical Man-Month comes to mind: "Plan to throw one away; you will anyhow."

The corollary of which is "Make it work. Make it right. Make it fast."

I suppose this makes me an advocate of doing some design up-front, but not getting paralyzed by it. It doesn't have to be perfect the first time through. Plan to refactor. Hopefully you will have written things in such a way that you're not really throwing much code away, but rearranging things in a more pleasing manner.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文