オブジェクト指向でプログラムを書くときは、ドメイン領域をデータ型で表現することに重点を置きます。一見すると、これは良いアイデアのように見えます。ユーザーと一緒に作業するのであれば、クラスを作成しない理由はありませんUser
。そして、ユーザーが車を売買するのであれば、なぜクラスを持たないのCar
でしょうか? このようにして、データを簡単に維持し、フローを制御できます。これは、現実世界のイベントの順序を反映しているだけです。これはドメイン オブジェクトにとっては非常に便利ですが、多くの内部オブジェクト (つまり、現実世界から何も反映していないが、プログラム ロジックでのみ発生するオブジェクト) にとってはあまり効果的ではありません。最良の例は、Java のコレクション型の数です。Java (および他の多くの OOP 言語) には、両方の配列List
s があります。JDBCにはResultSet
これも一種のコレクションですが、Collection
インターフェースを実装していません。InputStream
入力には、連結リストのように、データへのシーケンシャル アクセス用のインターフェイスを提供するものをよく使用します。ただし、どのような種類のコレクション インターフェイスも実装していません。したがって、コードがデータベースで動作し、ResultSet
それを使用する場合、テキスト ファイルやInputStream
.
MFUFA の原則は、型の定義にはあまり注意を払わず、一般的な抽象化に注意を払うように教えてくれます。このため、Clojure では、言及されているすべての型 (シーケンス) に対して単一の抽象化を導入しています。イテラブルは自動的にシーケンスに強制され、ストリームは単なる遅延リストであり、結果セットは以前のタイプのいずれかに簡単に変換できます。
もう 1 つの例はPersistentMap
、構造体とレコードにインターフェイスを使用することです。このような共通のインターフェースを使用すると、再利用可能なサブルーチンを作成するのが非常に簡単になり、リファクタリングに多くの時間を費やす必要がなくなります。
質問を要約して答えるには:
- OOP で頻繁に発生する問題の簡単な例: 多くの異なるソース (DB、ファイル、ネットワークなど) からデータを読み取り、それを同じ方法で処理します。
- 優れた MFUFA 設計を行うには、抽象化を可能な限り共通にし、アドホックな実装を避けるようにしてください。たとえば、 a-la 型を避ける
UserList
-List<User>
ほとんどの場合、これで十分です。
- ポイント 2 の提案に従います。さらに、データ型 (クラス) にできるだけ多くのインターフェイスを追加するようにしてください。たとえば、本当に必要な場合
UserList
(たとえば、多くの追加機能が必要な場合)、List
とIterable
インターフェイスの両方をその定義に追加します。
- OOP (少なくとも Java と C# では) は、初期設計時にオブジェクト全体の動作をカプセル化しようとするため、この原則にはあまり適していません。ほとんどの場合、問題のクラスを拡張し、必要なメソッドを新しいオブジェクトに入れることができますが、1) 他の誰かが独自の派生クラスを実装している場合、それはあなたのものと互換性がありません。2) クラス
final
またはすべてのフィールドが作成されるprivate
場合があるため、派生クラスはそれらにアクセスできません (たとえば、クラスに新しい関数を追加するには、追加のString
クラスを実装する必要がありますStringUtils
)。それにもかかわらず、上で説明したルールにより、OOP コードで MFUFA を使用することがはるかに簡単になります。ここでの最良の例は、Clojure 自体です。これは、オブジェクト指向スタイルで適切に実装されていますが、MFUFA の原則に従っています。
アップデート。オブジェクト指向スタイルと関数型スタイルの違いについての別の説明を覚えています。これは、私が上で述べたすべてのことをよりよく要約しているかもしれません: オブジェクト指向スタイルでプログラムを設計することは、データ型(名詞) の観点から考えることですが、関数型スタイルで設計することは、操作の観点から考えることです(動詞)。いくつかの名詞が似ていることを忘れるかもしれませんが (例えば、継承を忘れるなど)、実際には多くの動詞が同じことを行う (例えば、同じまたは類似のインターフェースを持つ) ことを常に覚えておく必要があります。