19

オブジェクト指向設計 (OOD) は、データとそのメソッドを組み合わせます。私が見る限り、これは 2 つの素晴らしいことを達成します: カプセル化 (データが何であるかは気にせず、必要な値を取得する方法のみを気にしません) とセマンティクス (データを名前と関連付け、そのメソッドは一貫して当初の意図どおりにデータを使用します)。

では、OOD の強みはどこにあるのでしょうか。対照的に、関数型プログラミングでは、豊かさを名詞ではなく動詞に帰するため、カプセル化とセマンティクスの両方がデータ構造ではなくメソッドによって提供されます。

私はスペクトルの機能の端にあるシステムで作業しており、OO のセマンティクスとカプセル化を絶えず切望しています。しかし、OO のカプセル化が、オブジェクトの柔軟な拡張に対する障壁になる可能性があることがわかります。したがって、現時点では、セマンティクスがより大きな強みであると考えています。

それとも、カプセル化はすべての価値のあるコードの鍵ですか?

編集: 具体的には、OO がここで提供するカプセル化のタイプを意味します。changeColor(door,blue)になりdoor.changeColor(blue)ます。

4

11 に答える 11

29

「カプセル化」のかなり狭い定義を使用しているようです。あなたがカプセル化を「データとメソッドを結合すること」と定義していると推測するのは正しいでしょうか?</p>

私が間違っている場合は、この投稿の残りの部分を無視してください。

カプセル化はあいまいな用語ではありません。実際、これは国際標準化機構によって定義されています。ISO の Open Distributed Processing の参照モデル - 次の 5 つの概念を定義します。

エンティティ: 興味のある具体的または抽象的なもの。

オブジェクト: エンティティのモデル。オブジェクトは、その動作によって特徴付けられ、二重に、その状態によって特徴付けられます。

動作 (オブジェクトの): 発生する可能性がある一連の制約を伴うアクションのコレクション。

インターフェース: オブジェクトの相互作用のサブセットと、それらがいつ発生するかに関する一連の制約から構成される、オブジェクトの動作の抽象化。

カプセル化: オブジェクトに含まれる情報は、オブジェクトがサポートするインターフェースでの相互作用を通じてのみアクセスできるという特性。

さらに、自明の提案を行うことができます。一部の情報はこれらのインターフェイスを介してアクセスできるため、一部の情報はオブジェクト内で非表示にしてアクセスできないようにする必要があります。そのような情報が示す特性は情報隠蔽と呼ばれ、モジュールは難しい決定と変更される可能性のある決定の両方を隠すように設計されるべきであると主張することで Parnas が定義した、優れたコンピューティング論文の 1 つを参照してください。

http://www.cs.umd.edu/class/spring2003/cmsc838p/Design/criteria.pdf

情報が隠されているのはデータだけではないことに注意することが重要です。変更が難しい、または変更される可能性が高いのは、オブジェクトに関連付けられた動作のサブセットです。

あなたの投稿では、オブジェクト指向と関数型プログラミングのカプセル化の違いはデータ管理に起因すると言っているようですが、少なくとも ISO と Parnas によると、データ管理はカプセル化の鍵ではありません。したがって、関数型プログラミングのカプセル化がOOのカプセル化と異なる必要がある理由がわかりません。

さらに、関数型プログラミングはカプセル化を提供すると投稿で述べていますが、「…データ構造ではなくメソッドによって」です。これは、絶対的なものではなく、規模の違いだと思います。「データ構造」ではなく「オブジェクト」という言葉を使用する場合 (誤解があれば教えてください)、OO のオブジェクトによるカプセル化と関数型プログラミングのメソッドによるカプセル化に重要性を見出しているようです。

しかし、上記の ISO の定義によれば、オブジェクトとは私がモデル化したいものすべてです。したがって、それらのクラスの一部がパッケージのインターフェース (つまり、パッケージのパブリック クラス) に寄与し、一部が情報隠蔽 (パッケージ内のプライベート クラス) である限り、クラスはパッケージ内にカプセル化できます。

同様に、メソッドはクラス内にカプセル化されます。一部のメソッドはパブリックであり、一部のメソッドはプライベートです。これを一段低くして、McCabian コードのシーケンシャル シーケンスがメソッド内にカプセル化されていると言うことができます。それぞれが、カプセル化された領域内にカプセル化されたノードのグラフを形成します。そして、これらすべてのグラフがグラフ スタックを形成します。したがって、関数型プログラミングは関数/ファイル レベルでうまくカプセル化されている可能性がありますが、これは OO のメソッド/クラス グラフと何ら変わりはなく、OO のクラス/パッケージ グラフとも本質的に違いはありません。

また、Parnas が上で使用している単語に注意してください: 変更。情報隠蔽は、将来の困難な設計上の決定の変更など、潜在的なイベントに関係しています。OO の強みはどこにあるのかと尋ねます。確かにカプセル化は OO の強みですが、「カプセル化の強みはどこにあるのか」という疑問が生じます。その答えは、非常に明快なものの 1 つです。それは、チェンジ マネジメントです。特に、カプセル化により、変更の最大の潜在的な負担が軽減されます。

ここでは、「潜在的な結合」の概念が役立ちます。

「カップリング」自体は、「あるモジュールから別のモジュールへの接続によって確立される関連付けの強さの尺度」として定義されています。

http://www.research.ibm.com/journal/sj/382/stevens.pdf

また、論文が言うように、「モジュール間の接続を最小限に抑えることで、変更やエラーがシステムの他の部分に伝播する経路も最小限に抑えられるため、ある部分の変更が原因である悲惨な「リップル」効果が排除されます。別の場所でエラーが発生し、他の場所で追加の変更が必要になり、新しいエラーが発生するなど。」</p>

ただし、ここで定義されているように、簡単に解除できる制限が 2 つあります。まず、結合はモジュール内接続を測定しません。これらのモジュール内接続は、モジュール間接続と同じくらい多くの「リップル」効果を引き起こす可能性がありますただし、これは結合が定義された要素間の接続 (つまり、ラベルまたはアドレスへの参照) に関して定義されていません)。第二に、モジュールが接続されている、または; 結合の定義には、Parnas が話す潜在的な変化を管理する余地はほとんどありません。

これらの問題は両方とも、潜在的な結合 (プログラムのすべての要素間で形成可能な接続の最大数) の概念によって、ある程度解決されます。たとえば、Java では、パッケージ内のパッケージ プライベート (デフォルトのアクセサー) であるクラスは、その上で接続を形成できません (つまり、リフレクションにもかかわらず、外部のクラスはそれに依存できません)。それに依存関係があります。この public クラスは、現時点で他のクラスが依存していなくても、潜在的な結合に寄与します。将来、設計が変更されたときにクラスが依存する可能性があります。

カプセル化の強さを確認するには、負担の原則を検討してください。負担の原則には 2 つの形式があります。

ストロング フォームでは、エンティティのコレクションを変換する負荷は、変換されたエンティティの数の関数であると述べています。弱形式は、エンティティのコレクションを変換する際の潜在的な最大負荷は、変換されるエンティティの最大潜在数の関数であると述べています。

ソフトウェア システムを作成または変更する負担は、作成または変更されたクラスの数の関数です (ここでは、オブジェクト指向システムを想定して「クラス」を使用し、クラス/パッケージ レベルでのカプセル化に関係しています。関数型プログラミングの関数/ファイルレベルに関心がありました)。(「負担」とは、通常、最新のソフトウェア開発のコスト、時間、またはその両方であることに注意してください。) 特定の変更されたクラスに依存するクラスは、変更されたクラスに依存しないクラスよりも影響を受ける可能性が高くなります。 .

変更されたクラスが課す可能性のある最大の負荷は、それに依存するすべてのクラスに影響を与えることです。

したがって、変更されたクラスへの依存関係を減らすと、その更新が他のクラスに影響を与える可能性が減り、そのクラスが課す可能性のある最大の負担が減ります。(これは、「構造化された設計」論文の再記述にすぎません。)

したがって、システム内のすべてのクラス間の潜在的な依存関係の最大数を減らすと、特定のクラスへの影響が他のクラスへの更新を引き起こす可能性が減少し、したがってすべての更新の潜在的な最大負荷が減少します。

したがって、カプセル化は、すべてのクラス間の潜在的な依存関係の最大数を減らすことにより、負担の原則の弱い形式を緩和します。これはすべて、プログラムを構造化する論理的手段として潜在的な結合を使用して、そのような主張を数学的に証明しようとする「カプセル化理論」によってカバーされています。

ただし、「カプセル化はすべての価値のあるコードの鍵ですか?」と尋ねる場合は注意してください。答えは間違いなく「いいえ」です。価値のあるすべてのコードの鍵は 1 つではありません。カプセル化は、特定の状況では、コードの品質を向上させて「価値のある」ものにするツールにすぎません。</p>

また、「…カプセル化は、オブジェクトの柔軟な拡張に対する障壁になる可能性があります」とも書いています。はい、確かに可能です。実際、変更が困難または変更される可能性のあるオブジェクトの設計上の決定を拡張することに対する障壁になるように設計されています。ただし、これは悪いことだとは考えられていません。別のアプローチは、すべてのクラスを公開し、プログラムに最大の潜在的な結合を表現させることです。しかし、負担の原則の弱い形は、更新がますます費用がかかるようになると述べています。これらは、拡張に対する障壁を測定するためのコストです。

最後に、カプセル化とセマンティクスの興味深い比較を行います。あなたの意見では、オブジェクト指向のセマンティクスがその大きな強みです。私もセマンティストではありません (善良なラムゼイ氏が彼のコメントで言及するまでは、そのような言葉が存在することさえ知りませんでした) が、あなたが意味するのは「セマンティクス」の意味であると推測します。単語の意味の解釈」であり、非常に基本的には、woof() メソッドを持つクラスは Dog と呼ばれるべきです。

このセマンティクスには確かに大きな強みがあります。

私が興味を持っているのは、セマンティクスをカプセル化と比較して勝者を探すことです。私はあなたがそれを見つけるとは思えません。

私の意見では、カプセル化の動機には 2 つの力があります。セマンティックと論理です。

セマンティック カプセル化とは、カプセル化されたノード (一般的な用語を使用する場合) の意味に基づくカプセル化を意味します。したがって、「アニマル」と「ミネラル」という名前の 2 つのパッケージがあることを伝え、Dog、Cat、および Goat の 3 つのクラスを指定し、これらのクラスをどのパッケージにカプセル化するかを尋ねると、次のようになります。システムのセマンティクスは、3 つのクラスが「鉱物」ではなく「動物」パッケージ内にカプセル化されていることを示していると主張するのは完全に正しいでしょう。

ただし、カプセル化のもう 1 つの動機はロジックであり、特に前述の潜在的な結合の研究です。カプセル化理論は、実際には、潜在的な結合を最小限に抑えるために、いくつかのクラスをカプセル化するために使用する必要があるパッケージの数の方程式を提供します。

私にとって、カプセル化は全体として、このセマンティック アプローチと論理的アプローチの間のトレードオフです。これにより、プログラムがセマンティックに理解しやすくなる場合は、プログラムの潜在的な結合が最小値を超えることを許可します。しかし、膨大で無駄なレベルの潜在的な結合は、それが意味的にどれほど明白であっても、プログラムを再構築する必要があるという警告になります。

(そして、良きミスター・ラムジーがまだ読んでいるなら、あなたまたはあなたのセマンティストの友人が、私がここで使用している「セマンティクス」フェーズについて、より適切な言葉を教えてくれませんか? より適切な用語を使用した方がよいでしょう。)

よろしく、エド。

于 2009-03-23T09:34:55.157 に答える
9

カプセル化とそれによる抽象化は、明らかに OO の主な強みです。「もの」は、それらに対してどのような「アクション」を呼び出すことができるかを述語するため、名詞は動詞よりも意味上の重要性が高くなります。

最終的に、ある程度のカプセル化なしに、複雑なシステムを一貫性のある保守可能な形式で設計することを想像するのは困難です。

于 2009-03-21T09:44:32.560 に答える
5

オブジェクトシステムがおそらくこれらのどちらも提供しない Lisp プログラマーとして、私はこう言います: 上記のどれもありません。

jwz: "疑似 Smalltalk オブジェクト モデルが負け、汎用関数 (no-external-overrides ルールによって適切に制約されている) が勝つ".

あなたや他の人がここにリストした望ましい属性 (カプセル化、モジュール性など) は、あなたが考えるほど OO に固有のものではないと思います。多くの場合、Java スタイルの OO と一緒に提供されますが、純粋にその結果ではありません。

于 2009-03-22T18:56:33.520 に答える
4

何らかの形のモジュール性は、スケーラブルな設計の鍵です。人間には限界があるため、一度に多くの情報を「把握」することはできません。そのため、大規模なプロジェクトを理解するための基礎を提供し、作業の割り当てを細分化する方法を提供するために、問題を管理しやすくまとまりのあるチャンクに細分化する必要があります。多くの人々の間で大きなプロジェクトの。

上記の目標を達成するために、大規模プロジェクトの最も効果的な「分割」/「分割」を選択する方法は? 経験上、ここでは OO が大きな勝者であることが示されています。多くの人が、OO がこれを得意とする 2 つの重要な属性であることに同意すると思います。

  • Encapsulated : 各クラスは、「秘密」(その実装に関して時間の経過とともに変化する可能性が高い実装固有の仮定のセット) をカプセル化し、これらの仮定にとらわれないインターフェイスを公開します。このようなカプセル化された抽象化を階層化することで、予想される変更に直面してコンポーネント/実装を簡単に交換できる堅牢な設計を構築できます。
  • 名詞中心: ほとんどのドメインでは、人間は、最初にドメインの名詞/データについて考えてドメイン モデルを分解し、次に各名詞に関連付けられている助動詞を特定する方が優れているようです。

関数型プログラミング (FP) と OO に関して、私はこれについてブログに書いたことがありますが、簡単に言えば、FP は実装技術に関するものであり、OO はプログラムの構造と設計に関するものであり、したがって 2 つは補完的であり、OO がより支配的であると思います。スケールの「大」エンドと「小」エンドで FP がより支配的です。つまり、大規模なプロジェクトでは、高レベルの構造は OO クラスの設計によって最もよく記述されますが、モジュール レベルの詳細 (実装、およびモジュールのインターフェイスの形状の詳細) の多くは FP によって最もよく形成されます。影響します。

于 2009-03-22T16:22:41.050 に答える
4

複雑さを分離することは、あらゆる設計の主な目標である IMOです。つまり、機能自体よりも使いやすいインターフェースの背後に機能をカプセル化することです。

オブジェクト指向はそのためのさまざまなメカニズムを提供します-2つのoyu言及:

カプセル化により、実際の実装とは独立したカスタム サーフェスを設計できます。(言い換えれば、「シンプルは違うことを意味する」)。

セマンティクスにより、問題ドメインの要素を表すエンティティをモデル化できるため、理解しやすくなります。


プロジェクトが一定の規模に達すると、複雑さを管理する必要があります。何年にもわたって、プログラミングは、私たちが管理することを学んだ複雑さの限界に沿ってすくい取ってきたという主張に賭けたいと思います。

私は何年もの間関数型プログラミングに手を出していませんでしたが、私の理解では、数学者のパワフル、エレガント、そして美しいという言葉の意味によって最もよく説明できます。この文脈では、「美しい」と「エレガント」は、複雑なシステムの真の、または関連する構造への素晴らしい洞察を説明しようとし、驚くほど単純な視点からそれを見ていきます. 複雑さを与えられたものとして受け入れ、それをナビゲートしようとします。

あなたが言及した柔軟性は、私の理解では、必要に応じて POV を変更できるということですが、それはカプセル化に反します。

OO、OTOH は還元主義者のアプローチです。より高いレベルに行くことで POV を変更します。「古い OO」では、POV の単一の階層があり、インターフェースは (このモデルでは) 異なる POV をモデル化する方法です。

あえて言えば、OOの強みは「普通の人」に向いているところです。

于 2009-03-21T11:19:17.653 に答える
3

オブジェクト指向設計の強みは、設計で発生する遅延バインディングの量に比例します。これは OO の Kay の概念であり、Nygaard の概念ではありません。アラン・ケイは次のように書いています。

私にとって OOP とは、メッセージング、ローカルでの保持と保護、状態プロセスの隠蔽、およびすべてのものの極端な遅延バインディングのみを意味します。これは、Smalltalk と LISP で実行できます。これが可能なシステムは他にもあるかもしれませんが、私は知りません。

文献の多くは、オブジェクト指向の C++ の考え方を支持して遅延バインディングを無視しています。

于 2009-04-01T02:20:17.310 に答える
2

一歩下がって、これをより高いレベルから見てみましょう。あらゆる言語機能の利点は、問題のドメインに関して、より自然な方法で問題/解決策を簡潔に表現できることにあります。

OOP の仕組みは、構造体と関数ポインターを使用してプレーン C で簡単に実装できます。そうすることで、少し OOP の感覚を得ることができます。ただし、OOP のイディオムは、そのような環境ではあまり役に立ちません。OOP の実際の言語サポートがある場合、パラダイムの表現力が明らかになり、言語がアイデアを実装する方法は、「言われる」ことと方法に非常に大きな影響を与えます。たとえば、lisp、python、ruby などでクロージャ/ラムダを使用するコードの違いを参照してください。

したがって、最終的には、コンポーネントとその根底にある概念についてではなく、それらがどのように組み合わされて使用され、C++ の OO がどのようなものになるかについてです。

于 2009-04-02T18:26:22.373 に答える
1

IMHO、OO は単に、オブジェクトが他のオブジェクトと相互作用することを意味します。カプセル化とは、単に概念を抽象化することを意味します。したがって、Socket と .Connect() を何かに作成します。それがどのように接続されるか、あなたは本当に気にしません (これは基本的にカプセル化の私の定義です)。

そして、純粋な関数型プログラミングはオブジェクトを使用して通信できます..しかし、それらのオブジェクトは不変である必要があります。したがって、やはり私見ですが、FP は OO の概念を簡単に使用できます。C などの命令型言語は引き続き OO の概念を使用できます。たとえば、使用してはならないプライベート セクションを含む各「クラス」のファイルです。

于 2009-04-06T03:58:25.733 に答える
0

OO の真の力は、カプセル化ではなくポリモーフィズムにあります。カプセル化はある程度達成可能であり、関数型言語で使用されますが、関数型言語で実装された場合、ポリモーフィズムは非常に厄介です。

( OO の威力を理解するには、ギャング オブ フォーによる「デザイン パターン」を読んでください。)

@Phil、あなたが正しく理解していれば、あなたが言及した違いは、プログラムがデータ/メソッドを呼び出す方法の間にあります.ooでは、最初にオブジェクト/インスタンスがあり、次にオブジェクトのデータ/メソッドがオブジェクトを介して呼び出されます; 関数型では、メソッドが直接呼び出されます。

しかし、関数型プログラムの実装を見ると、データとメソッドが (クラスではなくファイルに) ラップされていることがわかります。たとえば、C プログラムには、他のファイルからアクセスできる関数を宣言するヘッダー ファイルがあり、これらの宣言された関数を介してのみアクセスできる場合、データはプライベート データです。プログラマーが十分に注意している限り、OO のカプセル化のほとんどは関数型プログラムで実装できます。(特定のポインター トリックを使用することで、継承も利用できます。)

于 2010-05-29T00:43:04.520 に答える