ポリモーフィズム

ポリモーフィズム の重要な構成要素である。 オブジェクト指向プログラミング.物事を単純化するために、これは異なるクラスのオブジェクトが同じインターフェースにアクセスできるという事実に基づいている。つまり ルビー 開発者 が得られる。 多型 の3つである:

継承

継承 は、親クラスと子クラス(つまり親クラスを継承するクラス)を作成することにあります。子クラスは親クラスの機能を受け継ぎ、また機能を変更したり追加したりすることができます。

クラス ドキュメント
  attr_reader :名前
終了

クラス PDFDocument < ドキュメント
  def 拡張
    :pdf
  終了
終了

class ODTDocument < ドキュメント
  def 拡張
    :odt
  終了
終了

モジュール

モジュール ルビー には多くの使い方がある。そのひとつがミックスインだ(ミックスインについては 究極のブレークダウンルビー対Python).Rubyのミキシンは、他のプログラミング言語のインターフェイスと同じように使うことができます(例えば ジャワを含むオブジェクトに共通するメソッドを定義することができます。モジュールには読み取り専用のメソッドを含めるのがよい習慣です。つまり、このオブジェクトの状態を変更しないメソッドです。

課税モジュール
  デフタックス

     価格 * 0.23
  終了
終了

クラス Car
  課税対象を含む
 attr_reader :価格
終了

クラス Book
  課税対象を含む

 attr_reader :価格
終了

アヒルのタイピング

これは動的型付け言語の重要な特徴のひとつである。この名前は、「アヒルのように見え、アヒルのように泳ぎ、アヒルのように鳴くなら、それはおそらくアヒルである」という有名なテストに由来する。これは プログラマー は、与えられたオブジェクトがどのクラスに属しているかに関心を持つ必要はない。重要なのは、このオブジェクトに対して呼び出せるメソッドである。

上記の例で定義したクラスを使用する:

クラス 車
  attr_reader :価格

 def initialize(price)
    価格 = 価格
   終了
終了

クラス Book
  attr_reader :価格

 def initialize(price)
    価格 = 価格
  終了
終了

car = Car.new(20.0)
book = Book.new(10.0)

[車, 本].map(&:価格)

グラップル

GraphQL はAPI用の比較的新しいクエリー言語である。その利点は、非常にシンプルな構文を持っていること、さらに、各顧客が望むものを正確に得ることができ、それ以外は何も得ることができないため、クライアントが正確に得たいものを決めることができることである。

のサンプルクエリ GraphQL:

{
  すべてのユーザー
     ユーザー
        id
        ログイン
        電子メール

       }
     }
   }

回答例

{
  "allUsers":{
    "users":[
     {
        "id":1,
        "login":"user1"、
        "email":"[email protected]"
      },
      {
        "id":2,
        "login":"user2"、
        "email":"[email protected]"
      },
    ]
  }

現時点で知るべきことはこれだけだろう。では、本題に入ろう。

問題の提示

To best understand the problem and its solution, let’s create an example. It would be good if the example was both original and fairly down-to-earth. One that each of us can encounter someday. How about… animals? Yes! Great idea!

rubyとgrapqlのポリモーフィズム - 動物

で書かれたバックエンドアプリケーションがあるとする。 Ruby on Rails.これは、上記のスキームを処理するためにすでに適応されている。また、すでに GraphQL を構成した。クライアントが以下の構造で問い合わせを行えるようにしたい:

{
 allZoos : {
    動物園{
      名前
      都市
      動物: {
        ...
      }
    }
  }
}

足りない情報を得るためには、3つの点の代わりに何を入れればいいのか。

実施

以下、目標を達成するために必要なステップを紹介しよう。

QueryTypeにクエリを追加する

まず、allZoosというクエリが何を意味するのかを定義する必要がある。そのためには、ファイルapp/graphql/types/query_type.rb とクエリーを定義する:

   モジュール Types
      class QueryType < Types::BaseObject
       フィールド :all_zoos, [Types::ZooType], null: false

       def all_zoos
          Zoo.all
       終了
    終了
 終了

クエリーはすでに定義されている。次は戻り値の型を定義する番だ。

タイプの定義

最初に必要なタイプはZooTypeです。ファイルで定義しましょう。 app/graphql/types/ zoo_type.rb:

モジュール Types
  class ZooType < Types::BaseObject
    field :name, String, null: false
    field :city, String, null: false
    field :animals, [Types::AnimalType], null: false
  終了
終了

次にAnimalType型を定義する:

モジュール Types
  class AnimalType < Types::BaseUnion
   指定可能な型 ElephantType, CatType, DogType

     def self.resolve_type(obj, ctx)
       if obj.is_a?(Elephant)
          ElephantType
       elsif obj.is_a?(Cat)
         CatType
       elsif obj.is_a?(Dog)
        DogType
      end
    end
  end
end

私たちは何を見るのだろうか? コード 上記?

  1. AnimalTypeは タイプ::ベースユニオン.
  2. ある組合を構成しうるすべてのタイプをリストアップしなければならない。
    3.関数をオーバーライドする self.resolve_object(obj, ctx)、これは、与えられたオブジェクトの型を返さなければならない。

次のステップは、動物の種類を定義することである。しかし、いくつかのフィールドはすべての動物に共通であることがわかっている。それらをAnimalInterface型に含めよう:

モジュール Types
  モジュール AnimalInterface
    include Types::BaseInterface

    field :name, String, null: false
    field :age, 整数, null: false
  終了
終了

このインターフェイスがあれば、特定の動物のタイプを定義することができる:

モジュール Types
  class ElephantType < Types::BaseObject
    Types::AnimalInterface を実装

    フィールド :trunk_length, Float, null: false
  終了
終了

モジュール Types
  class CatType < Types::BaseObject
   Types::AnimalInterface を実装しています。

   フィールド :hair_type, String, null: false
  終了
終了

モジュール Types
  class DogType < Types::BaseObject
    Types::AnimalInterface を実装

     フィールド :breed, String, null: false
  終了
終了

そうだ!準備完了!最後の質問:クライアント側で行ったことをどのように使えばいいのでしょうか?

クエリーの構築

{
 allZoos : {
   動物園{
      名前
      都市
      動物: {
        名前

        ... on ElephantType { {...
          名前
          年齢
          体幹の長さ
        }

         ... on CatType { ...
          名前
          年齢
          ヘアタイプ
         }
         ... on DogType {
          名前
          年齢
          犬種
         }
       }

ここで追加の__typenameフィールドを使えば、与えられた要素の正確な型を返すことができる(例:CatType)。回答例はどのようになるでしょうか?

{
  "allZoos":[

   {
      "name":"Natura Artis Magistra"、
      "city":"アムステルダム"、
      "animals":[
        {
          "__typename":"エレファントタイプ"
          "name":"フランコ"、
          "age":28,
          「幹の長さ9.27
         },
         {
         "__typename":"DogType"
         "name":"Jack"、
         "age":9,
         犬種「ジャック・ラッセル・テリア
        },
      ]
    }
  ]
} 

分析

この方法の欠点は明らかである。クエリーでは、すべての動物がこれらのフィールドを持っていることがわかっているにもかかわらず、それぞれのタイプに名前と年齢を入力しなければならない。これは、コレクションにまったく異なるオブジェクトが含まれている場合には気にならない。しかし、この場合、動物はほとんどすべてのフィールドを共有している。どうにか改善できないだろうか?

もちろんだ!ファイルに最初の変更を加える app/graphql/types/zoo_type.rb:

モジュール Types
  class ZooType < Types::BaseObject
    field :name, String, null: false
    field :city, String, null: false
    field :animals, [Types::AnimalInterface], null: false
  終了
終了

私たちはもはや、これまで定義してきた組合を必要としない。次のように変更する。 タイプ::アニマルタイプ への Types::AnimalInterface.

次のステップは、以下の型を返す関数を追加することである。 タイプ ::動物インターフェース さらに、直接使われることのない型であるorphan_typesのリストも追加した:

モジュール Types
  モジュール AnimalInterface
    include Types::BaseInterface

   field :name, String, null: false
   field :age, 整数, null: false

   definition_methods do
      def resolve_type(obj, ctx)
        if obj.is_a?(Elephant)
          象型
        elsif obj.is_a?(Cat)
          CatType
        elsif obj.is_a?(Dog)
          DogType
        end
      end
    end
    orphan_types Types::ElephantType, Types::CatType, Types::DogType
  end
終了

このシンプルな手順のおかげで、クエリーはそれほど複雑な形をしていない:

{
  allZoos : {
   動物園{
      名前
      都市
      動物: {
        名前
        名前
        年齢

       ... on ElephantType { {...
          幹の長さ

       }
       ... on CatType { 毛の長さ } ... on CatType { 毛の長さ
          毛の長さ

       }
       ... on DogType { 犬種
          犬種

        }
      }
    }
  }
}

概要

GraphQL は本当に素晴らしいソリューションだ。まだ知らない人は試してみて。信じてほしい。これは、例えばREST APIに現れる問題を解決する上で素晴らしい仕事をしてくれる。上で示したように、 多型 は、そのための本当の障害ではない。私はそれに取り組むための2つの方法を提示した。
リマインダー

続きを読む

GraphQL Ruby。パフォーマンスについては?

レールおよびその他の輸送手段

TMUX、Vim、Fzf + RipgrepによるRails開発

jaJapanese