2

現在取り組んでいるプロジェクトには多くの問題があります。このプロジェクトは 10 年以上前のもので、90 年代に非常に人気があった商用 C++ フレームワークの 1 つに基づいていました。問題はステートチャートにあります。このフレームワークは、状態パターンの非常に一般的な実装を提供します。各状態は個別のクラスであり、エントリ時のアクション、状態内のアクションなどがあります。受信したイベントに従って現在の状態を設定するスイッチがあります。

悪魔は細部に隠されています。そのプロジェクトは巨大です。2000KLOCくらいです。確かにステートチャートが多すぎます (ステートチャートを使用して実装された「for」ループを見たことがあります)。さらに...フレームワークでは、ステートチャートを別のステートチャートに埋め込むことができるため、ネストのレベルが7つ以上のステートチャートが多数あります。ステートチャートは異なるスレッドで実行され、ステートチャート間でイベントを送信できるため、多くの同期の問題 (およびインターフェイスの大きな混乱) があります。

私は、この問題の規模が圧倒的であり、どう対処したらよいか分からないことを認めなければなりません。私の最初のアイデアは、ステートチャートからできるだけ多くのコードを削除して、別のクラスに入れることでした。次に、これらのクラスをステートチャートから委任して、仕事をします。しかし、その結果、論理的に特定の機能を持たない多くの個別の関数が作成され、ステートチャート アーキテクチャの変更には、そのクラスと関数の変更も必要になります。

だから私は助けを求めています:これを修正するのに役立つ本/記事/魔法のアーティファクトを知っていますか?少なくとも、隠れた依存関係を導入することなくステートチャートからできるだけ多くのコードを分離し、分離されたコードを保守可能、テスト可能、および再利用可能に保ちたいと考えています。

これを処理する方法について何か提案があれば、私に知らせてください。

4

3 に答える 3

1

ステートチャート パターンは特に switch ステートメントを削除するために使用されることを意図しているため、これは恐ろしい悪用のように聞こえます。さらに、状態は非同期イベントでのみ変更する必要があります。イベントを処理していて、複数の状態 (または for ループなど) を変更する場合、これもパターンの恐ろしい乱用です。

これらの 2 つの点から始めます。それらを修正するだけで、同時実行性の問題の多くが解決されるからです。決定する必要があるのは次のとおりです。

  1. システムに対する外部の非同期イベントは何ですか? これらは、イベント処理中に発生するものではなく、状態遷移を決定する必要がある唯一のものです。イベントによって、0 または 1 の状態遷移が発生する場合があります。これらの状態遷移のリストを取得したら、システムの実際の状態を再構築できます。UML 状態図を知っている場合は、自分自身のためだけでなく (非常に役立ちますが)、将来 UML 状態図に戻る必要があるすべての人のためにも、チャート作成プログラムで図をスケッチするのに最適な時期です。事業。あなたが学んだように、これは起こります。
  2. 本当の状態とは何かがわかったので、コード内にあってはならない状態をリストします。これは通常、何かが「機能的に分解」できることを示します。これらのそれぞれの状態オブジェクトの代わりに、おそらく必要なのは個別の関数だけです。これにより、状態オブジェクトのオーバーヘッドが大幅に削減され、コードが大幅にクリーンアップされます。
  3. 今度は、あなたが言及した恐ろしい switch ステートメントに取り組むときです。それらが本当に状態に基づいている場合は、まったく必要ありません。代わりに、ステート マシンを直接呼び出すことができるはずです。

何かのようなもの:

myStateMachine->myEvent();

スイッチなしで動作するはずです。ただし、これは、非同期イベント間で機能しない一部のオブジェクトにも当てはまる場合があることに注意してください。これは、継承を使用して同じ効果を得ることができる場所の指標でもあります。あなたが持っている場合:

switch (someTypeIdentifier)
{
case type1:
  doSomething();
  break;

case type2:
  doSomethingElse();
  break;
}

通常、正しい OOP メソッドは、必要なことを行う仮想メソッド doSomething() を使用して、抽象ベース TypeBase から派生した 2 つの実際の型 Type1、Type2 を作成することです。これが便利な理由は、(Open/Closed Principle の意味で) 処理を "閉じる" ことができ、必要に応じて新しい派生型を追加することで機能を拡張できることを意味するためです (拡張のために開いたままにします)。これにより、開発者は非常に見苦しく複雑になる可能性があるこれらの switch ステートメントから手を差し伸べることができるため、バグが大幅に節約され、代わりに個別の動作が個別のクラスにカプセル化されます。

4 - ここで、スレッドの問題を修正してください。複数のスレッドから使​​用されるすべてのオブジェクトを識別します。リストを作る。さて、これらはどのように使われますか?それらのいくつかは常に一緒に使用されますか? グループの作成を開始します。ここでの目標は、これらのオブジェクトに最適なカプセル化のレベルを見つけ、オブジェクトを独自の同期を制御する個々のクラスに分離し、オブジェクトの実際の「トランザクション」の原子レベルを把握し、クラスのメソッドを作成することです。適切なミューテックス、条件変数などで舞台裏でラップされた、意味のあるトランザクションを公開します。

あなたは、「それは大変な作業のように聞こえます。なぜ、自分ですべてを書くのではなく、それをすべて行うのですか?」と言っているかもしれません。良い質問!:) 理由は実に単純です。すべてを自分で行う場合は、とにかくこれらの手順を実行する必要があります。状態、動的ポリモーフィズムを特定し、マルチスレッド トランザクションのハンドルを取得する必要があります。しかし、既存のコードから始めると、文書化されていない暗黙のビジネス ルールがすべて含まれており、あらゆる種類の予期しないバグが発生する可能性があります。すべてを持ち込む必要はありません - バグであると思われる場合は、過去にシステムを操作したことがある人 (利用可能な場合)、QA、またはバグを特定できる人とロジックについて話し合い、それが本当にあるかどうかを確認してください。引き継ぐべきです。

最終的に、これはソフトウェア エンジニアリングの一部である手動プロセスです。状態図を作成してコードに公開するのに役立つ CASE ツール、関数とクラスの間でコードを移動するのに役立つ多くの IDE に見られるようなリファクタリング ツール、およびスレッド化の必要性を特定するのに役立つ同様のツールがあります。 . ただし、これらのことを 1 つのプロジェクトに取り上げるべきではありません。これらはソフトウェア エンジニアの一部であるため、キャリアを通じて習得し、何年にもわたる仕事を通じてより深く学ぶ必要があります。彼らはあなたのためにそれをしません。なぜ、どのように行うのかを知る必要があります。

于 2011-09-12T22:11:10.793 に答える
0

あなたの最善の策は、あなたが理解するのと同じくらい恐ろしく壊れている場合、最初から始める可能性が高いように私には聞こえます。ドキュメントはありますか?ドキュメントに基づいていくつかのsanerソフトウェアの構築を開始できますか?

完全な書き直しがオプションではない場合(そしてそれらが私の経験では決してない場合)、私は次のいくつかを試してみます:

  1. まだ持っていない場合は、システム全体のアーキテクチャ図を描きます。すべてのビットがどのように連携するかをスケッチしますこれは、システムを潜在的に管理可能/テスト可能な部分に分解するのに役立ちます。
  2. 何らかの要件やテスト計画がありますか?そうでない場合は、それを記述して、すでに存在するコード/機能のさまざまなチャンクに対して単体テストを開始できますか?それができれば、現在機能ているものをほとんど壊すことなく、リファクタリングを開始できます。
  3. 少し分解したら、単体テストを統合テストに組み込み、より多くの機能をまとめます。

私はそれらを自分で読んだことはありませんが、あなたが使用できるいくつかのアドバイスがあるかもしれないこれらの本について良いことを聞いたことがあります:

  1. リファクタリング:既存のコードの設計の改善(オブジェクトテクノロジーシリーズ)
  2. レガシーコードを効果的に使用する(Robert C. Martin)

幸運を!:-)

于 2011-09-12T21:33:51.397 に答える
0

ステートチャート (ネストされたステートチャートを含む) は、複雑な制御フローを指定、理解、さらにはシミュレート/検証するための強力な方法です。しかし、メリットを得るには、適切なツールのステートチャート モデル (以前は Statemate を使用していましたが、まだ利用できるかどうかはわかりません) に加えて、チャートからコードへの信頼できるマッピング (コードの生成に Statemate を使用) が必要です。 ) - そうすれば、(ほとんどの場合) 状態管理コードを忘れることができます! あなたの状況では、モデルを持っていない場合は、コードから逆にしようとします.モデルが登場するときの感覚。これがうまくいけば、コードの非常に優れた仕様/モデルが得られ、将来のコード編集がはるかに簡単になります (たとえそうでなくても)。

于 2011-09-14T10:04:59.633 に答える