「クラスの代わりにマップを使用してデータを表現する」-リッチヒッキー


19

リッチヒッキーことで、このビデオ、Clojureの作成者、彼はJavaで行ったように、代わりにそれを表現するためにクラスを使用してのデータを表現するためにマップを使用することを助言します。単にマップとして表されている場合、APIユーザーはどのように入力キーを知ることができるのか、私はそれがどのように改善できるのか理解していません。

PersonAPI {
    Person addPerson(Person obj);
    Map<String, Object> addPerson(Map<String, Object> personMap);
}

2番目の関数では、APIユーザーはどのようにして人を作成するための入力を知ることができますか?



私もこれを知りたいです、そして、例の質問はそれに全く答えないと思います。
-sydan

私が知っている、私はSEのどこか前にこの議論を見てきました。JavaScriptのコンテキストにあったと思いますが、引数は同じでした。しかしそれを見つけることができません。
セバスチャンレッド

2
ClojureはLispなので、Lispに適切なことをする必要があります。Javaを使用するときは、...でコードを記述します。
AK_

回答:


12

Exagg'itive Summary (TM)

いくつかのものがあります。

  • プロトタイプの継承とクローニング
  • 新しいプロパティの動的な追加
  • 同じクラスの異なるバージョン(仕様レベル)のオブジェクトの共存。
    • より新しいバージョン(仕様レベル)に属するオブジェクトには、追加の「オプション」プロパティがあります。
  • 新旧のプロパティの内観
  • 検証ルールのイントロスペクション(以下で説明)

致命的な欠点が1つあります。

  • コンパイラは、スペルミスの文字列をチェックしません。
  • 自動リファクタリングツールは、プロパティキーの名前を変更しません-豪華なものにお金を払わない限り。

大事なのは、イントロスペクションを使用してイントロスペクションを取得できるということですこれは通常起こることです:

  • リフレクションを有効にします。
  • 大規模なイントロスペクションライブラリをプロジェクトに追加します。
  • さまざまなオブジェクトメソッドとプロパティを属性または注釈でマークします。
  • イントロスペクションライブラリに魔法をかけてみましょう。

言い換えれば、FPとのインターフェースが必要ない場合は、Rich Hickeyのアドバイスを受ける必要はありません。

最後になりましたがString、プロパティキーとして使用するのが最も簡単な意味を持ちますがStrings を使用する必要はありませんが、少なくとも(最もきれいではありません)Android™を含む多くのレガシーシステムは、フレームワーク全体で整数IDを広範囲に使用して、クラス、プロパティ、リソースなどを参照します。

AndroidはGoogle Inc.の商標です。


両方の世界を幸せにすることもできます。

Javaの世界では、通常どおりゲッターとセッターを実装します。

FPの世界では、

  • Object getPropertyByName(String name)
  • void setPropertyByName(String name, Object value) throws IllegalPropertyChangeException
  • List<String> getPropertyNames()
  • Class<?> getPropertyValueClass(String name)

これらの関数の中には、いや、見苦しいコードがありますが、それを埋めるIDEプラグインがあります。コードを読み取るスマートプラグインです。

物事のJava側は、通常と同じようにパフォーマンスが向上します。コードのcodeい部分は決して使用ません。Javadocから非表示にすることもできます。

世界のFP側は、必要な「リート」コードを何でも記述できます。通常、コードが遅いことについては怒りません。


一般に、オブジェクトの代わりにマップ(プロパティバッグ)を使用することは、ソフトウェア開発では一般的です。関数型プログラミングや特定の種類の言語に固有のものではありません。特定の言語に対する慣用的なアプローチではないかもしれませんが、それを必要とする状況があります。

特に、シリアル化/逆シリアル化には多くの場合、同様の手法が必要です。

「オブジェクトとしてのマップ」に関する一般的な考え方。

  1. そのような「オブジェクトとしてのマップ」を検証するための関数を提供する必要があります。違いは、「オブジェクトとしてマップ」により、より柔軟な(制限の少ない)検証基準が可能になることです。
  2. 「オブジェクトとしてのマップ」に追加フィールドを簡単に追加できます。
  3. 有効なオブジェクトの最小要件の仕様を提供するには、以下が必要です。
    • マップで予想される「最低限必要な」キーのセットをリストする
    • 値を検証する必要があるキーごとに、値検証関数を提供します
    • 複数のキー値をチェックする必要がある検証ルールがある場合は、それも提供します。
    • 利点は何ですか?このように仕様を提供することは内省的です。最小限必要なキーのセットを照会し、各キーの検証関数を取得するプログラムを作成できます。
    • OOPでは、これらすべてが「カプセル化」という名前でブラックボックスにロールアップされます。機械可読な検証ロジックの代わりに、呼び出し元は人間が読める「APIドキュメント」のみを読み取ることができます(幸いなことに存在する場合)。

commonplace私には少し強いようです。私はそれがあなたが説明するように使用されていることを意味しますが、ライブラリがひどく隠そうとすることは悪名高くて厄介なもの(バイト配列や裸のポインタなど)の1つでもあります。
テラスティン

@Telastynこの「千匹の蛇のugい頭」は、通常、2つのシステム間の通信境界で発生します。何らかの理由で、通信チャネルまたはプロセス間チャネルでは、オブジェクトをそのままテレポートできません。プロトコルバッファなどの新しい手法は、オブジェクトとしてのマップのこの古風なユースケースをほぼ排除したと思います。他の有効なユースケースがまだあるかもしれませんが、私はそれについてほとんど知識がありません。
rwong

2
致命的な欠点については、同意します。ただし、「ミススペルが容易」および「リファクタリングが困難」なプロパティキー名が可能な限り定数または列挙に保持されると、その問題はなくなります。もちろん、拡張性を制限します:
。– user949300

「1つの致命的な欠点」が本当に致命的な場合、なぜそれを効果的に使用できるのか。また、クラスと静的型付けは直交しています-動的に型付けされていても、Clojureでクラスを定義できます。
ネイサンデイビス

@NathanDavis(1)私の答えは静的型付けの観点(C#)から書かれていることを認めます。質問者と同じ視点を共有しているため、この答えを書きました。FP中心の視点が欠けていることは認めます。(2)SE.SEへようこそ。Clojureで尊敬されている人物であるため、既存の回答に満足できない場合は、時間をかけて独自の回答を記入してください。ダウン票は評判を引き、新しい回答はアップ票を引き付け、評判をすぐに追加します。(3)「不完全なオブジェクト」がどのように役立つかがわかります。特定のオブジェクト(名前、アバター)の2つのプロパティを照会し、残りを除外できます。
rwong

9

それは彼が何について話しているかを本当に知っている誰かによる素晴らしい話です。読者はすべてを読むことをお勧めします。長さはわずか36分です。

彼の主なポイントの1つは、シンプルさは後に変更の機会を開くということです。を表すクラスを選択すると、Person指摘したように静的に検証可能なAPIを作成することの利点がすぐに得られますが、機会を制限したり、後で変更や再利用のコストを増やしたりするコストが伴います。

彼のポイントは、クラスを使用することは合理的な選択かもしれませんが、コストを十分に認識して意識的に選択する必要があることであり、プログラマーは伝統的に、それらのコストを考慮することはもちろん、それらのコストに気付くという非常に貧弱な仕事をします。要件が大きくなるにつれて、その選択を再評価する必要があります。

以下は、Personオブジェクトのリストを使用する場合と比較して、マップのリストを使用する方が潜在的に簡単な、いくつかのコード変更です(1つまたは2つはトークで言及されました)。

  • 人をRESTサーバーに送信する。(Mapプリミティブを送信可能な形式にするために作成された関数は、非常に再利用可能であり、ライブラリで提供されることもあります。Personオブジェクトは、同じジョブを実行するためにカスタムコードを必要とする可能性があります)。
  • リレーショナルデータベースクエリからユーザーのリストを自動的に作成します。(繰り返しますが、1つの汎用かつ高度に再利用可能な関数)。
  • 人を表示および編集するためのフォームを自動的に生成します。
  • 一般的な機能を使用して、学生と従業員など、非常に不均質な個人データを処理します。
  • 特定の郵便番号に居住するすべての人のリストを取得します。
  • そのコードを再利用して、特定の郵便番号のすべてのビジネスのリストを取得します。
  • 他のクライアントに影響を与えることなく、顧客固有のフィールドを個人に追加します。

この種の問題を常に解決し、それらのパターンとツールを用意していますが、最初からよりシンプルで柔軟性のあるデータ表現を選択することで作業が容易になった場合、考え続けることはほとんどありません。


これに名前はありますか?オブジェクトプロパティマッピングまたはオブジェクト属性マッピング(ORMと同じ行に沿って)と言いますか?
rwong

4
Choosing a class to represent a Person provides the immediate benefit of creating a statically-verifiable API... but that comes with the cost of limiting opportunities or increasing costs for change and reuse later on.間違っており、信じられないほど不誠実です。それは向上しますが、破壊変更を行う際に、コンパイラが自動的に見つけて、あなたのためのニーズが速度にあなたの全体のコードベースを起動するように更新することをすべての場所を指摘するので、上の後に変更するためのあなたのチャンスを。動的コードでは、それを行うことはできませんが、以前の選択と本当に結びつくのです!
メイソンウィーラー

4
@MasonWheeler:あなたが本当に言っているのは、より動的な(より緩やかに型付けされた)データ構造よりもコンパイル時の型安全性を重視しているということです。
ロバートハーベイ

1
多態性は、OOPに限定された概念ではありません。マップの場合、包括的ポリモーフィズム(要素がマップが処理できるサブタイプの場合)またはアドホックポリモーフィズム(要素がタグ付きユニオンの場合)があります。これは内部です。マップ上で実行できる操作は多態的である場合もあります。要素で高次関数を使用する場合のパラメトリック多型、またはディスパッチする場合のアドホック。カプセル化は、名前空間または他の形式の可視性管理で実現できます。基本的に、オブジェクトの分離は、操作をデータ型に割り当てることとは異なります。
siefca

1
@GillBatesなんでそんなこと言うの?これらの仮想メソッドを「マップ内」に配置する機会を失うだけです。しかし、それはまさに、Rich Hickeyが言っていることです。「ActiveObjects」は本当にアンチパターンです。データをそれが何であるか(データ)として扱い、振る舞いと絡み合わせないでください。懸念事項を分離することで、大きなシンプルさの利点が得られます。
ヴァージル

4
  • 変更される可能性のある柔軟なコンテンツで、データの動作がほとんどまたはまったくない場合は、マップを使用します。IMOは、N個のフィールド、N個のセッター、およびN個のゲッターを持つ貧血ドメインモデルで構成される典型的な「javabean」または「データオブジェクト」であり、時間の無駄です。豪華な構造でラップすることで、栄光に満ちた構造で他の人を感動させようとしないでください。正直に、あなたの意図を明確にし、マップを使用してください。(または、ドメイン、JSONまたはXMLオブジェクトに意味がある場合)

  • データに重要な実際の動作(別名メソッド(Tell、Don's Ask))がある場合は、クラスを使用します。そして、実際のオブジェクト指向プログラミングを使用するために背中を叩いてください:-)。

  • データに多くの重要な検証動作と必須フィールドがある場合は、クラスを使用します。

  • データの検証動作が中程度であれば、それは境界線です。

  • データがプロパティ変更イベントを発生させる場合、それは実際には簡単であり、Mapの面倒ははるかに少ないです。ちょっとしたサブクラスを書くだけです。

  • Mapを使用する主な欠点の1つは、ユーザーが値をStrings、int、Foosなどにキャストする必要があることです。これが非常に面倒でエラーが発生しやすい場合は、クラスを検討してください。または、関連するゲッターでマップをラップするヘルパークラスを検討します。


1
実際、Rich Hickeyが主張しているのは、データの実際の振る舞いが重要な場合は、おそらく「設計」全体を間違っているということです。データは「情報」です。情報は、現実の世界では「データが保存される場所」ではありません。情報には「情報の変化を制御する操作」はありません。私たちは情報がどこに保存されているかを人々に伝えることによって情報を伝えません。オブジェクト指向のメタファーは、時として世界の適切なモデルです...しかし、たいていの場合、そうではありません。それが彼の言うことです-「ypur問題について考えてください」。すべてがオブジェクトであるわけではありません-ほとんどありません。
ヴァージル

0

のAPIにmapは2つのレベルがあります。

  1. マップ用のAPI。
  2. アプリケーションの規則。

APIは、慣例によりマップに記述できます。たとえば、ペア:api api-validateをマップに配置したり:api-foo validate-foo、慣例にすることができます。マップは保存することもできapi api-documentation-linkます。

規則を使用すると、プログラマーは、マップとして実装された「タイプ」全体のアクセスを標準化するドメイン固有の言語を作成できます。を使用(keys map)すると、実行時にプロパティを決定できます。

マップには魔法のようなものはなく、オブジェクトには魔法のようなものはありません。それはすべてディスパッチです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.