window.pipedriveLeadboosterConfig = { bas: 'leadbooster-chat.pipedrive.com', företagId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(funktion () { var w = fönster if (w.LeadBooster) { console.warn('LeadBooster finns redan') } annars { w.LeadBooster = { q: [], on: funktion (n, h) { this.q.push({ t: "o", n: n, h: h }) }, trigger: funktion (n) { this.q.push({ t: 't', n: n }) }, } } })() Polymorfism i Ruby och GraphQL - The Codest
Codest
  • Om oss
  • Tjänster
    • Utveckling av programvara
      • Frontend-utveckling
      • Backend-utveckling
    • Staff Augmentation
      • Frontend-utvecklare
      • Backend-utvecklare
      • Dataingenjörer
      • Ingenjörer inom molntjänster
      • QA-ingenjörer
      • Övriga
    • Det rådgivande
      • Revision och rådgivning
  • Industrier
    • Fintech & bankverksamhet
    • E-commerce
    • Adtech
    • Hälsoteknik
    • Tillverkning
    • Logistik
    • Fordon
    • IOT
  • Värde för
    • VD OCH KONCERNCHEF
    • CTO
    • Leveranschef
  • Vårt team
  • Fallstudier
  • Vet hur
    • Blogg
    • Möten
    • Webbinarier
    • Resurser
Karriär Ta kontakt med oss
  • Om oss
  • Tjänster
    • Utveckling av programvara
      • Frontend-utveckling
      • Backend-utveckling
    • Staff Augmentation
      • Frontend-utvecklare
      • Backend-utvecklare
      • Dataingenjörer
      • Ingenjörer inom molntjänster
      • QA-ingenjörer
      • Övriga
    • Det rådgivande
      • Revision och rådgivning
  • Värde för
    • VD OCH KONCERNCHEF
    • CTO
    • Leveranschef
  • Vårt team
  • Fallstudier
  • Vet hur
    • Blogg
    • Möten
    • Webbinarier
    • Resurser
Karriär Ta kontakt med oss
Pil tillbaka GÅ TILLBAKA
2022-01-13
Utveckling av programvara

Polymorfism i Ruby och GraphQL

Lukasz Brzeszcz

I den här artikeln kommer jag att presentera användningen av polymorfism i GraphQL. Innan jag börjar är det dock värt att påminna om vad polymorfism och GraphQL är.

Polymorfism

Polymorfism är en viktig komponent i objektorienterad programmering. Förenklat bygger det på att objekt av olika klasser har tillgång till samma gränssnitt, vilket innebär att vi kan förvänta oss samma funktionalitet från var och en av dem, men inte nödvändigtvis implementerad på samma sätt. I Ruby-utvecklare kan erhållas polymorfism på tre olika sätt:

Ärftlighet

Ärftlighet består i att skapa en överordnad klass och underordnade klasser (dvs. som ärver från den överordnade klassen). Underklasser får den överordnade klassens funktionalitet och gör det också möjligt att ändra och lägga till funktionalitet.

Exempel:

klass Dokument
  attr_läsare :namn
slut

klass PDFDocument < Dokument
  def förlängning
    :pdf
  slut
slut

klass ODTDocument < Dokument
  def förlängning
    :odt
  slut
slut

Moduler

Moduler i Ruby har många användningsområden. Ett av dem är mixins (läs mer om mixins i Den ultimata uppdelningen: Ruby vs. Python). Mixins i Ruby kan användas på liknande sätt som gränssnitt i andra programmeringsspråk (t.ex. i Java) kan du till exempel definiera metoder som är gemensamma för objekt som kommer att innehålla en viss mixin. Det är en god praxis att inkludera skrivskyddade metoder i moduler, dvs. metoder som inte ändrar objektets tillstånd.

Exempel:

modul Taxable
  def skatt

     pris * 0,23
  slut
slut

klass Bil
  include Skattepliktig
 attr_läsare :pris
slut

klass Bok
  inkluderar Taxable

 attr_läsare :pris
slut

Skrivning av anka

Detta är en av de viktigaste egenskaperna hos dynamiskt typade språk. Namnet kommer från det berömda testet om det ser ut som en anka, simmar som en anka och kvackar som en anka, då är det förmodligen en anka. Testet programmerare behöver inte vara intresserad av vilken klass det givna objektet tillhör. Det som är viktigt är de metoder som kan anropas på detta objekt.

Med hjälp av de klasser som definieras i exemplet ovan:

klass Bil
  attr_reader :pris

 def initialize(pris)
    @pris = pris
   slut
slut

klass Bok
  attr_läsare :pris

 def initialize(pris)
    @pris = pris
  slut
slut

bil = Bil.ny(20.0)
bok = Bok.ny(10.0)

[bil, bok].map(&:pris

GrapQL

GraphQL är ett relativt nytt frågespråk för API:er. Till dess fördelar hör att det har en mycket enkel syntax och dessutom bestämmer kunden exakt vad den vill få eftersom varje kund får exakt vad den vill ha och inget annat.

Exempel på fråga i GraphQL:

{
  allUsers {
     användare {
        id
        inloggning
        e-post

       }
     }
   }

Provsvar:

{
  "allUsers": {
    "användare": [
     {
        "id": 1,
        "login": "user1",
        "email": "[email protected]"
      },
      {
        "id": 2,
        "login": "user2",
        "email": "[email protected]"
      },
    ]
  }
}

Detta är förmodligen allt vi behöver veta just nu. Så, låt oss komma till saken.

Presentation av problemet

För att på bästa sätt förstå problemet och dess lösning, låt oss skapa ett exempel. Det skulle vara bra om exemplet var både originellt och ganska jordnära. Ett som var och en av oss kan stöta på någon dag. Vad sägs om... djur? Ja! Ja! Bra idé!

ruby och grapql polymorfism - djur

Anta att vi har en backend-applikation som är skriven i Ruby on Rails. Den är redan anpassad för att hantera ovanstående schema. Låt oss också anta att vi redan har GraphQL konfigurerad. Vi vill göra det möjligt för kunden att göra en förfrågan inom följande struktur:

{
 allaZoo : {
    djurpark: {
      namn
      stad
      djur: {
        ...
      }
    }
  }
}

Vad som ska sättas i stället för de tre prickarna för att få fram den saknade informationen - det får vi reda på senare.

Implementering

Nedan kommer jag att presentera de steg som behövs för att uppnå målet.

Lägga till en fråga i QueryType

Först måste du definiera vad exakt frågan allZoos betyder. För att göra detta måste vi besöka filenapp/graphql/types/query_type.rb och definiera frågan:

   modul Typer
      klass QueryType < Typer::BaseObject
       fält :all_zoos, [Types::ZooType], null: false

       def all_zoos
          Zoo.alla
       slut
    slut
 slut

Frågeställningen är redan definierad. Nu är det dags att definiera returtyperna.

Definition av typer

Den första typen som krävs kommer att vara ZooType. Låt oss definiera den i filen app/graphql/types/ zoo_type.rb:

modul Typer
  klass ZooType < Typer::BaseObject
    fält :name, Sträng, null: false
    fält :city, Sträng, null: false
    fält :animals, [Types::AnimalType], null: false
  slut
slut

Nu är det dags att definiera typen AnimalType:

modul Typer
  klass AnimalType < Typer::BasUnion
   möjliga_typer ElephantType, CatType, DogType

     def self.resolve_type(obj, ctx)
       if obj.is_a?(Elephant)
          ElefantTyp
       elsif obj.is_a?(Cat)
         KattTyp
       elsif obj.is_a?(Hund)
        HundTyp
      slut
    slut
  slut
slut

Vad är det vi ser i kod ovan?

  1. AnimalType ärver från Typer::BaseUnion.
  2. Vi måste lista alla typer som kan ingå i en viss förening.
    3. Vi åsidosätter funktionen self.resolve_object(obj, ctx),som måste returnera typen av ett givet objekt.

Nästa steg är att definiera vilka typer av djur det handlar om. Vi vet dock att vissa fält är gemensamma för alla djur. Låt oss inkludera dem i typen AnimalInterface:

modul Typer
  modul AnimalInterface
    include Typer::BaseInterface

    fält :namn, Sträng, null: false
    fält :age, heltal, null: false
  slut
slut

Med detta gränssnitt kan vi gå vidare med att definiera typerna av specifika djur:

modul Typer
  klass ElephantType < Typer::BaseObject
    implementerar Types::AnimalInterface

    fält :trunk_length, Float, null: false
  slut
slut

modul Typer
  klass CatType < Typer::BaseObject
   implementerar Types::AnimalInterface

   fält :hair_type, String, null: false
  slut
slut

modul Typer
  klass DogType < Typer::BaseObject
    implementerar Types::AnimalInterface

     fält :breed, String, null: false
  slut
slut

Så där ja! Nu är vi klara! En sista fråga: hur kan vi använda det vi har gjort från kundens sida?

Uppbyggnad av förfrågan

{
 allaZoo : {
   djurpark: {
      namn
      stad
      djur: {
        __typenamn

        ... på ElephantType {
          namn
          ålder
          snabelLängd
        }

         ... på CatType {
          namn
          ålder
          hårTyp
         }
         ... på DogType {
          namn
          ålder
          ras
         }
       }
     }
   }
 }

Vi kan använda ett extra __typename-fält här, som returnerar den exakta typen av ett givet element (t.ex. CatType). Hur kan ett exempel på svar se ut?

{
  "allZoos": [

   {
      "namn": "Natura Artis Magistra",
      "city": "Amsterdam",
      "animals": [
        {
          "__typenamn": "ElefantTyp"
          "namn": "Franco",
          "ålder": 28,
          "trunkLength": 9.27
         },
         {
         "__typenamn": "DogType"
         "name": "Jack",
         "ålder": 9,
         "ras": "Jack Russell Terrier"
        },
      ]
    }
  ]
} 

Analys

En nackdel med detta tillvägagångssätt är uppenbar. I frågan måste vi ange namn och ålder för varje typ, trots att vi vet att alla djur har dessa fält. Detta är inte så besvärande när samlingen innehåller helt olika objekt. I det här fallet delar dock djuren nästan alla fält. Kan det förbättras på något sätt?

Naturligtvis! Vi gör den första ändringen i filen app/graphql/types/zoo_type.rb:

modul Typer
  klass ZooType < Typer::BaseObject
    fält :name, Sträng, null: false
    fält :city, Sträng, null: false
    fält :animals, [Types::AnimalInterface], null: false
  slut
slut

Vi behöver inte längre den union som vi tidigare definierade. Vi förändrar Typer::AnimalType till Typer::AnimalInterface.

Nästa steg är att lägga till en funktion som returnerar en typ från Typer :: DjurInterface och även lägga till en lista över orphan_types, dvs. typer som aldrig används direkt:

modul Typer
  modul AnimalInterface
    include Typer::BaseInterface

   fält :namn, Sträng, null: false
   fält :age, Heltal, null: false

   definition_metoder do
      def resolve_type(obj, ctx)
        if obj.is_a?(Elephant)
          ElefantTyp
        elsif obj.is_a?(Katt)
          KattTyp
        elsif obj.is_a?(Hund)
          HundTyp
        slut
      slut
    slut
    orphan_types Typer::ElephantType, Typer::CatType, Typer::DogType
  slut
slut

Tack vare denna enkla procedur har frågan en mindre komplex form:

{
  allaZoo : {
   djurpark: {
      namn
      stad
      djur: {
        __typenamn
        namn
        ålder

       ... på ElephantType {
          snabelLängd

       }
       ... på CatType {
          hårTyp

       }
       ... på DogType {
          ras

        }
      }
    }
  }
}

Sammanfattning

GraphQL är en riktigt bra lösning. Om du inte känner till det ännu, prova det. Lita på mig, det är värt det. Den gör ett bra jobb med att lösa problem som uppstår i till exempel REST API:er. Som jag visade ovan, polymorfism är inte ett verkligt hinder för det. Jag presenterade två metoder för att ta itu med det.
Påminnelse:

  • Om du arbetar med en lista över objekt som har en gemensam bas eller ett gemensamt gränssnitt - använd gränssnitten,
  • Om du arbetar med en lista med objekt med en annan struktur, använd ett annat gränssnitt - använd unionen

Läs mer om

GraphQL Ruby. Hur är det med prestanda?

Räls och andra transportmedel

Rails-utveckling med TMUX, Vim, Fzf + Ripgrep

Relaterade artiklar

Utveckling av programvara

Bygg framtidssäkrade webbappar: Insikter från The Codest:s expertteam

Upptäck hur The Codest utmärker sig genom att skapa skalbara, interaktiva webbapplikationer med banbrytande teknik som ger sömlösa användarupplevelser på alla plattformar. Läs om hur vår expertis driver digital omvandling och affärsutveckling...

DEKODEST
Utveckling av programvara

Topp 10 Lettlandsbaserade mjukvaruutvecklingsföretag

Läs mer om Lettlands främsta mjukvaruutvecklingsföretag och deras innovativa lösningar i vår senaste artikel. Upptäck hur dessa teknikledare kan hjälpa till att lyfta ditt företag.

thecodest
Lösningar för företag och uppskalningsföretag

Java Software Development Essentials: En guide till framgångsrik outsourcing

Utforska denna viktiga guide om framgångsrik outsourcing av Java-programvaruutveckling för att förbättra effektiviteten, få tillgång till expertis och driva projektframgång med The Codest.

thecodest
Utveckling av programvara

Den ultimata guiden till outsourcing i Polen

Den kraftiga ökningen av outsourcing i Polen drivs av ekonomiska, utbildningsmässiga och tekniska framsteg, vilket främjar IT-tillväxt och ett företagsvänligt klimat.

TheCodest
Lösningar för företag och uppskalningsföretag

Den kompletta guiden till verktyg och tekniker för IT-revision

IT-revisioner säkerställer säkra, effektiva och kompatibla system. Läs mer om hur viktiga de är genom att läsa hela artikeln.

Codest
Jakub Jakubowicz CTO och medgrundare

Prenumerera på vår kunskapsbas och håll dig uppdaterad om expertisen från IT-sektorn.

    Om oss

    The Codest - Internationellt mjukvaruutvecklingsföretag med teknikhubbar i Polen.

    Förenade kungariket - Huvudkontor

    • Kontor 303B, 182-184 High Street North E6 2JA
      London, England

    Polen - Lokala tekniknav

    • Fabryczna Office Park, Aleja
      Pokoju 18, 31-564 Kraków
    • Brain Embassy, Konstruktorska
      11, 02-673 Warszawa, Polen

      Codest

    • Hem
    • Om oss
    • Tjänster
    • Fallstudier
    • Vet hur
    • Karriär
    • Ordbok

      Tjänster

    • Det rådgivande
    • Utveckling av programvara
    • Backend-utveckling
    • Frontend-utveckling
    • Staff Augmentation
    • Backend-utvecklare
    • Ingenjörer inom molntjänster
    • Dataingenjörer
    • Övriga
    • QA-ingenjörer

      Resurser

    • Fakta och myter om att samarbeta med en extern partner för mjukvaruutveckling
    • Från USA till Europa: Varför väljer amerikanska startup-företag att flytta till Europa?
    • Jämförelse av Tech Offshore Development Hubs: Tech Offshore Europa (Polen), ASEAN (Filippinerna), Eurasien (Turkiet)
    • Vilka är de största utmaningarna för CTO:er och CIO:er?
    • Codest
    • Codest
    • Codest
    • Privacy policy
    • Användarvillkor för webbplatsen

    Copyright © 2025 av The Codest. Alla rättigheter reserverade.

    sv_SESwedish
    en_USEnglish de_DEGerman da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian jaJapanese ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek sv_SESwedish