9

機能を関数に入れることによって、それだけでカプセル化の例が構成されますか、それともオブジェクトを使用してカプセル化する必要がありますか?

カプセル化の概念を理解しようとしています。私が考えたのは、次のようなものから行く場合です:

n = n + 1

これは大量のコードの一部として実際に実行され、それを取得してこのような関数に入れ、その加算ロジックをメソッドにカプセル化しました。

addOne(n)
    n = n + 1
    return n

それとも、addOne の詳細を外の世界から隠している場合は、カプセル化のみであるというケースが多いのでしょうか?それがオブジェクト メソッドであり、private/protected のアクセス修飾子を使用している場合のように?

4

12 に答える 12

18

私は、答えの傾向と思われるものに最初に反対します。はい、関数はある程度の実装をカプセル化します。オブジェクトは必要ありません(クラスを意味するために使用すると思います)。

マイヤーズも参照してください。

于 2009-02-10T20:07:35.227 に答える
12

おそらく、抽象化とカプセル化を混同しているのかもしれません。カプセル化は、オブジェクト指向のより広い文脈で理解されています。

カプセル化には、次の3つすべてが適切に含まれます。

  • 抽象化
  • 実装の非表示
  • 責任の分割

抽象化は、カプセル化の1つのコンポーネントにすぎません。あなたの例では、それがかつて存在していたコードの本体から追加機能を抽象化しました。これを行うには、コード内のいくつかの共通点を識別します。つまり、特定のケースに対する概念(加算)を認識します(変数nに1を加算します)。この機能により、抽象化により、カプセル化されたコンポーネント(メソッドまたはオブジェクト)が再利用可能になります。

カプセル化の概念にとって同様に重要なのは、実装を非表示にするという考え方です。これが、カプセル化がオブジェクト指向の分野で議論されている理由です。実装の非表示は、オブジェクトをそのユーザーから保護し、その逆も同様です。OOでは、オブジェクトの実装がプライベートメソッド内で行われている間に、オブジェクトのユーザーにパブリックメソッドのインターフェイスを提示することでこれを行います。

これには2つの利点があります。まず、オブジェクトへのアクセスを制限することにより、オブジェクトのユーザーがオブジェクトを無効な状態のままにしておく可能性がある状況を回避します。第2に、ユーザーの観点からすると、ユーザーがオブジェクトを使用する場合、オブジェクトとの結合は緩くなります。後で実装を変更しても、影響はありません。

最後に、責任の分割(オブジェクト指向設計のより広い文脈で)は、カプセル化に適切に対処するために考慮しなければならないものです。関数のランダムなコレクションをカプセル化することは無駄です。責任は、重複やあいまいさをできるだけ少なくするために、明確かつ論理的に定義する必要があります。たとえば、Toiletオブジェクトがある場合、Kitchenオブジェクトからその責任のドメインを遮断する必要があります。

ただし、限られた意味では、関数が、たとえば、関数を抽象化することによって一部の機能を「モジュール化」するというのは正しいことです。しかし、私が言ったように、用語としての「カプセル化」は、オブジェクト指向のより広い文脈で理解され、上記の3つの基準を満たすモジュール化の形式に適用されます。

于 2009-02-10T22:03:26.647 に答える
3

方法は、車が良い運転の例であるのと同じように、カプセル化の例ではありません。カプセル化はシナクスに関するものではなく、論理的な設計上の問題です。オブジェクトとメソッドの両方が、良いカプセル化と悪いカプセル化を示す可能性があります。

それについて考える最も簡単な方法は、コードが実装について知る/気にする必要のないコードの他の部分から詳細を隠す/抽象化するかどうかです。

車の例に戻る:オートマチックトランスミッションは優れたカプセル化を提供します:ドライバーとして、あなたは前進/後退と速度を気にします。マニュアルトランスミッションはカプセル化が悪い:ドライバーの観点から、低速/高速に必要な特定のギアは、一般的にドライバーの意図とは無関係です。

于 2009-02-10T20:08:26.440 に答える
3

いいえ、カプセル化にオブジェクトは必要ありません。非常に広い意味では、「カプセル化」は単に「詳細を見えなくする」ことを意味し、その点で、メソッドはその実装の詳細をカプセル化しています。

ただし、コードをメソッドに分割したからといって、コードが適切に設計されていると言えるわけではありません。500 個のパブリック メソッドで構成されるプログラムは、1 つの 1000 行のメソッドで実装された同じプログラムよりも優れているわけではありません。

プログラムの構築では、オブジェクト指向の手法を使用しているかどうかに関係なく、さまざまな場所でカプセル化について考える必要があります: メソッドの実装の詳細を隠したり、それについて知る必要のないコードからデータを隠したりします。 、モジュールへのインターフェースの簡素化など。

更新:更新された質問に答えるために、「コードをメソッドに入れる」と「アクセス修飾子を使用する」の両方がロジックをカプセル化する異なる方法ですが、それぞれが異なるレベルで動作します。

コードをメソッドに入れると、そのメソッドを構成するコードの個々の行が非表示になるため、呼び出し元はそれらの行が何であるかを気にする必要がなくなります。メソッドの署名だけを気にします。

クラスのメソッドに(たとえば)「プライベート」のフラグを立てると、そのメソッドが非表示になるため、クラスの消費者はそれについて心配する必要がありません。クラスの public メソッド (またはプロパティ) だけを気にします。

于 2009-02-10T20:16:16.327 に答える
2

カプセル化の抽象的な概念は、実装の詳細を非表示にすることを意味します。オブジェクト指向は、ecnapsulationの使用の一例にすぎません。もう1つの例は、実装モジュールと定義モジュールを使用する(または使用する)module-2と呼ばれる言語です。定義モジュールは実際の実装を隠し、したがってカプセル化を提供しました。

カプセル化は、何かをブラックボックスと見なすことができる場合に使用されます。オブジェクトはブラックボックスです。あなたはそれらが提供する方法を知っていますが、それらがどのように実装されているかは知りません。

[編集]更新された質問の例については、カプセル化をどの程度狭くまたは広く定義するかによって異なります。あなたのAddOneの例は、私が信じていることを何も隠していません。変数が配列インデックスであり、メソッドmoveNextを呼び出し、別の関数setValueとgetValueを持っている場合は、情報の隠蔽/カプセル化になります。これにより、人々は(おそらく他のいくつかの関数と一緒に)あなたの構造をナビゲートし、配列を使用していることを認識して変数を設定および取得することができます。プログラミング言語が他のまたはより豊富な概念をサポートする場合は、意味とインターフェイスを変更して、moveNext、setValue、およびgetValueの実装を変更できます。私にとってそれはカプセル化です。

于 2009-02-10T20:04:54.903 に答える
1

それはコンポーネントレベルのものです

これをチェックしてください:

コンピュータサイエンスでは、カプセル化とは、ソフトウェアコンポーネントの内部メカニズムとデータ構造を、定義されたインターフェイスの背後に隠すことです。これにより、コンポーネント(他のソフトウェア)のユーザーは、コンポーネントの機能を知るだけで済み、できません。それがどのように行われるかの詳細に依存するようにします。目的は、変更の可能性を実現することです。コンポーネントの内部メカニズムは、他のコンポーネントに影響を与えることなく改善できます。または、コンポーネントを、同じパブリックインターフェイスをサポートする別のコンポーネントに置き換えることができます。

(私はあなたの質問を完全には理解していません、そのリンクがあなたの疑問をカバーしていないかどうか私に知らせてください)

于 2009-02-10T20:01:36.393 に答える
1

例えを使ってこれをいくらか単純化してみましょう。車のキーを回すと起動します。キーだけではありませんが、そこでが起こっているのかを知る必要はありません。あなたにとって、キーターン=モータースタート。キーのインターフェース(つまり、関数呼び出し)は、エンジンを回転させるスターターモーターの実装などを非表示にします...(実装)。それがカプセル化です。内部で何が起こっているのかを知る必要がなくなり、満足しています。

たとえば、キーを回すために人工の手を作成した場合、それはカプセル化ではありません。あなたは何も隠さずに追加の仲買人のがらくたでキーを回しています。それはあなたの例が私に思い出させるものです-両方とも関数呼び出しによって達成されますが、それは実装の詳細をカプセル化していません。この例では、あなたのコードを手に取った人は誰もあなたに感謝しません。実際、彼らはあなたの人工的な手であなたをクラブする可能性が高くなります。

情報を非表示にするために考えられる任意のメソッド(クラス、関数、ダイナミックライブラリ、マクロ)をカプセル化に使用できます。

于 2009-02-10T22:07:56.090 に答える
1

カプセル化は、オブジェクトの属性 (データ メンバー) と動作 (メンバー関数) を組み合わせて 1 つのエンティティとしてクラスとして参照するプロセスです。

于 2009-10-12T06:02:03.717 に答える
0

International Organization for Standardization によって作成された Open Distributed Processing の参照モデルでは、次の概念が定義されています。

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

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

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

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

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

これらは非常に幅広いものです。ただし、関数内に機能を配置することが、これらの用語でカプセル化を構成すると論理的に見なすことができるかどうかを見てみましょう。

まず、関数は明らかに「関心のあるもの」のモデルであり、(おそらく) 実行したいアルゴリズムを表し、そのアルゴリズムは解決しようとしている問題に関連しています (したがって、そのモデルです)。 .

関数には動作がありますか? 確かにそうです。関数を実行する前にどこかから呼び出さなければならないという制約の下で実行されるアクションのコレクション (任意の数の実行可能なステートメントである可能性があります) が含まれています。関数は、因果関係なしにいつでも自発的に呼び出されることはありません。法律用語っぽい?もちろんです。それでも、進みましょう。

関数にはインターフェースがありますか? 確かにそうです: 名前と仮パラメーターのコレクションがあり、関数に含まれる実行可能ステートメントにマップされます。関数が呼び出されると、名前とパラメーターのリストが理解されて、実行可能のコレクションを一意に識別します。呼び出し側が実際のステートメントを指定せずに実行されるステートメント。

関数には、関数に含まれる情報が、オブジェクトによってサポートされているインターフェイスでの相互作用を介してのみアクセスできるというプロパティがありますか? うーん、まあ、それはできます。

一部の情報はそのインターフェイスを介してアクセスできるため、一部の情報は関数内で非表示にしてアクセスできないようにする必要があります。(このような情報が示す特性は情報隠蔽と呼ばれ、モジュールは難しい決定と変更される可能性のある決定の両方を隠蔽するように設計されるべきであると主張して Parnas が定義したものです。) では、どの情報が関数内に隠されているのでしょうか?

これを確認するには、まずスケールを考慮する必要があります。たとえば、Java クラスをパッケージ内にカプセル化できると主張するのは簡単です。一部のクラスは公開され (したがって、パッケージのインターフェースになります)、一部はパッケージ非公開になります (したがって、パッケージ内に情報が隠されます)。 . カプセル化理論では、クラスはノードを形成し、パッケージはカプセル化された領域を形成し、全体がカプセル化されたグラフを形成します。クラスとパッケージのグラフは、3 番目のグラフと呼ばれます。

関数 (またはメソッド) 自体がクラス内にカプセル化されていると主張するのも簡単です。繰り返しになりますが、一部の関数は公開され (したがって、クラスのインターフェイスの一部になります)、一部の関数は非公開になります (したがって、クラス内に情報が隠されます)。関数とクラスのグラフは、2 番目のグラフと呼ばれます。

次に、関数について説明します。関数自体がカプセル化の手段である場合、関数には、他の関数に公開されている情報と、関数内に隠されている情報が含まれている必要があります。この情報は何でしょうか?

McCabe から 1 人の候補者が提示されました。Thomas McCabe は、循環的複雑性に関する画期的な論文で、ソース コードについて次のように説明しています。「グラフの各ノードはプログラム内のコード ブロックに対応し、フローはシーケンシャルであり、アークはプログラム内の分岐に対応します。」

順次実行の McCabian ブロックを、関数内にカプセル化できる情報の単位として考えてみましょう。関数内の最初のブロックは常に最初で唯一の実行が保証されているブロックであるため、最初のブロックは他の関数によって呼び出される可能性があるという点で公開されていると見なすことができます。ただし、関数内の他のすべてのブロックは、他の関数から呼び出すことができないため (フローの途中で関数にジャンプできる言語を除く)、これらのブロックは関数内に情報が隠されていると見なされる場合があります。

これらの (おそらく少し曖昧な) 定義を使用すると、「はい」と言うことができます。関数内に機能を配置することは、カプセル化を構成します。関数内のブロックのカプセル化は、最初のグラフです。

ただし、注意点があります。すべてのクラスが public であるパッケージをカプセル化すると考えますか? 上記の定義によれば、パッケージへのインターフェイス (つまり、すべてのパブリック クラス) が実際にパッケージの動作のサブセットを他のパッケージに提供していると言えるので、テストに合格します。ただし、この場合のサブセットはパッケージ全体の動作であり、情報が隠されているクラスはありません。したがって、上記の定義を厳格に満たしているにもかかわらず、定義の精神を満たしていないと感じています。真のカプセル化を主張するには、何かが情報に隠されている必要があります。

あなたが与える例についても同じことが言えます。n = n + 1 は単一の McCabian ブロックであると見なすことができます。これは、それ (および return ステートメント) が単一の連続した実行フローであるためです。ただし、これを配置する関数にはブロックが 1 つしか含まれておらず、そのブロックが関数の唯一のパブリック ブロックであるため、提案された関数内に情報が隠されているブロックはありません。ですから、カプセル化の定義は満たしているかもしれませんが、精神を満たしていないと言えます。

もちろん、カプセル化のような利点を証明できない限り、これはすべて学術的なものです。

カプセル化を促進する 2 つの力があります: セマンティックと論理です。

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

ただし、カプセル化のもう 1 つの動機はロジックです。

システムの構成は、システムの各ノードとそれが存在するカプセル化された領域の正確かつ網羅的な識別です。Java システムの特定の構成は、(3 番目のグラフで) システムのすべてのクラスを識別し、各クラスが存在するパッケージを指定することです。

システムを論理的にカプセル化するということは、その構成に依存するシステムの数学的な特性を特定し、その特性が数学的に最小化されるようにそのシステムを構成することを意味します。

カプセル化理論は、カプセル化されたすべてのグラフが最大可能エッジ数 (MPE) を表すことを提案しています。たとえば、クラスとパッケージの Java システムでは、MPE は、そのシステムのすべてのクラス間で存在できるソース コードの依存関係の潜在的な最大数です。同じパッケージ内の 2 つのクラスは、互いに情報を隠すことができないため、両方が相互に依存関係を形成する可能性があります。ただし、個別のパッケージ内の 2 つのパッケージ プライベート クラスは、相互に依存関係を形成しない場合があります。

カプセル化理論は、MPE を最小化するために、特定の数のクラスに対して必要なパッケージの数を教えてくれます。エンティティのコレクションを変換する際の最大の潜在的な負荷は、変換されるエンティティの最大の潜在的な数の関数であると、負荷の原則の弱い形式が述べているため、これは便利です。クラスを変更すると、特定の更新を行う潜在的なコストが大きくなります。したがって、MPE を最小化すると、更新の潜在的な最大コストが最小化されます。

n 個のクラスと、パッケージごとに p 個のパブリック クラスの要件がある場合、カプセル化理論は、MPE を最小化する必要があるパッケージの数 r は、r = sqrt(n/p) という式で与えられることを示しています。

これは、システム内の McCabian ブロックの総数 n を考えると、持つべき関数の数にも当てはまります。上で述べたように、関数には常に 1 つの public ブロックしかないため、システムに含める関数の数 r の式は、r = sqrt(n) に単純化されます。

確かに、カプセル化を実践するときにシステム内のブロックの総数を考慮した人はほとんどいませんが、クラス/パッケージ レベルでは容易に実行できます。さらに、MPE を最小化することはほぼ完全に直感的です: それは、パブリック クラスの数を最小化し、パッケージにクラスを均一に分散しようとすることによって行われます (または、少なくとも、ほとんどのパッケージに 30 個のクラスを含め、1 つのモンスター パッケージに 500 個のクラスを含めることは避けます。その場合、後者の内部 MPE は、他のすべての MPE を簡単に圧倒する可能性があります)。

したがって、カプセル化には、セマンティックと論理のバランスを取ることが含まれます。

とても楽しいです。

于 2009-02-11T11:36:24.307 に答える
0

厳密なオブジェクト指向の用語では、ノーと言いたくなるかもしれません.「単なる」関数はカプセル化と呼ばれるほど強力ではありません...しかし、現実の世界では、明白な答えは「はい、関数はいくつかのコードをカプセル化します」.

この冒涜に苛立つオブジェクト指向の純粋主義者は、状態がなく単一のメソッドを持つ静的な匿名クラスを検討してください。AddOne() 関数がカプセル化でない場合、このクラスもカプセル化されません!

カプセル化は抽象化の一形態であり、その逆ではありません。;-)

于 2009-09-23T02:38:40.310 に答える
-1

通常、メソッドだけでなくプロパティを参照せずにカプセル化について話すことはあまり意味がありません。確かに、メソッドにアクセス制御を設定することはできますが、カプセル化されたメソッドにスコープされたデータがないと、それが無意味以外になるかどうかを確認するのは困難です。 。おそらくあなたはそれを検証するいくつかの議論をすることができますが、私はそれが曲がりくねっていると思います。

つまり、グローバル関数としてではなく、クラスにメソッドを配置したという理由だけで、カプセル化を使用していない可能性があります。

于 2009-02-10T20:02:51.227 に答える