web-dev-qa-db-ja.com

抽象化のより高いレベルで異なる古典的なOOPオブジェクト間の接着剤を処理する最良の方法は何ですか?

オブジェクト指向プログラミングでは、各クラス/オブジェクト/インスタンスは明確に定義された目的を持つべきであると一般的に言われています。たとえば、ウィンドウツールキットにはWindowクラス、MinecraftのようなゲームにはChunkLoaderクラス、Matrix<>線形代数ライブラリのクラス(テンプレート)。これらの各ケースで、クラスには特定のジョブがあります。これらの例では、ジョブはそれぞれ、基盤となるウィンドウAPIを処理し、有用な関数を公開し、チャンクをロードおよびアンロードし、有用な操作でマトリックスを表します。

このガイドライン(各クラスの単一の目的)は多くの場合良いアドバイスですが、プログラムの接着剤の部分ではフラットに見えるようです。つまり、必要に応じて、チャンクローダー、行列、ウィンドウクラスを呼び出す必要があります。これは、それらの間でデータを往復させたり、それらの間の適切なポリモーフィックインターフェイスを使用して設定したり、前述の部分(ChunkLoaderなど)を組み合わせて全体を構成する他のホストにすることができます。たとえば、Minecraftのようなゲームでは、メインループを処理し、必要に応じてより明確な目的で他のクラスを呼び出す必要があります。

私がプログラムに取り組んでいる間、私の最初の(そしてこれまでのところ唯一の)アイデアは、プログラムのより広い領域を抽象化し、それらの内部のより抽象的でないクラスを扱うクラスを作成することです。ただし、これは名前などに関係なく、マネージャクラスの形式をとる傾向があります。たとえば、ビデオゲームには、世界の一般的な部分ごとにデータメンバー(つまり、世界自体をレンダリングするメンバー、他のプレーヤー用、およびGUI用のメンバー)があるRenderingManagerクラスがある場合があります。 )。次に、その内部で、GUIのさまざまな部分(インベントリ、ヘルス、一時停止画面など)の独自のメンバーを持つGuiManagerクラス。

ただし、この再帰的なマネージャー構造にはいくつかの問題があります。

  • 私が尋ねる前に行った調査によると、マネージャー(マネージャーと呼ばれるかどうかに関係なく)は自分自身では悪い習慣です。
  • マネージャークラスは、プログラムの多く(またはすべて)の部分に触れるため、プログラムの特に複雑な部分であり、これらの部分の特異性に留意する必要があります。これは、抽象化の各レイヤーへのクリーンなインターフェースを備えることで部分的に軽減できますが、すべての問題が常にクリーンなインターフェースに対応できるわけではありません。
  • 上位クラスでは、残りのコードの膨大な部分に依存しているため、マネージャークラスはその性質上、単体テストが困難です。単体テストを実行するには、基本的に完全なプログラムが必要です。ポリモーフィズムを緩和策として使用することは望ましくありません。各マネージャークラスへのインターフェイスは、抽象化のレベルが低いタイプを完全に隠す必要があるため、他のタイプへのポインターの受け渡しが実用的ではなくなるためです。さらに、C++などの言語では、基本的なメンバーではポリモーフィズムを使用できません。つまり、単体テストを有効にするために、すでに複雑なマネージャークラスはより複雑になります。
  • このマネージャークラスの階層は、各ノードがインスタンスであるツリーで表すことができます。メインマネージャーをルート、サブマネージャーをその子として、以下同様に最も基本的なクラスまで続きます。場合によっては、ノードが従兄弟と通信する必要があります(たとえば、レンダラーがビデオゲームの物理エンジンにクエリを実行するため)。この設計では、これを処理する明確な方法はありません。

プログラムの大規模な組織化、および/またはより基本的なOOPパーツを接着するためのより良いテクニックはありますか?ある場合、それは何ですか?そうでない場合、どのようにして前述の問題?

6
john01dav

マネージャークラスは間違った抽象化から生まれるので、それらに基づくソリューションはうまく適合せず、特にOOの概念で)あらゆる種類の問題を引き起こします。

今週私が遭遇した一例を挙げさせてください。 Androidアプリケーションで「サブスクリプション」処理を記述していました。今では「通常」、SubscriptionManagerが表示され、Subscriptionが表示されます。これには、有料かどうかに関係なく、おそらく製品IDが含まれます。それは、サブスクリプションが何であるかを一般的に抽象化したものです(一部は主張するでしょう)。

しかし、私が書いたのは、単一のメソッドContent retrieveContent()を含む(おおまかに)単一のSubscriptionクラスだけでした。

「サブスクリプション」とは基本的にanyアプリケーションに一般化する代わりに、myアプリケーションでのサブスクリプションである独自のSubscriptionを作成しました。私だけのために、すべてのアプリケーションのサブスクリプション管理を解決する必要はありませんでした。これは、ビジネスコンセプトmySubscriptionの一部である可能性があることも意味します。そして私のドメインでは、サブスクリプションの唯一の目的はいくつかのコンテンツにアクセスすることです。

これは実際にはすべてのオブジェクトに当てはまります。 (アプリケーション内の)すべてのオブジェクトには、ある程度のビジネスロジックが含まれている必要があります。他のすべてのオブジェクト(StringMatrixWindowなど)は、アプリケーションとはまったく関係なく、できればライブラリから取得する必要があります。

最後に、一緒に「接着」する必要のあるものはありません。むしろ、物事は互いに自然に含まれるか、ビジネスロジックのラインに沿って互いに参照します(SubscriptionContentを取得しますが、代わりにContentManager.retriveByProductId()またはいくつかのような)。

注意:ほとんどのプロジェクト(過去20年間に見たもの)は「マネージャー」を実行するだけであり、ほとんどが手続き型の分解で満足しているので、OOとインラインで何かをしようとしている潮流に逆らって泳いでいるでしょう。

1