一見単純に見える継承は、多くの場合、変化に耐える複雑な構造になります。 クラス階層は本物の森のように成長します。
継承の目的は、コード(メソッドのセット)を、それが提供し必要とするエンティティプロパティの最小セット(通常はオブジェクト)にバインドすることです。 これにより、コードの再利用、テスト、および分析が簡素化されます。 しかし、プロパティのセットは時間とともに非常に大きくなり、重要な方法で交差し始めます。 そして、ミックスインやその他の多重継承がクラスの構造に現れます。
階層の深さを変更すると問題が発生するため、事前に「依存性注入」について考え、複雑なリファクタリングツールを開発して使用する必要があります。
これをすべて回避することは可能ですか? 試してみる価値があります-メソッドをオブジェクトの特性プロパティ(タグ)のセットに結び付け、これらのセットのネストに従って継承階層を自動的に構築します。
ゲームキャラクターの階層を設計しましょう。 コードの一部はすべてのキャラクターに共通です-それはプロパティの空のセットに関連付けられています。 それらを表示するコードは、異なるバージョンのOpenGLとDirectXのオプションの形式で表示されます。 何かはキャラクターの種族、魔法の能力の存在や種類などに依存します。 文字タグが主要です。 それらは明示的にリストされ、継承されません。 また、実装は、タグのセットに応じて(ネストすることにより)継承されます。 したがって、カンガルーは歩兵から継承されたため、MANPADSから撮影することはできません。
このアプローチのアイデアは、Dmitry Kimによって提案されました。 著者はコードでそれを具体化しなかったので、この省略を修正しようとします。
いつものように
githubでClojureにこのアプローチを実装します。
この継承メソッドの実装は、ジェネリック関数のClojureシステム(マルチメソッド)の上で行われます。
defmultiで定義された各マルチメソッドには、階層と、引数を階層の要素(または要素の配列)にマップするディスパッチ関数があります。 通常、階層要素はデータ型ですが、階層で「キーワード」と「シンボル」を使用して、必要な型に割り当てられたデータをマークすることもできます。
階層要素の具体的なメソッド実装は、defmetodを使用して指定されます。
次のようになります。
(use 'inheritance.grid) (def grid (make-grid-hierarchy)) (defmulti canFly " " (grid-dispatch1) :hierarchy #'grid) (defmulti canFireball " " (grid-dispatch1) :hierarchy #'grid) (defmulti canFire " " (grid-dispatch1) :hierarchy #'grid) (defmethod canFly (get-grid-node {} #'grid) [p] false)
仕組み:
まず、階層を作成する必要があります-これは通常のClojure階層で、タグのセット(連想配列の形式)を階層に参加するキャラクターにマップするテーブルがあります。 テーブルは最初は空で、階層オブジェクトのメタ情報に保存されています。
(defn make-grid-hierarchy " " [] (let [h (make-hierarchy)]
タグの各セットは階層に登録する必要があります-シンボルが作成され、階層内の正しい場所に含まれ、このシンボルが見つかるようにテーブル内の対応するエントリが追加されます。 階層内の適切な場所を計算することが、この継承管理方法の基盤です。
(defn register-grid-node " " [ho] (let [nl (get (meta h) :grid-hierarchy-cache {})] (if-let [s (nl o)]
ここで、一連のタグで定義された特定のラティスノードの型を、この型に属すると思われるデータに関連付ける方法を学習する必要があります。
(defn with-grid-node " , " [nh] (let [s (get-grid-node nh)] (fn [v] (with-meta v (assoc (or (meta v) {}) :grid-node s)))))
ノードのテーブルで検索が繰り返されるのを避けるために、この関数はノードに対応する文字を受け取り、この文字を引数のメタ情報に追加するクロージャーを返します。
ディスパッチ機能は簡単です。
(defn grid-dispatch " " [] (fn [& v] (vec (map (fn [a] (:grid-node (meta a))) v)))) (defn grid-dispatch1 " " [] (fn [v & _] (:grid-node (meta v))))
私はすでにCommon Lispでそのような継承
を実装しようとしました 。 しかし、MOPデバイスはわかりません。その実装はCLOSに組み込まれておらず、あまり効率的でもありません。