ブログ 約3分

C++のOOP設計スキルを高める2つの簡単なアプローチ

Share this article
C++のOOP設計スキルを高める2つの簡単なアプローチ

結合度は通常、凝集度と対比されます。低結合は高凝集と相関することが多く、その逆も同様です。低結合は、構造の整ったコンピュータシステムと優れた設計の兆候であることが多く、高凝集と組み合わせることで、高い可読性と保守性という一般的な目標を支えます。このケーススタディの目的は、疎結合と高凝集の利点、そしてそれらをC++で実現する方法を示すことです。ここでは、ファイルからデータを読み込み、それを処理して結果を出力ファイルへ書き込むアプリケーションの設計を取り上げます。

設計の良くない解決策

この最初の設計では、 CDataProcessor という1つのクラスだけを使って次の処理を行います。

  • ファイルからデータを取得する。
  • データを処理する。
  • 結果を出力する。

そして、main メソッドがこのクラスの各メソッドを呼び出します。

Class diagram c1

この解決策の欠点

  • 低凝集: CDataProcessor クラスには多くの責務があるため、このアルゴリズムをほかのアプリケーションで簡単に再利用できません。
  • 高結合:処理ロジックがコンソールとデータプロバイダーに強く結合しています。

設計のリファクタリング

高凝集

凝集度を高めるには、各責務を異なるクラスに割り当てる必要があります。したがって、次の3つのクラスが必要です。

  • CFileProvider:ファイルからデータを取得します。
  • CDataProcessing:データを処理します。このクラスは処理を実行するためにほかのクラスを利用できますが、設計をシンプルに保つため、この例ではこのクラスだけで十分だと考えます。
  • CResultReporting:結果をファイルへ書き込みます。

これで各クラスが単一の責務を持つようになりました。この設計には次の利点があります。

  • 各クラスを理解しやすくなる。
  • コードを保守しやすくなる。
  • 各クラスをほかのアプリケーションで再利用しやすくなる。

低結合

データがファイルではなくデータベースに保存されている場合はどうなるでしょうか。前の設計では、アプリケーションがファイルプロバイダーに強く結合しています。

この問題を解決するには、任意のソースからデータを取得するためのメソッドを提供するインターフェースが必要です。さらに、ファイルベースのデータに対しては、このインターフェースを実装するクラスが必要です。

この目的には、 NVI を使うのが良い解決策になり得ます。このパターンは、事前条件と事後条件を定義できるため、抽象クラスだけを使うよりも有用です。特に開発中に役立つオブジェクト指向プログラミングの手法です。事前条件と事後条件により、クラス階層(一般的には抽象化)の不変条件が、プログラム実行中の指定された地点で破られないことを保証できます。

今回のケースでは、 IDataProvider インターフェースを追加できます。

Class diagram overview

また、 CFileProvider は IDataProvider を継承して GetDataFromImplを実装します。同じ設計は CDataProcessing と CReportResult

リファクタリング後のクラス間の新しいコラボレーションは次のとおりです。

Class diagram c2

クラスファクトリー

修正後の設計では、 IDataProviderIDataProcessingIReportResult の具象インスタンスが main メソッドで作成されます。より良い方法は、この責務をファクトリークラスに割り当て、必要なオブジェクト群をインスタンス化するロジックを分離することです。

コントローラー

すべてのクラス間のオーケストレーションは main メソッドに実装されています。この責務を Controller クラスに割り当てれば、ほかのアプリケーションでも再利用できるため、より望ましい設計です。

コントローラーは3つのクラスとやり取りする必要があります。ここで問題になるのは、これらのインスタンスをどうやってコントローラーへバインドするかです。

インスタンスのバインドには、次の2つの方法があります。

    • 次の名前のメソッドを追加する:  BindInstances(IDataProviderPtr,IDataProcesingPtr,IReportResultPtr)
    • コントローラーをテンプレートとして定義する。この場合、次のようにインスタンス化されます。
CController<CFileProvider,CDataProcessor,CConsoleReport>

2つの解決策の違いは、1つ目では各データプロバイダーが IDataProviderを継承しなければならないのに対し、2つ目では CFileProvider に必要なのは GetData(Data&) だけであり、 IDataProvider

OOPとテンプレートのどちらを使うべきかについては、C++の専門家の間で多くの議論があります。この2つのアプローチの間にある緊張関係については、こちらの 記事 が参考になります。

リファクタリング後のクラス間の新しいコラボレーションは次のとおりです。

Class diagram c3

リファクタリングの利点

リファクタリング後、アプリケーションはより柔軟になり、さまざまなシナリオで利用できます。

  • ファイル、データベース、XMLファイル、CSVファイルなどからデータを取得できる。
  • 1つのクラスだけでなく、さまざまなクラスを使ってデータを処理できる。
  • 結果をコンソールやファイルなどへ出力できる。
Share this article