web-dev-qa-db-ja.com

情報隠蔽と静的型安全性

私は他の人と一緒にプロジェクトに取り組んでおり、情報の非表示と静的な型の安全性について話し合っています。以下にシナリオを示します。

言語:C++ 11

シナリオ:ツリーのような構造を作成します。各ツリーノードには独自のクラスがあり、すべて基本クラスNodeTypeのサブタイプです。各サブタイプには、他のノードをリンクする独自のルールがあります。たとえば、NodeTypeANodeTypeBNodeTypeCNodeTypeのサブクラスであり、

  • NodeTypeAは、最初の子としてNodeTypeB、2番目の子としてのみNodeTypeAを持つことができます。
  • NodeTypeBは、その子孫としてNodeTypeBのみを持つことができます。 (それは任意の数の子を持つことができます)
  • NodeTypeCは、最初の子としてNodeTypeA、2番目の子としてのみNodeTypeBを持つことができます。

例には再帰の問題があるかもしれませんが、説明のためには問題ありません。

これで、各ノードを作成するFactoryクラスがあります。

  • createNodeTypeA
  • createNodeTypeB
  • createNodeTypeC

ユーザーテキスト入力をツリーに変換したいBuilderクラスがあります。

Builderは各ノードのタイプに関する情報を知らない必要はありません。 Factoryから取得したポインタを保持し、それらを別のFactoryのメソッドに渡します。 Builderの観点では、すべてのノードのタイプを基本クラスNodeTypeにすることができます。

ジレンマ

さらにInformation Hidingを重視する場合、ファクトリのメソッドは次のようになります。

  • NodeType *createNodeTypeA(NodeType *first, NodeType *second)
  • NodeType *createNodeTypeB(std::vector<NodeType *> children)
  • NodeType *createNodeTypeC(NodeType *first, NodeType *second)

正確さを提供するために、ファクトリーメソッドの各パラメーターの実行時チェックを提供する必要があります。

Static Type Safetyをさらに重視する場合、ファクトリのメソッドは次のようになります。

  • NodeTypeA *createNodeTypeA(NodeTypeB *first, NodeTypeA *second)
  • NodeTypeB *createNodeTypeB(std::vector<NodeTypeB *> childron)
  • NodeTypeC *createNodeTypeC(NodeTypeA *first, NodeTypeB *second)

およびBuilderは、各ノードのサブタイプを知っている必要があります。私たちが得るものは型安全です。

次の図は、2つの方法を示しています。

Information Hiding v.s. Static Type Safety

ディスカッション

好意情報非表示

  • 情報自体を隠すことは重要です。 Builderの開発者は、ノードのサブタイプの知識がなくても作業できます。
  • コードレビュー、テスト、自動分析ツールにより、静的型安全性の損失を補うことができます。資格のあるチームはそれらをかなりうまく完了することが期待されています。

優先静的型安全性

質問

どれが良い習慣かもしれませんか?私たちは、長期的な影響をより重視します。

7
Leo Jacob
NodeType *createNodeTypeA(NodeType *first, NodeType *second)
NodeType *createNodeTypeB(std::vector<NodeType *> children)
NodeType *createNodeTypeC(NodeType *first, NodeType *second)

これは「情報を非表示にする」ことはなく、Builderはどのファクトリを呼び出すか、どのファクトリに渡す必要があるかを認識しています。したがって、動的タイプを使用しても、この場合は何のメリットもないようです

Builderを実際にノードタイプのないものにするために何ができるか、単一のファクトリを使用できます。

NodeType *createNode(std::vector<NodeType*> children)

ただし、これではノードタイプを指定できません。これはしばしば構築の場合に当てはまり、一般に疑問を投げかけます。原則として、ビルダーからノードタイプ情報を削除することは可能ですか?おそらく、デカップリングは、同調/先取りの側にあるべきです。

4
max630

情報非表示を優先:

  • 情報自体を隠すことは重要です。 Builderの開発者は、ノードのサブタイプの知識がなくても作業できます。

ファクトリメソッドに渡される引数は特定の(ランタイム)タイプである必要があるため、これは正しくありません。したがって、プログラムは、コンパイル時にチェックされるか実行時にチェックされるかに関係なく、ツリーを構築するときにサブタイプを認識する必要があります。

  • コードレビュー、テスト、自動分析ツールにより、静的型安全性の損失を補うことができます。資格のあるチームは、それらをかなりうまく完了することが期待されています。

または、コンパイラーに処理を任せることで、多くの作業を節約できます。

本当の問題は、あなたのインターフェースがうまく設計されていないことです。異なるサブタイプは相互の知識を必要とするため、それらは独立しておらず、互換性もありません。設計が実際に提供する拡張性と実際に必要な拡張性を自問してください(近い将来現実的に発生する可能性のあるユースケースが思いつかなければ、それは必要ありません)。

この設計の失敗の原因は、ツリーデータ構造を煩わしく表現していること、つまりノード間の関係をノード自体のデータとして保存していることにあると思います。あなたが言及するサブタイプの唯一の違いは、他のノードとの関係が異なることです。したがって、これらの関係をノードの表現から分離すると、ノードをサブタイプ化する必要がなくなる、または少なくとも、より優れた抽象化を提供できる場合があります。

このような変更の難しさは、ノード間の関係を使用する機能をグラフ(検索)アルゴリズムとして再構成する必要があることです。多くの場合、これには考え方の変更が必要です。 ブーストグラフライブラリ を使用すると、これに役立ち、ツリー構造、ノード/エッジデータ表現、およびアルゴリズムを簡単に分離できるようにデータ構造とアルゴリズムが提供されます。

4
D Drmmr

それは不完全な比較です

「情報の隠蔽」と「静的データ型付け」は対立する概念ではなく、相互に排他的でも絶対的でも相対的でもありません。

これは @ DDmmr回答が触れる-設計 である本当の問題から遠ざかる誤った二分法です。次に、関連するコメントが重要な手がかりを示します。 ツリー構造は、単純なスクリプトの抽象構文ツリー(AST)です


デザインに「簡単なスクリプト」の文法がありません

ユーザーのテキスト入力をツリーに変換したいビルダーBuilderがあります。

この設計ステートメントは、構文解析をOPの質問に直接つながるオブジェクトの構築と統合することをお勧めします。解析には、「単純なスクリプト」の定義された構造が必要です。この定義は文法と呼ばれます。

文法は、「単純なスクリプト」の基本的なビット、許容される組み合わせ、および組み合わせの組み合わせなどを記述します。必然的に特定の(複雑な)ビットは、NodesおよびNodeサブタイプ(および関連する可能性のあるプロパティ))として直接マッピングされます。

重要なのは、基本的なものから最も複雑なものまで、これらすべての異なるビットに名前があります。 「番号」、「キーワード」、「数字」、「文字」、「演算子」など.


懸念の分離

AST "名前付きノード"はメタデータであり、まだ構築されていない具体的なNodeオブジェクトの抽象化です。このメタデータは、AST Nodeツリーの実装。

その場合、デザインは3つの一般的な部分になります。定義された文法、構文解析、具体的なNodeオブジェクト構築です。


ノード実装設計

解析の出力はASTです。しかし、最終的なNodeツリーはASTから派生したものであり、ASTではありません。しかし、私は確かに混乱を見ることができます。これは人工的な抽象概念のように感じますが、重要な設計ポイントです。

今ビルダーはASTを取ります。 Nodeリレーションルールは既にASTの構造に組み込まれていることに注意してください。したがって、ビルダーは、AST構造のみに基づいてNodeツリーを構築しますが、これらのルールの明確な知識は必要ありません。

「単純なスクリプト」は本格的な汎用スクリプト言語ではないので、解析は本当に簡単ですが、これが単一責任a.k.a.懸念の分離に違反することを許してはいけないと思います。

おそらくASTをスクリプトの基本粒子の要素/ノードに完全に解析する必要はありません。多分NodeMetaDataオブジェクトがあるとしましょうAST Nodeスーパークラスのプロパティに対応します。


OP quandaryは設計されています

「情報の非表示」は、懸念事項の分離と慎重に設計されたBuilderを考えると、本質的に適切です。静的型パラメーターは、明確に定義されたメタデータを持つASTオブジェクトに道を譲ります。

Builderとファクトリーは、Nodeもサブタイプオブジェクトも引数として取りません。 BuilderはAST=をステップスルーし、ファクトリーはASTメタデータに基づいてインスタンス化および呼び出されます。ビルダーはNodeTreeを工場で作成されたNodeTreeパーツから組み立てます。

ノードツリーの構築は潜在的に複雑なので、「ビルダー」や「ファクトリー」などの構築設計パターンを調べます。ファクトリパターンは複雑さによって異なることに注意してください。

1
radarbob

私はもう少しコンテキストが高く評価されていたと思います:)。

あなたのジレンマを言い換えさせてください:

  • できるだけ抽象的な方法でツリーを表示できるようにしたいと考えています。 「ツリーは、一般的なツリーでできることは何でもできる親(ルートノードを除く)を1つだけ持つノードの空ではないコレクションです」と言うことができます。
  • Treeの無効性をすばやくチェックできるようにしたい。

ツリーを構築するアルゴリズムは、さまざまなノードタイプを認識し、有効なツリーを構築する必要があります。アルゴリズムがノードのストリームを提供されている場合、チェックした後、どのノードがどこに行くべきかをアルゴリズムが通知できるはずです。無効な入力を検出したり、無効なツリーを作成するよう強制されたりした場合は、単に停止してエラーを出力します。

したがって、TreeNodeTypefindChildrengetRootなどのgetSizeをパラメーターとして取るgetDepthを含むonlyメソッドを定義します。メソッドParserを返すbuildTree(algorithm)を呼び出すと、 NodeType(これはツリーと同等です!!!)。 NodeTypeANodeTypeBなどの列挙型を含むことができるNodeTypeCのペイロードを調べることにより、ノードがNodeTypeTypeA、およびTypeBであるかどうかを判断します。ノードタイプを区別するすべてのロジックはParserにあります。

結論として、さまざまなノードタイプを認識するParser(またはアルゴリズム)と呼ばれるものを使用して、非常に一般的なツリーの処理を実行できるTree抽象化から懸念を分離しました。

1
Harsh Verma

情報の非表示は外部的なメリットです。クラスAが与えられた場合、Aは情報を非表示にするメリットがありません。それはクラスがその仕事をするのを助けません、そしてそれはコードについての開発者の理由を助けません。役に立たない知識にさらされる必要のない外部の開発者を支援します。情報を非表示にすることは、クラスが他のクラスに対して行う好意です。

静的型チェックは、クラスとクラス開発者にとってメリットがあります。検証を少なくする必要があるため、クラスにメリットがあります。クラスAとメソッドFooを指定します。これは整数を受け取り、10進数を返します。メソッドFooは、整数nullか配列か何かを確認する必要はありません。その確認は、コンパイル時または実行時にそのメソッド内の他の場所で行われ、整数になります。 Fooがプライベートの場合、10進数を返します。これは開発者にとってのメリットです。

さて、あなたが本当にこのトレードオフをする必要があるかどうかは言えませんが、情報の隠蔽は省略できます。ネガティブは外部的なものであり、直接的な問題ではありません。

コメントに応じて更新する:並べ替えアルゴリズムの手続き型実装があり、いくつかのグローバル変数といくつかの関数を使用して中間結果を返すとします。非常に堅実で、高速で、効率的で、大きなOです。オブジェクト指向設計を使用して書き換えるのに最適なターゲットではありませんが、残りのアプリケーションを実行します。これをクラスでラップするだけでも、高速で効率的でバグのない堅固な実装です。グローバル(現在はクラスレベルのフィールドまたはプロパティ)とプライベートメソッドを非表示にしても、高速化したり、クラス内のバグを排除したりすることはできません。これは、安全に使用するために内部でどのように動作するかの詳細を他の誰かが学ぶ必要がないようにします。架空の並べ替えクラスのメリットはゼロであり、アプリケーションにとっては大きな勝利です。

0
jmoreno