في هذه المقالة، سأعرض استخدام تعدد الأشكال في GraphQL. ولكن قبل أن أبدأ، من الجدير بالذكر ما هو تعدد الأشكال و GraphQL.
تعدد الأشكال
تعدد الأشكال مكوِّن أساسي في البرمجة الموجهة للكائنات. لتبسيط الأمور، يعتمد الأمر على حقيقة أن كائنات الفئات المختلفة لديها إمكانية الوصول إلى نفس الواجهة، مما يعني أنه يمكننا أن نتوقع من كل منها نفس الوظيفة ولكن ليس بالضرورة تنفيذها بنفس الطريقة. في مطورو روبي يمكن الحصول على تعدد الأشكال بثلاث طرق
الوراثة
الوراثة تتمثل في إنشاء فئة أصل وفئات فرعية (أي وراثة من الفئة الأصل). تحصل الفئات الفرعية على وظائف الفئة الأصل وتسمح لك أيضًا بتغيير وإضافة وظيفة.
مثال على ذلك:
فئة المستند
أتر_قارئ :الاسم
نهاية
فئة مستند PDFDocument < مستند
تعريف الامتداد
:pdf
نهاية
نهاية
فئة ODTDocument < مستند
تعريف الامتداد
:odt
نهاية
النهاية
الوحدات النمطية
الوحدات النمطية في روبي لها العديد من الاستخدامات. أحد هذه الاستخدامات هو الخلطات (اقرأ المزيد عن الخلطات في التقسيم النهائي: روبي مقابل Python). يمكن استخدام mixins في روبي بشكل مشابه للواجهات في لغات البرمجة الأخرى (على سبيل المثال، في جافا)، على سبيل المثال، يمكنك أن تُعرِّف فيها طرائق مشتركة للكائنات التي ستحتوي على مزيج معين. من الممارسات الجيدة تضمين توابع للقراءة فقط في الوحدات النمطية، أي التوابع التي لن تعدّل حالة هذا الكائن.
مثال على ذلك:
الوحدة الخاضعة للضريبة
تعريف الضريبة
السعر * 0.23
النهاية
النهاية
فئة سيارة
تشمل الخاضعة للضريبة
السمة_القارئ :السعر
نهاية
فئة الكتاب
تضمين خاضع للضريبة
السمة_القارئ :السعر
النهاية
كتابة البط على الآلة الكاتبة
هذه إحدى السمات الرئيسية للغات المكتوبة ديناميكيًا. تأتي التسمية من الاختبار الشهير إذا كان شكلها كالبطة وتسبح كالبطة وتصدر صوتًا كالبطة فهي على الأرجح بطة. إن مبرمج لا يجب أن يكون مهتمًا بالفئة التي ينتمي إليها الكائن المُعطى. المهم هو الطرق التي يمكن استدعاؤها على هذا الكائن.
باستخدام الفئات المحددة في المثال أعلاه:
فئة سيارة
السمة_القارئ:السعر
تعريف تهيئة(السعر)
@السعر = السعر
النهاية
النهاية
فئة الكتاب
attr_reader :السعر
تعريف تهيئة(السعر)
@السعر = السعر
النهاية
النهاية
سيارة = Car.new.new(20.0)
كتاب = كتاب جديد(10.0)
[سيارة، كتاب].خريطة(&:السعر
GrapQL
GraphQL هي لغة استعلام جديدة نسبيًا لواجهات برمجة التطبيقات. تشمل مزاياها حقيقة أنها تحتوي على صيغة بسيطة للغاية، وبالإضافة إلى ذلك، يقرر العميل ما يريد الحصول عليه بالضبط حيث يحصل كل عميل على ما يريده بالضبط ولا شيء آخر.
نموذج الاستعلام في GraphQL:
{
جميع المستخدمين {
المستخدمون {
المعرف
تسجيل الدخول
البريد الإلكتروني
}
}
}
ربما هذا هو كل ما نحتاج إلى معرفته في الوقت الحالي. لذا، دعونا ندخل في صلب الموضوع.
عرض المشكلة
لفهم المشكلة وحلها على أفضل وجه، دعونا نخلق مثالاً. سيكون من الجيد أن يكون المثال أصليًا وواقعيًا إلى حد ما. مثال يمكن أن يواجهه كل واحد منا يوماً ما. ماذا عن... الحيوانات؟ نعم! فكرة رائعة!
لنفترض أن لدينا تطبيقًا خلفيًا مكتوبًا بلغة Ruby on Rails. تم تكييفه بالفعل للتعامل مع المخطط أعلاه. لنفترض أيضًا أن لدينا بالفعل GraphQL تم تكوينه. نريد تمكين العميل من إجراء استفسار ضمن الهيكل التالي:
{
جميع حدائق الحيوان : {
حديقة الحيوان: {
الاسم
المدينة
الحيوانات: {
...
}
}
}
}
ما الذي يجب وضعه بدلاً من النقاط الثلاث من أجل الحصول على المعلومات الناقصة - سنكتشف ذلك لاحقاً.
التنفيذ
سأعرض فيما يلي الخطوات اللازمة لتحقيق الهدف.
إضافة استعلام إلى نوع الاستعلام
أولاً، تحتاج أولاً إلى تحديد ما يعنيه الاستعلام allZoos بالضبط. للقيام بذلك، نحتاج إلى زيارة ملفالتطبيق/جرافكل/أنواع/query_type.rb وتعريف الاستعلام:
الوحدة النمطية أنواع
صنف QueryType <أنواع:::BaseObject
الحقل :all_zoos, [Types::ZooType], فارغ: خطأ
تعريف all_zoos
Zoo.all
نهاية
النهاية
النهاية
تم تعريف الاستعلام بالفعل. الآن حان الوقت لتحديد أنواع الإرجاع.
تعريف الأنواع
سيكون النوع الأول المطلوب هو ZooType. دعونا نعرّفه في الملف التطبيق/جرافكل/أنواع/ zoo_type.rb:
الوحدة النمطية أنواع
صنف ZooType <الأنواع:::BaseObject
الحقل :اسم، سلسلة، فارغة: خطأ
الحقل :مدينة، سلسلة، فارغة: خطأ
الحقل :حيوانات، [أنواع:::نوع الحيوان]، فارغ: خطأ
نهاية
النهاية
حان الوقت الآن لتعريف النوع AnimalType:
الوحدة النمطية أنواع
صنف AnimalType <الأنواع::BaseUnion
أنواع_أنواع ممكنة نوع الفيل، نوع القطة، نوع الكلب، نوع الكلب
def self.resol_type(obj, ctx)
إذا كان obj.is_a?(الفيل)
نوع الفيل
إلا إذا كان obj.is_a؟(Cat)
نوع القطة
elsif obj.is_a?(كلب)
نوع الكلب
نهاية
النهاية
النهاية
النهاية
علينا إدراج جميع الأنواع التي يمكن أن تشكل اتحادًا معينًا. 3- نتجاوز الدالة self.resol_object(obj, ctx),والتي يجب أن تُعيد نوع الكائن المُعطى.
الخطوة التالية هي تحديد أنواع الحيوانات. ومع ذلك، نحن نعلم أن بعض الحقول مشتركة بين جميع الحيوانات. دعنا ندرجها في نوع AnimalInterface:
الوحدة النمطية أنواع
الوحدة النمطية AnimalInterface
تضمين الأنواع::الواجهة الأساسية
الحقل :اسم، سلسلة، فارغة: خطأ
الحقل :عمر، عدد صحيح، فارغ: خطأ
نهاية
نهاية
بعد الحصول على هذه الواجهة، يمكننا المضي قدمًا في تحديد أنواع الحيوانات المحددة:
الوحدة النمطية أنواع
صنف ElephantType <أنواع:::كائن أساسي
يُنفذ الأنواع::AnimalInterface
الحقل :طول_الجذع, عائم, فارغ: خطأ
نهاية
نهاية
الوحدة النمطية أنواع
صنف CatType <الأنواع:::كائن أساسي
يُنفذ الأنواع::AnimalInterface
الحقل :نوع_الشعر، سلسلة، فارغة: خطأ
نهاية
النهاية
الوحدة النمطية أنواع
صنف نوع الكلب <أنواع:::كائن أساسي
يُنفذ الأنواع::AnimalInterface
الحقل :سلالة، سلسلة، لاغية: خطأ
نهاية
النهاية
هذا كل شيء! جاهز! سؤال أخير: كيف يمكننا استخدام ما قمنا به من جانب العميل؟
بناء الاستعلام
{
جميع حدائق الحيوان : {
حديقة الحيوان: {
الاسم
المدينة
الحيوانات: {
__ نوع
... على نوع الفيل {
اسم
العمر
طول الجذع
}
... على CatType {
الاسم
العمر
نوع الشعر
}
... على نوع الكلب {
الاسم
العمر
السلالة
}
}
}
}
}
يمكننا استخدام حقل __typename إضافي هنا، والذي سيعيد النوع الدقيق لعنصر معين (على سبيل المثال، CatType). كيف سيبدو نموذج الإجابة؟
أحد عيوب هذا النهج واضح. في الاستعلام، يجب أن ندخل الاسم والعمر في كل نوع، على الرغم من أننا نعلم أن جميع الحيوانات تحتوي على هذه الحقول. هذا ليس مزعجًا عندما تحتوي المجموعة على كائنات مختلفة تمامًا. لكن في هذه الحالة، تشترك الحيوانات في جميع الحقول تقريبًا. هل يمكن تحسينها بطريقة ما؟
بالطبع! نجري التغيير الأول في الملف التطبيق/جرافكل/أنواع/zoo_type.rb:
الوحدة النمطية أنواع
صنف ZooType <الأنواع:::BaseObject
الحقل :اسم، سلسلة، فارغة: خطأ
الحقل :مدينة، سلسلة، فارغة: خطأ
الحقل :حيوانات، [أنواع:::AnimalInterface]، فارغ: خطأ
نهاية
النهاية
لم نعد بحاجة إلى الاتحاد الذي حددناه من قبل. نحن نغير الأنواع::نوع الحيوان إلى الأنواع::واجهة الحيوان.
الخطوة التالية هي إضافة دالة تُرجع نوعًا من أنواع :: واجهة الحيوان وأضف أيضًا قائمة بالأنواع اليتيمة، أي الأنواع التي لا تُستخدم مباشرةً أبدًا:
الوحدة النمطية أنواع
الوحدة النمطية AnimalInterface
تضمين الأنواع::الواجهة الأساسية
الحقل :اسم، سلسلة، فارغة: خطأ
الحقل :عمر، عدد صحيح، فارغ: خطأ
تعريف_الأساليب تفعل
def resol_type(obj, ctx)
إذا كان obj.is_a؟ (فيل)
نوع الفيل
إلا إذا كان obj.is_a?(Cat)
نوع القطة
elsif obj.is_a?(كلب)
نوع الكلب
نهاية
النهاية
النهاية
أنواع الأنواع اليتيمة أنواع::ElephantType، أنواع::CatType، أنواع::DogType
نهاية
النهاية
وبفضل هذا الإجراء البسيط، يكون الاستعلام أقل تعقيدًا:
{
جميع حدائق الحيوان : {
حديقة الحيوان: {
الاسم
المدينة
الحيوانات: {
__ اسم
الاسم
العمر
... على نوع الفيل {
طول الجذع
}
... على CatType {
نوع الشعر
}
... على نوع الكلب {
سلالة
}
}
}
}
}
الملخص
GraphQL هو حل رائع حقاً. إذا كنت لا تعرفه بعد، جربه. ثق بي، إنه يستحق ذلك. إنه يقوم بعمل رائع في حل المشاكل التي تظهر في، على سبيل المثال، واجهات برمجة تطبيقات REST. كما أوضحت أعلاه, تعدد الأشكال ليست عقبة حقيقية أمامها. قدمت طريقتين لمعالجتها. تذكير:
إذا كنت تعمل على قائمة كائنات ذات قاعدة مشتركة أو واجهة مشتركة - استخدم الواجهات,
إذا كنت تعمل على قائمة من الكائنات ذات بنية مختلفة، فاستخدم واجهة مختلفة - استخدم الاتحاد