はじめに
本稿では、複数のプログラミング課題の実装について記述します。これには、以前の課題(クイズプログラム)の最終イテレーションと、最近行われた回路設計のイテレーション2つが含まれます。これらの課題は、自身のスキルを試すと同時に、技術的な成長を促す良い機会となりました。
設計と分析
課題4:多機能クイズシステム
1. コード分析
今回のイテレーションでは、主に以下の機能が追加されました:
- 問題タイプの拡張:多肢選択問題、記述式問題の追加
- クラス設計:継承の概念を活用し、多肢選択問題と記述式問題を基底の問題クラスから派生させ、必要なメソッドをオーバーライド
- 採点ロジックの拡張:多肢選択問題と記述式問題の採点ロジックを追加。部分正解のケースを考慮する必要があった
ここでは、以下のような工夫を行いました:
class MultiChoiceQuestion extends BaseQuestion { // 多肢選択問題クラス
public MultiChoiceQuestion(int qNumber, String qContent, String correctAnswer) {
super(qNumber, qContent, correctAnswer);
}
}
class FillInQuestion extends BaseQuestion { // 記述式問題クラス
public FillInQuestion(int qNumber, String qContent, String correctAnswer) {
super(qNumber, qContent, correctAnswer);
}
}
実際には、これは単純な継承ではなく、各問題タイプを区別するための実装です。回答を別々に保存し、以下のように判定を行います:
boolean isExactMatch = standardAnswer.equals(userAnswer); // 完全一致の場合(記述式用)
boolean isCorrectOrder = containsAllCharacters(standardAnswer, userAnswer); // 順序が異なる選択肢(例:正解=ABD、回答=DBA)
boolean isPartialMatch = containsSomeCharacters(standardAnswer, userAnswer); // 部分一致の判定(余分な文字がないこと)
3つの判定パターンを用意し、それぞれの場合で分岐処理を行うことで、多肢選択問題と部分正解のケースを処理しています。
2. ソースコード分析結果
SourceMonitorを使用したコード分析結果は割愛します。
3. UML図
クラス間の関係を示すUML図は割愛します。
4. シーケンス図
システムの動作を示すシーケンス図は割愛します。
課題5:電子回路シミュレータ
1. コード分析
これは電子機器をシミュレートする新しい課題でした。以下のような設計を行いました:
- 電子機器クラス(ElectronicDevice)
- プロパティ:
- identifier: 機器の一意識別子(例:VCC、GNDなど)
- inputVoltage: 入力電圧(デフォルト0V)
- outputVoltage: 出力電圧(デフォルト0V)
- deviceNumber: 機器番号
- inputPort, outputPort: 入力ポートと出力ポート
- メソッド:
- getOutputVoltage(): 出力電圧を返し、computeOutput()を呼び出して計算
- setInputVoltage(double voltage): 入力電圧を設定
- computeOutput(): 抽象メソッド、各機器が入力電圧に基づいて出力電圧を計算
- プロパティ:
- 具体的な機器クラス
- 電圧ソース(VoltageSource): 固定220Vを出力、電源機器を表現
- アース(Ground): 固定0Vを出力、アース機器を表現
- 制御機器(スイッチ、ステップ速度調整器、連続速度調整器): 回路のオン/オフ状態、速度調整、電圧出力の調整
- 制御対象機器(ランプ、ファンなど): 電圧によって制御され、ランプの明るさやファンの回転数などの具体的な操作を実行
各機器クラスはElectronicDeviceを継承し、computeOutput()メソッドを実装して、機器の出力電圧や明るさなどを計算します。
制御機器クラス:
- スイッチ: 回路のオン/オフ状態を制御。toggle()メソッドで状態を切り替え。
- ステップ速度調整器: 4段階(0-3)の速度調整機能。
- 連続速度調整器: 0から1の間で出力電圧比率を細かく調整。
これらの機器は、入力コマンド(例:#K1、#F2+など)によって制御されます。
回路接続と機器制御:
- ProcessSystemクラスが入力コマンドを解析し、機器を管理。機器接続(例:[VCC 1])と制御コマンド(#K2、#F3+など)を処理。
- 機器接続の解析:parseConnection()メソッドが正規表現を使用して接続コマンドを解析し、deviceListを更新。
- 機器制御:parseControlEquipment()メソッドが制御コマンドを解析し、実行。
直列回路(SeriesCircuit): 直列回路クラスが機器を接続し、直列回路の動作をシミュレート。電圧はVCCから他の機器に流れ、スイッチがオフの場合は回路が中断され、電圧は0Vになります。
computeSystemOutput()メソッドがシステム全体の出力を計算し、結果を機器番号順に出力します。
全体として、大きな継承関係が存在します。Electrical_equipmentが基底クラスで、その下にVoltage_source、Ground、Switch、Binning_governor、Continuous_governor、Light、Fanなどの具体的な機器クラスが存在し、最後に機器を管理するProcessSystemクラスがあります。
2. ソースコード分析結果
SourceMonitorを使用したコード分析結果は割愛します。
3. UML図
クラス間の関係を示すUML図は割愛します。
4. シーケンス図
システムの動作を示すシーケンス図は割愛します。
課題6:回路シミュレータの拡張
1. コード分析
今回のイテレーションでは、並列回路と新しい制御対象機器が追加されました。新しい制御対象機器については、Fanクラスを継承する新しいクラスを追加し、computeOutput()メソッドをオーバーライドしてファンの回転数を計算するだけで変更点は少ないです。また、機器の処理時にフローシングファン(floor fan)の操作を追加しました。
最も重要なのは新たに追加された並列回路です。抵抗による分圧計算を行うというシンプルなアプローチで、並列回路クラスには直列回路のリスト(private List<SeriesCircuit> circuits)を保持させ、computeOutput()で分圧を計算し、総電圧を計算します。
また、以前の機器のcomputeOutput()メソッドをcompute()に変更しました。これは、現在compute()メソッドを機器の各種パラメータ値の計算に統一し、computeOutput()を主に電圧や抵抗の処理に使用するためです。
2. ソースコード分析結果
SourceMonitorを使用したコード分析結果は割愛します。
3. UML図
クラス間の関係を示すUML図は割愛します。
4. シーケンス図
システムの動作を示すシーケンス図は割愛します。
課題を通じて学んだこと
- 課題4の実装では、多種類の問題タイプを設計するために継承関係を試みました。オブジェクト指向設計の原則に見合うように見えましたが、実際には「擬似継承」の状態でした。多肢選択問題や記述式問題は本質的に単一選択問題と大差なく、継承は問題タイプを区別するためだけのものです。この設計により、コードの拡張性が低下し、新しいタイプの問題を追加する際には既存クラスの修正が必要になります。
- 課題5と6の回路シミュレーションでは、機器間のデータフローと制御ロジックが重要でした。特に直列回路と並列回路の実装では、各機器の入出力インターフェースと制御コマンドの解析を慎重に設計する必要がありました。制御コマンドの解析時に、コマンドの順序と形式の一貫性を保証することが難しく、プログラムのエラーや予期しない動作の原因となりました。
- 回路シミュレーションの各機器には、電圧ソース、電圧制御器、負荷機器など、異なる計算ロジックがあります。各機器の出力は他の機器の影響を受ける可能性があります。デバッグ時に回路の出力が異常な場合、多くの場合は機器の状態やパラメータが正しく伝搬されていないことが原因です。例えば、直列回路ではスイッチの状態が正しく更新されないと、回路が中断され出力がゼロになります。並列回路では、compute()とcomputeOutput()を分けていなかったため、分圧計算時に抵抗値を考慮できず、計算誤誤が発生しました。
- 課題6では並列回路のシミュレーションを実装しました。並列回路の出力電圧は抵抗による分圧で計算しますが、実際のコーディングでは、電流の分流と電圧分配を正確にシミュレートし、特に異なる機器和回路の組み合わせの場合にどのように処理するかが課題でした。最初は直列回路と並列回路間の相互影響を適切に処理できず、計算結果が不正確になることがありました。
改善提案
- 継承のためだけの継承は避けるべきです。特に親クラスと子クラスの差が小さい場合、インターフェースやストラテジーパターンを使用してコードの柔軟性と保守性を向上させることを検討してください。
- 課題4のクイズプログラムでは、プロセスをさらに最適化するために、回答のセットなどの属性を追加し、採点を容易にするべきです。これにより、プログラムの品質が向上します。
- 課題6では、94点の成績でしたが、後からソート処理でA(フローシングファン)を追加し忘れていたことに気づきました。これは、フローシングファンがソートに含まれていなかった原因です。