15

Clojure言語の作成者は、「オープンで大規模な関数のセットは、オープンで小規模な拡張可能な抽象化のセットで動作することが、アルゴリズムの再利用とライブラリの相互運用性の鍵である」と主張しています。明らかに、多くの抽象化(クラス)とそれらを操作する比較的少数の関数のセットを作成する典型的なOOPアプローチと矛盾します。本、本の章、記事、またはトピックについて詳しく説明した個人的な経験を提案してください。

  1. OOPに現れる問題の動機付けの例と、「いくつかの抽象化で多くの関数」を使用することでそれらにどのように対処するか
  2. MFUFA*設計を効果的に行う方法
  3. MFUFAに向けてOOPコードをリファクタリングする方法
  4. OOP言語の構文がMFUFAの邪魔になる方法

* MFUFA:「いくつかの抽象化で多くの機能」

4

4 に答える 4

18

プログラミングにおける「抽象化」には、主に 2 つの概念があります。

  1. パラメータ化 (「ポリモーフィズム」、汎用性)。
  2. カプセル化(データ隠蔽)、

[編集: これら 2 つはデュアルです。1 つ目はクライアント側の抽象化であり、2 つ目は実装者側の抽象化です (これらのことを気にする場合: 形式論理または型理論の観点から、それらはそれぞれ普遍的および存在量化に対応します)。]

OO では、クラスは両方の種類の抽象化を実現するための便利な機能です。

広告 (1)、ほとんどすべての「パターン」に対して、カスタム クラス (または複数) を定義する必要があります。一方、関数型プログラミングでは、多くの場合、同じ目標を達成するためのより軽量で直接的なメソッド、特に関数とタプルがあります。たとえば、GoF の「設計パターン」のほとんどが FP では冗長であることがよく指摘されます。

広告 (2)、カプセル化が必要な頻度は、チェックしておく必要のあるあらゆる場所に可変状態が残っていない場合は、少し少なくなります。FP で ADT を作成することはできますが、ADT はより単純で汎用的なものになる傾向があるため、必要な ADT の数は少なくなります。

于 2012-05-12T16:41:22.163 に答える
9

オブジェクト指向でプログラムを書くときは、ドメイン領域をデータ型で表現することに重点を置きます。一見すると、これは良いアイデアのように見えます。ユーザーと一緒に作業するのであれば、クラスを作成しない理由はありませんUser。そして、ユーザーが車を売買するのであれば、なぜクラスを持たないのCarでしょうか? このようにして、データを簡単に維持し、フローを制御できます。これは、現実世界のイベントの順序を反映しているだけです。これはドメイン オブジェクトにとっては非常に便利ですが、多くの内部オブジェクト (つまり、現実世界から何も反映していないが、プログラム ロジックでのみ発生するオブジェクト) にとってはあまり効果的ではありません。最良の例は、Java のコレクション型の数です。Java (および他の多くの OOP 言語) には、両方の配列Lists があります。JDBCにはResultSetこれも一種のコレクションですが、Collectionインターフェースを実装していません。InputStream入力には、連結リストのように、データへのシーケンシャル アクセス用のインターフェイスを提供するものをよく使用します。ただし、どのような種類のコレクション インターフェイスも実装していません。したがって、コードがデータベースで動作し、ResultSetそれを使用する場合、テキスト ファイルやInputStream.

MFUFA の原則は、型の定義にはあまり注意を払わず、一般的な抽象化に注意を払うように教えてくれます。このため、Clojure では、言及されているすべての型 (シーケンス) に対して単一の抽象化を導入しています。イテラブルは自動的にシーケンスに強制され、ストリームは単なる遅延リストであり、結果セットは以前のタイプのいずれかに簡単に変換できます。

もう 1 つの例はPersistentMap、構造体とレコードにインターフェイスを使用することです。このような共通のインターフェースを使用すると、再利用可能なサブルーチンを作成するのが非常に簡単になり、リファクタリングに多くの時間を費やす必要がなくなります。

質問を要約して答えるには:

  1. OOP で頻繁に発生する問題の簡単な例: 多くの異なるソース (DB、ファイル、ネットワークなど) からデータを読み取り、それを同じ方法で処理します。
  2. 優れた MFUFA 設計を行うには、抽象化を可能な限り共通にし、アドホックな実装を避けるようにしてください。たとえば、 a-la 型を避けるUserList-List<User>ほとんどの場合、これで十分です。
  3. ポイント 2 の提案に従います。さらに、データ型 (クラス) にできるだけ多くのインターフェイスを追加するようにしてください。たとえば、本当に必要な場合UserList(たとえば、多くの追加機能が必要な場合)、ListIterableインターフェイスの両方をその定義に追加します。
  4. OOP (少なくとも Java と C# では) は、初期設計時にオブジェクト全体の動作をカプセル化しようとするため、この原則にはあまり適していません。ほとんどの場合、問題のクラスを拡張し、必要なメソッドを新しいオブジェクトに入れることができますが、1) 他の誰かが独自の派生クラスを実装している場合、それはあなたのものと互換性がありません。2) クラスfinalまたはすべてのフィールドが作成されるprivate場合があるため、派生クラスはそれらにアクセスできません (たとえば、クラスに新しい関数を追加するには、追加のStringクラスを実装する必要がありますStringUtils)。それにもかかわらず、上で説明したルールにより、OOP コードで MFUFA を使用することがはるかに簡単になります。ここでの最良の例は、Clojure 自体です。これは、オブジェクト指向スタイルで適切に実装されていますが、MFUFA の原則に従っています。

アップデート。オブジェクト指向スタイルと関数型スタイルの違いについての別の説明を覚えています。これは、私が上で述べたすべてのことをよりよく要約しているかもしれません: オブジェクト指向スタイルでプログラムを設計することは、データ型(名詞) の観点から考えることですが、関数型スタイルで設計することは、操作の観点から考えることです(動詞)。いくつかの名詞が似ていることを忘れるかもしれませんが (例えば、継承を忘れるなど)、実際には多くの動詞が同じことを行う (例えば、同じまたは類似のインターフェースを持つ) ことを常に覚えておく必要があります。

于 2012-05-12T21:13:41.100 に答える
6

引用のはるかに古いバージョン:

「リストの単純な構造と自然な適用可能性は、驚くほど非特異的な関数に反映されています。Pascal では、宣言可能なデータ構造が多すぎるため、関数内の特殊化が誘導され、カジュアルな連携が妨げられ、不利になります。1 つのデータ構造で 100 個の関数を操作する方がよいでしょう。 10 個の関数を 10 個のデータ構造で操作するよりも。」

...有名なSICP本の序文から来ています。この本には、このトピックに関する適用可能な資料がたくさんあると思います。

于 2012-05-12T18:17:21.617 に答える
0

ライブラリとプログラムに違いがあることを理解していないと思います。

正常に機能する OO ライブラリは、通常、少数の抽象化を生成します。この抽象化は、プログラムがドメインの抽象化を構築するために使用します。大規模な OO ライブラリ (およびプログラム) では、継承を使用してさまざまなバージョンのメソッドを作成し、新しいメソッドを導入します。

そうです、同じ原則がオブジェクト指向ライブラリにも当てはまります。

于 2012-05-12T15:51:01.173 に答える