見切り発車の技法
不確実性をそのまま受け入れ、オブジェクト指向プログラミングで対処する方法について
「何をしたいか」は初めに決まる
2年間凍結されたプロジェクトの実装を引き継いだときの話です。 内容は、「生産マシンの稼働実績と製品番号を自動で紐づけする」というものでした。 この「したいこと」自体はプロジェクト開始時から変わらず、しかし揉めるのはその方法についてでした。 生産マシンのPLCと直接通信して稼働情報を得るのか、もしくはPLCがDBに書き込む情報を参照するのか。 実際に決まった仕様は後者で、これは本番稼働の1ヶ月前でした。
このように、プロジェクトの初期段階では「何をしたいか」は決まっていても、「どうやって実現するか」は不確実なことが多いです。 しかし、OOPの設計原則を用いることで、この不確実性を受け入れ、柔軟に対応することが可能です。 何故ならば、OOPは「何をするか」を中心に設計を行うため、実装の詳細が後から変わっても影響を最小限に抑えることができるからです。
構造を安定させる
上の例では、PLCと直接通信する場合とDBを参照する場合で、データの取得方法が大きく異なります。 逆を言えば、データの取得方法が変わっても、最終的に得られる「稼働実績と製品番号の紐づけ」という結果は同じです。 この取得方法の違いを吸収するために、インターフェースを定義し、そのインターフェースに基づいて実装を行うことが重要です:
// 稼働実績取得のインターフェース
interface IProductionDataProvider
{
List<ProductionRecord> GetProductionRecords();
}
class LinkProductionData
{
private IProductionDataProvider _dataProvider;
public LinkProductionData(IProductionDataProvider dataProvider)
{
_dataProvider = dataProvider;
}
public void LinkData()
{
var records = _dataProvider.GetProductionRecords();
// 稼働実績と製品番号の紐づけ処理
}
}
不確実な部分をインターフェースでそのまま表現し、実装の詳細は後から決めることで、構造を安定させることができます。 更に依存性注入を用いることで、未定な実装をスタブ・モックし、テストを行うことも可能です:
class StubProductionDataProvider : IProductionDataProvider
{
public List<ProductionRecord> GetProductionRecords()
{
// スタブデータを返す
return new List<ProductionRecord>
{
new ProductionRecord { /* ... */ },
new ProductionRecord { /* ... */ }
};
}
}
class LinkProductionDataTests
{
public void TestLinkData()
{
var stubProvider = new StubProductionDataProvider();
var linker = new LinkProductionData(stubProvider);
linker.LinkData();
// 結果の検証
}
}
何が不確実か
OOPは「何をしたいか」が明確であれば、その実現方法が不確実であっても、その不確実性をコードにそのまま表現することができます。 しかし、何が不確実であるかを正確に把握することが重要です。 その多くは明示されるとは限らず、プロジェクトの進行とともに明らかになることもあります。
不確実性を見つける1つの方法は、関係者が何について揉めているかを観察することです。 「何をしたいか」が関係者間で明確であれば、各々の裁量はその実現方法に集中します。 よって、その部分が不確実である可能性が高いです。
まとめ
OOPは不確実性を受け入れ、柔軟に対応するための強力な手法です。 従って、「何をしたいか」が明確であれば、その実現方法が不確実であっても、OOPの設計原則を用いることで、安定した構造を保ちながら開発を進めることが可能です。 重要なのは、何が不確実であるかを正確に把握し、その部分をインターフェースで表現することです。