状态图模式旨在专门用于删除 switch 语句,所以这听起来像是一种可怕的滥用。此外,状态应该只在异步事件上改变。如果您正在处理一个事件并且您通过多个状态(或 for 循环等)进行更改,那么这也是对该模式的可怕滥用。
我将从这两点开始,因为它们将解决您的大部分并发问题,只需修复它们即可。你需要确定的是:
- 您对系统的外部异步事件是什么?这些是唯一应该确定状态转换的事情,而不是在事件处理期间发生的事情。一个事件可能会导致 0 或 1 个状态转换。获得这些状态转换的列表后,您就可以重建系统的实际状态。如果您了解 UML 状态图,这将是在图表程序中绘制一个草图的最佳时机,不仅对您自己(尽管它会极大地帮助您),而且对将来必须返回到项目。正如您所了解的,这会发生。
- 现在您知道什么是真正的状态,请列出代码中不应该出现的状态。这通常表明某些东西可以“功能分解”。而不是每个状态对象,可能只需要一个单独的函数。这将减少状态对象的大量开销,并且应该极大地清理代码。
- 现在是时候解决你提到的那些可怕的 switch 语句了。如果它们真的基于状态,那么您根本不需要一个。相反,您应该能够直接调用状态机。
就像是:
myStateMachine->myEvent();
它应该可以在没有任何开关的情况下工作。但请注意,即使对于某些不能跨异步事件工作的对象,情况也可能如此。这也表明您可以在哪里使用继承来获得相同的效果。如果你有:
switch (someTypeIdentifier)
{
case type1:
doSomething();
break;
case type2:
doSomethingElse();
break;
}
通常正确的 OOP 方法是创建两个实际类型 Type1、Type2,它们都派生自抽象基础 TypeBase,并使用虚拟方法 doSomething() 来满足您的需求。这很有用的原因是因为它意味着您可以“关闭”处理(在打开/关闭原则的含义中),并且仍然通过根据需要添加新的派生类型来扩展功能(使其对扩展开放)。这可以疯狂地节省错误,因为它使开发人员摆脱了那些可能变得非常丑陋和令人费解的 switch 语句,而是将每个单独的行为封装在单独的类中。
4 - 现在寻找解决您的线程问题。识别从多个线程中使用的所有对象。做一个列表。现在,这些是如何使用的?其中一些总是一起使用吗?开始组团。这里的目标是找到最适合这些对象的封装级别,将对象分成单独的类来控制它们自己的同步,找出对象实际“事务”的原子级别,并制作类的方法公开那些有意义的事务,用适当的互斥锁、条件变量等包裹在幕后。
你可能会说“这听起来像是很多工作!为什么要做所有这些而不是自己写呢?” 好问题!:) 原因实际上很简单:如果您要自己完成所有操作,那么无论如何您都应该执行这些步骤。您应该识别您的状态、您的动态多态性并处理多线程事务。但是,如果您从现有代码开始,您还会拥有所有那些从未记录在案的未说出口的业务规则,并且可能会导致各种意外错误。你不必把所有东西都带过来——如果你怀疑这是一个错误,请与过去使用过系统的人(如果有的话)、QA 或任何可能发现错误的人讨论逻辑,看看它是否真的应该结转。
最后,这是一个手动过程,是软件工程的一部分。有一些 CASE 工具可以帮助绘制状态图,甚至将它们发布到代码中,还有一些重构工具,比如许多 IDE 中的工具,可以帮助在函数和类之间移动代码,以及可以帮助识别线程需求的类似工具. 但是,不应该为单个项目拾取这些东西。它们需要在你的整个职业生涯中学习,在多年的工作中更深入地学习它们,因为它们是成为软件工程师的一部分。他们不是为你做的。你仍然需要知道原因和方法,它们只是帮助更有效地完成它。