web-dev-qa-db-ja.com

この設計を改善して、より疎結合なシステムとテスト容易性を実現するにはどうすればよいですか?

私の質問

Cを使用してArduinoに 倒立振り子 を構築しました(つまり、すべてが手続き的に行われました)。私はアプリケーション設計を自己学習しようとしています。SOLID、疎結合、およびテスト容易性を念頭に置いて、コードをより多くのOOアプローチにリファクタリングしたいと考えています。

この設計を改善して、より疎結合なシステムとより優れたテスト容易性を実現するにはどうすればよいですか?


UML

UML-class-diagram


クラスの簡単な要約

MotorController-モーターコントローラーのインターフェイス。

DrokL928-使用するモーターコントローラーはMotorControllerを実装しています。

Cart-DrokL928の便利なラッパーです。カートをより直感的に制御できます。

Encoder-ロータリーエンコーダーから値を読み取るための外部ライブラリ。

EncoderWrapper-Encoderのラッパー。このようにして、外部APIを1つの場所にカプセル化します。

StateVector-現在の状態データを保持します。

StateUpdater-EncoderWrapperからのエンコーダー値を処理し、StateVectorに割り当てます。

LQRController-振り子を安定させるために、カートに送信する(現在の状態に基づいて)PWM信号を計算します。参照してください: Wikipediaの線形二次レギュレーター


特定の設計に関する質問

  1. StateUpdaterは定数IDLER_PULLEY_RADIUSおよびSYSTEM_LOOP_RATEを使用します。これらはStateUpdaterに属さないようなにおいですが、状態の計算に関連しています。 StateUpdater内のこれらのプライベートメソッドはすべて、別のクラスStateCalculatorに入れることができると思います。

  2. LQRControllerは定数pendulumBoundを使用します。つまり、LQRコントローラーは、振り子の角度が特定の範囲内にある場合にのみ入力を計算する必要があります。どういうわけかこれはここに属していないように感じますが、多分私はそれについて間違っています。完全を期すために、StateVectorの各変数に境界を追加する必要があるかもしれません。

  3. 現時点では、EncoderWrapperへの参照が必要なため、Encoderをテストハーネスにインスタンス化できません。そして、拡張機能として、StateUpdaterをテストハーネスにインスタンス化することはできません。どうすれば修正できますか?

  4. 最も理想的なケースでは、StateVectorをそれほどハードコードせず、変数を変更できるようにする必要があります。したがって、LQRControllerには任意の長さのgainVectorが必要です(現時点では4にハードコードされています)。これは抽象化しすぎですか?そうでない場合、どうすればこれを達成できますか?考えてみると、アルゴリズムが非常に具体的であるため、StateUpdaterが任意の状態変数の状態を計算する方法がわからないため、抽象化が多すぎると思います。

2
Hunter

優れたデザインの特徴の1つは、ドメインについての知識があまりない人にとっても、ほとんどの場合自明です。たまたま、質問を読む前に倒立振子が何であるかわからなかったため、図からいくつかの洞察を収集しようとしましたが、残念ながら、Cartが問題に関連している可能性がある唯一のオブジェクトのようでしたドメイン。

ウィキペディアで inverted pendulums についてすぐに読んだ後、デザインの詳細はより理解しやすくなりましたが、Pendulumなどの抽象化が欠落していて、代わりに状態とStateVectorStateUpdaterの間に分散した動作。

私の提案は、システムを外側から考え、実装方法ではなく、モデル化しようとしている概念に焦点を当てることですたとえば、あなたは言及しました:

Cart-DrokL928の便利なラッパーです。カートをより直感的に制御できます。

Cartは単なる「便利なラッパー」ではなく、モデル化しようとしている主要なものの1つです。カートには位置と速度があり、その動きを制御できます。 EncoderMotorControllerは、それぞれ現在の状態の取得と動きの制御に使用される実装の詳細です。

同様に、角度と速度のプロパティを持つPendulumクラスがあり、これらの値を取得するために内部でEncoderを使用します。

最後に、InvertedPendulumCartで構成されるシステム全体を表し、Pendulumを使用してシステムを安定させるControllerクラス。

これにより、すべての部分がより意味のあるオブジェクトに移動されたため、StateVectorおよびStateUpdaterがデザインから削除されます。

現時点では、EncoderWrapperへの参照が必要なため、Encoderをテストハーネスにインスタンス化できません。 ...どうすれば修正できますか?

MotorControllerの場合と同じようにインターフェイスを作成します。実際の実装では外部ライブラリが呼び出され、テストの返信定型文を単に返す1つ以上の偽の実装を作成できます。

5
casablanca

完全な答えではありません。しかし、あなたは組み込みシステムを設計しているので(そして偶然にも、私はArduinoがサポートする振り子に取り組んだだけです):これらは通常、大規模なステートマシンです。コントローラーはいずれにしても同様のインターフェースを備えているため、クラスの設計はかなり簡単です。だからそれはマイナーな部分です。大きなものは、状態マシンの設計です。そしてもちろん、リアルタイムアプリケーションを扱っているので、必要なパフォーマンスを優先して、常に優れたデザインの神々に従うことができないことがわかります:-/

Regaring test:これは特にRTアプリケーションに対してトリッキーです。テストを追加することで、システムの動作をゆがめます。例えば、最終的には死後に読むことができる高速バッファロガーを追加しました。空想的なデバッグを行うことは物理学では動きを見たいときに動きを止めることができないため、単に不可能です。

1
qwerty_so