نصمم لغة برمجة متعددة النماذج. الجزء 2 - مقارنة بين نماذج البناء في PL / SQL و LINQ و GraphQL

في آخر وظيفةلقد طرحت سؤالا مفاده أن منطق الأعمال لأنظمة المعلومات الحديثة يتضمن الكثير من العناصر ، وأوصافها ذات طبيعة تعريفية: هيكل المفاهيم ، العلاقات بينها ، الشروط ، القواعد ، تحول المفاهيم عند الانتقال من طبقة تطبيق إلى أخرى ، الاتحاد ، والتصفية ، والتجميع ، وما إلى ذلك. من وجهة نظري ، تعتبر الأنماط الوظيفية والموجهة للكائنات أدنى من المنطقية من حيث ملاءمة تنفيذ البرنامج لنموذج المجال. ينقل النمط المنطقي العلاقات بين المفاهيم بطريقة أكثر إحكاما وطبيعية. لذلك ، حددت لنفسي هدفًا لإنشاء لغة برمجة هجينة تجمع بين نموذج موجه نحو الكائن أو نموذج وظيفي مع نموذج منطقي. علاوة على ذلك ، يجب أن يكون المكون المنطقي مناسبًا لوصف نموذج المجال - بنية مفاهيمه ،وكذلك العلاقات والتبعيات بينهما.



في هذا المنشور ، أريد التحدث عن بعض اللغات والتقنيات الشائعة التي تتضمن عناصر البرمجة التصريحية - PL / SQL و MS LINQ و GraphQL . سأحاول معرفة المهام التي يتم حلها فيها باستخدام البرمجة التصريحية ، ومدى تشابك المقاربات التصريحية والضرورية ، وما هي المزايا التي توفرها ، والأفكار التي يمكن تعلمها منها.



الامتدادات الإجرائية لـ SQL



لنبدأ بمجال أصبح فيه هذا الارتباط منذ فترة طويلة معيارًا صناعيًا - لغات الوصول إلى البيانات. أشهرها PL / SQL ، وهو امتداد إجرائي للغة SQL. تسمح لك هذه اللغة بمعالجة البيانات في قاعدة بيانات علائقية باستخدام كل من الأمر (المتغيرات ، عبارات التحكم ، الوظائف ، الكائنات) وأنماط البرمجة التعريفية (تعبيرات SQL). باستخدام استعلام SQL ، يمكننا وصف خصائص البيانات التي نحتاجها - ما هي الحقول المطلوبة ، والجداول التي نأخذها منها ، وكيف ترتبط ببعضها البعض ، وما هي القيود التي يجب أن تمتثل لها ، وكيف يجب تجميعها ، إلخ. وسيعمل خادم قاعدة البيانات بشكل مستقل على وضع خطة تنفيذ الاستعلام والعثور على جميع المجموعات الممكنة من الحقول التي تفي بالشروط المحددة. يسمح لك الجزء الإجرائي من PL / SQL بتنفيذ هذه المهامالتي يصعب أو يستحيل التعبير عنها في شكل تعريفي - لمعالجة نتيجة استعلام في حلقة ، وإجراء حسابات عشوائية ، وتنظيم الكود في وظائف وفئات.



يتم دمج المكونات الإجرائية والتصريحية للغة بإحكام. يسمح لك PL / SQL بتعريف الوظائف وتنفيذ الاستعلامات بداخلها وإرجاع نتائجها واستخدام الوظائف داخل استعلام وتمريرها قيم حقول الجدول كوسائط. يمكنك الوصول إلى نتائج الاستعلام باستخدام المؤشرات ثم التكرار بشكل إلزامي عبر جميع السجلات المسترجعة. تمنحك المؤشرات مزيدًا من التحكم في محتويات الجداول وتسمح لك بتنفيذ منطق معالجة بيانات أكثر تعقيدًا من استخدام SQL وحدها. يمكن تعيين المؤشر إلى متغير المؤشر وتمريره كوسيطة للوظائف أو الإجراءات أو حتى تطبيق العميل. يمكن إنشاء رمز الطلب نفسه ديناميكيًا من خلال سلسلة من الأوامر الحتمية.يتيح لك الجمع بين الإجراءات والاستعلامات ، باستخدام بعض التعديلات ، تنفيذ الاستعلامات العودية. هناك أيضًا ميزات موجهة للكائنات في PL / SQL تتيح لك الإعلان عن أنواع البيانات المركبة لحقول الجدول ، وتضمين طرقًا فيها ، وإنشاء فئات من خلال الوراثة.



يسمح لك PL / SQL بتنفيذ منطق الأعمال على جانب خادم قاعدة البيانات. علاوة على ذلك ، سيكون تنفيذ نموذج المجال قريبًا جدًا من وصفه. سيتم تعيين المفاهيم الأساسية لنموذج المجال إلى نموذج البيانات العلائقية. تتوافق المفاهيم مع الجداول والسمات - الحقول الخاصة بهم. يمكن تضمين القيود المفروضة على قيم الحقول في أوصاف الجدول. ويمكن تعيين العلاقات مع الجداول الأخرى باستخدام مفاتيح خارجية. المفاهيم المجردة المبنية على أساس الأساسيات سوف تتوافق مع وجهة النظر. يمكن استخدامها في الاستعلامات مع الجداول ، بما في ذلك لبناء طرق عرض أخرى. طرق العرض مبنية على أساس الاستعلامات ، مما يسمح لك بتسخير القوة والمرونة الكاملة لـ SQL. في هذا الطريق،من الجداول وطرق العرض ، يمكنك بناء نموذج مجال معقد إلى حد ما ومتعدد المستويات بالكامل بأسلوب تعريفي. وكل ما لا يتناسب بشكل جيد مع الأسلوب التعريفي يمكن تنفيذه باستخدام الإجراءات والوظائف.



المشكلة الرئيسية هي أن تعليمات PL / SQL البرمجية يتم تنفيذها حصريًا على جانب خادم قاعدة البيانات. هذا يجعل من الصعب توسيع نطاق مثل هذا الحل. بالإضافة إلى ذلك ، سيكون النموذج الناتج مرتبطًا بشكل صارم بقاعدة البيانات العلائقية وسيكون من الصعب تضمين بيانات من مصادر أخرى فيه.



استعلام لغة متكامل



يعد الاستعلام المتكامل للغة (LINQ) مكونًا شائعًا في النظام الأساسي .NET يسمح لك بتضمين تعبيرات استعلام SQL بشكل طبيعي في رمز اللغة الرئيسي الموجه للكائنات. على عكس PL / SQL ، الذي يضيف نموذجًا ضروريًا إلى SQL على جانب خادم قاعدة البيانات ، يقوم LINQ بإحضار SQL إلى مستوى التطبيق. بفضل هذا ، يمكن استخدام الاستعلامات في LINQ للحصول على البيانات ليس فقط من قواعد البيانات العلائقية ، ولكن أيضًا من مجموعات الكائنات ووثائق XML واستعلامات LINQ الأخرى.



تتميز بنية LINQ بأنها مرنة للغاية ويتم دمج تعريفات الاستعلام بعمق مع نموذج OOP. يتيح لك LINQ إنشاء موفري خدمات خاصين بك للوصول إلى مصادر بيانات جديدة. يمكنك أيضًا تعيين طريقتك الخاصة في تنفيذ الاستعلام ، وعلى سبيل المثال ، تحويل شجرة تعبير LINQ للاستعلام إلى استعلام إلى مصدر البيانات المطلوب. يمكنك استخدام تعبيرات lambda والوظائف المحددة في رمز التطبيق الخاص بك في نص الطلب الخاص بك. صحيح ، في حالة LINQ to SQL ، سيتم تنفيذ الاستعلام على جانب خادم قاعدة البيانات ، حيث لن تكون هذه الوظائف متاحة ، ولكن يمكن استخدام الإجراءات المخزنة بدلاً من ذلك. الطلب هو جوهر لغة المستوى الأول ، يمكنك التعامل معه كما هو الحال مع كائن عادي. يمكن للمترجم أن يستنتج تلقائيًا نوع نتيجة الاستعلام وإنشاء الفئة المناسبة ، حتى لو لم يتم التصريح عنها صراحة.



دعنا نحاول استخدام LINQ لبناء نموذج مجال كمجموعة من الاستعلامات. يمكن وضع الحقائق الأولية في قوائم على جانب التطبيق أو في جداول على جانب قاعدة البيانات ، ويمكن تنسيق المفاهيم المجردة كاستعلامات LINQ. يتيح لك LINQ إنشاء استعلامات استنادًا إلى استعلامات أخرى عن طريق تحديدها في عبارة FROM. يسمح لك هذا ببناء مفهوم جديد بناءً على المفاهيم الموجودة ، وستتوافق الحقول الموجودة في قسم SELECT مع سمات المفهوم. وسيحتوي قسم WHERE على التبعيات بين المفاهيم. سيبدو مثال مع فواتير من منشور سابق بهذا الشكل.



سنضع كائنات مع الحسابات ومعلومات العملاء في القوائم:



List<Bill> bills = new List<Bill>() { ... };
List<Client> clients = new List<Client>() { ... };


وبعد ذلك سنقوم ببناء استفسارات لهم للحصول على الفواتير غير المدفوعة والمدينين:



IEnumerable<Bill> unpaidBillsQuery =
from bill in bills
where bill.AmountToPay > bill.AmountPaid 
select bill;
IEnumerable<Client> debtorsQuery =
from bill in unpaidBillsQuery 
join client in clients on bill.ClientId equals client.ClientId
select client;


لقد اتخذ نموذج المجال الذي تم تنفيذه باستخدام LINQ شكلاً غريبًا إلى حد ما - حيث يتم تضمين أنماط برمجة متعددة. يحتوي المستوى الأعلى من النموذج على دلالات حتمية. يمكن تمثيلها كسلسلة من تحولات الكائن ، وبناء مجموعات من الكائنات فوق المجموعات. كائنات الاستعلام هي عناصر من عالم OOP. يجب إنشاؤها ، وتخصيصها للمتغيرات ، ويجب تمرير المراجع إليها إلى طلبات أخرى. في المستوى الأوسط ، ينفذ كائن الاستعلام الإجراء الخاص بتنفيذ استعلام ، والذي يتم تخصيصه وظيفيًا باستخدام تعبيرات lambda التي تسمح لك بتكوين بنية النتيجة في قسم SELECT وتصفية السجلات في جملة WHERE. يتم تمثيل المستوى الداخلي من خلال إجراء تنفيذ الاستعلام الذي يحتوي على دلالات منطقية ويعتمد على الجبر العلائقي.



على الرغم من أن LINQ جعل من الممكن وصف نموذج المجال ، إلا أن بنية SQL تهدف في المقام الأول إلى جلب البيانات ومعالجتها. يفتقر إلى بعض التركيبات التي قد تكون مفيدة في النمذجة. إذا تم تمثيل بنية المفاهيم الأساسية في PL / SQL بشكل واضح للغاية في شكل جداول وطرق عرض ، ثم في LINQ اتضح أنه تم تحويله إلى كود OOP. بالإضافة إلى ذلك ، بينما يمكن الإشارة إلى الجداول وطرق العرض بالاسم ، يمكن الإشارة إلى استعلامات LINQ بأسلوب أمر. بالإضافة إلى ذلك ، فإن SQL مقيدة بالنموذج العلائقي ولديها قدرات محدودة عند العمل مع الهياكل في شكل الرسوم البيانية أو الأشجار.



أوجه الشبه بين النموذج العلائقي والبرمجة المنطقية



قد تلاحظ أن تطبيقات SQL و Prolog للنموذج لها أوجه تشابه. في SQL ، نبني طريقة عرض تستند إلى جداول أو وجهات نظر أخرى ، وفي Prolog نبني قواعد تستند إلى الحقائق والقواعد. في SQL ، تعد الجداول مجموعة من الحقول ، والمسندات في Prolog هي مجموعة من السمات. في SQL ، نحدد التبعيات بين حقول الجدول كتعبيرات في جملة WHERE ، وفي Prolog ، باستخدام المسندات والمتغيرات المنطقية التي تربط السمات الأصلية ببعضها البعض. في كلتا الحالتين ، قمنا بتعيين مواصفات الحل بشكل إعلاني ، ويعيد محرك تنفيذ الاستعلام المدمج الإدخالات الموجودة في SQL أو القيم المحتملة للمتغيرات في Prolog إلينا.



هذا التشابه ليس عرضيًا. على الرغم من أن الأساس النظري لـ SQL - تم تطوير الجبر العلائقي بالتوازي مع البرمجة المنطقية ، ولكن فيما بعد تم الكشف عن اتصال نظري بينهما. لديهم أساس رياضي مشترك - منطق الدرجة الأولى. يصف نموذج البيانات العلائقية قواعد بناء العلاقات بين جداول البيانات والبرمجة المنطقية - بين العبارات. تستخدم كلتا النظريتين مصطلحات مختلفة ، يتم تطبيقها في مجالات مختلفة ، تم تطويرهما بالتوازي ، لكن كان لهما أساس رياضي مشترك.



بالمعنى الدقيق للكلمة ، حساب التفاضل والتكامل العلائقي هو تكييف لمنطق الدرجة الأولى للعمل مع البيانات الجدولية. تمت مناقشة هذا السؤال بمزيد من التفصيل هنا.... وهذا يعني أن أي تعبير عن الجبر العلائقي (أي استعلام SQL) يمكن إعادة صياغته في تعبير لمنطق الدرجة الأولى ، ثم تنفيذه في Prolog. لكن ليس العكس. حساب التفاضل والتكامل هو مجموعة فرعية من منطق الدرجة الأولى. هذا يعني أنه بالنسبة لبعض أنواع العبارات المقبولة في منطق الدرجة الأولى ، لا يمكننا العثور على تشابهات في الجبر العلائقي. على سبيل المثال ، إمكانيات الاستعلامات العودية في SQL محدودة للغاية ، كما أن بناء العلاقات متعدية ليس متاحًا دائمًا. تعتبر عمليات Prolog مثل الانفصال المستهدف والنفي مثل الرفض أكثر صعوبة في التنفيذ في SQL. تمنحك بنية Prolog المرنة مزيدًا من المرونة للعمل مع الهياكل المتداخلة المعقدة وتدعم عمليات مطابقة الأنماط عليها.هذا يجعل الأمر مناسبًا عند العمل مع هياكل البيانات المعقدة مثل الأشجار والرسوم البيانية.



لكن عليك أن تدفع مقابل كل شيء. تعد خوارزميات تنفيذ الاستعلام المضمنة في قواعد البيانات العلائقية أبسط وأقل تنوعًا من خوارزميات الاستدلال في Prolog. هذا يجعل من الممكن تحسينها وتحقيق أداء أعلى بكثير. Prolog غير قادر أيضًا على معالجة ملايين الصفوف في قواعد البيانات العلائقية بسرعة. بالإضافة إلى ذلك ، لا تضمن خوارزمية الاستدلال في Prolog نهاية تنفيذ البرنامج على الإطلاق - يمكن أن يؤدي إخراج بعض العبارات إلى تكرار لا نهائي.



بالمناسبة ، عند تقاطع قواعد البيانات والبرمجة المنطقية ، هناك أيضًا تقنية مثل قواعد البيانات الاستنتاجية ولغة القواعد والاستعلامات الخاصة بهم كتالوج البيانات. بدلاً من السجلات في الجداول ، تقوم قواعد البيانات الاستنتاجية بتخزين كميات كبيرة من الحقائق والقواعد بأسلوب منطقي. تبدو Datalog مثل Prolog ، لكنها تركز على العمل مع الحقائق مجتمعة في مجموعات ، وليس حقائق فردية. بالإضافة إلى ذلك ، تم قطع بعض ميزات منطق الدرجة الأولى من أجل تحسين خوارزمية الاستدلال للعمل السريع مع كميات كبيرة من البيانات. لذا فإن الصيغة الأقل تعبيرا للغة المنطقية لها مزاياها أيضًا.



نهج تصريحي لوصف طبقة API



يربط SQL مبنى النموذج بطبقة الوصول إلى البيانات. لكن البرمجة التعريفية تتطور بنشاط في الطرف الآخر من التطبيق - في طبقة API. تكمن خصوصيتها في أن المعلومات حول بنية الطلبات يجب أن تكون متاحة لأولئك الذين يستخدمون واجهة برمجة التطبيقات هذه. يعد وجود وصف رسمي لهيكل الطلبات والاستجابات نموذجًا جيدًا. وفقًا لذلك ، هناك رغبة في مزامنة هذا الوصف مع رمز التطبيق ، على سبيل المثال ، إنشاء فئات الطلب والاستجابة بناءً عليه. حيث ستحتاج بعد ذلك إلى كتابة منطق معالجة الطلبات.



GraphQL عبارة عن إطار عمل لبناء واجهات برمجة تطبيقات يتجاوز هذا النهج التقليدي ، ولا يقدم لغة استعلام فحسب ، بل يوفر أيضًا بيئة تنفيذ استعلام. ليست هناك حاجة لإنشاء رمز ، فوقت التشغيل يفهم أوصاف الطلبات على أي حال. لتنفيذ واجهة برمجة التطبيقات باستخدام GraphQL ، تحتاج إلى:



  1. وصف أنواع البيانات (كائنات) التطبيق التي تشكل جزءًا من الطلبات والاستجابات ؛
  2. وصف هيكل الطلبات والردود ؛
  3. تنفيذ الوظائف التي تنفذ منطق إنشاء الكائنات للحصول على قيم حقولهم.


أنواع البيانات هي أوصاف لحقول الكائن. يتم دعم الأنواع مثل الأنواع القياسية والقوائم والتعداد والمراجع للأنواع المتداخلة. نظرًا لأن حقول النوع يمكن أن تحتوي على مراجع لأنواع أخرى ، يمكن تمثيل مخطط البيانات بأكمله كرسم بياني. الطلب هو وصف لهيكل البيانات المطلوب من API. يتضمن وصف الطلب قائمة بالعناصر المطلوبة وحقولها وسمات الإدخال. يجب أن يقترن كل نوع بيانات وكل حقل من حقوله بوظيفة محلل. يصف محلل النوع (الكائن) الخوارزمية للحصول على كائناته ، ويصف محلل المجال قيم حقل الكائن. أنها تمثل وظائف في واحدة من اللغات الوظيفية أو وجوه المنحى. يتلقى وقت تشغيل GraphQL طلبًا ، ويحدد أنواع البيانات المطلوبة ، ويستدعي أدوات حلها ، بما في ذلك سلسلة من الكائنات المتداخلة ،يجمع كائن استجابة.



تجمع GraphQL بين وصف مخطط البيانات التقريري وخوارزميات إلزامية أو وظيفية للحصول عليها. يتم وصف مخطط البيانات بشكل صريح وهو أساسي للتطبيق. يشير العديد من الأشخاص إلى أنه من الممارسات الجيدة إنشاء مخطط بيانات لا يكرر مخططات مصدر البيانات ، ولكنه يتوافق مع نموذج المجال. هذا يجعل GraphQL حلاً شائعًا لدمج مصادر البيانات المتباينة.



وبالتالي ، تسمح لك لغة GraphQL بالتعبير عن نموذج المجال بطريقة واضحة إلى حد ما ، لتمييزه عن بقية الكود ، لتقريب النموذج وتنفيذه معًا. لسوء الحظ ، يقتصر المكون التصريحي للغة فقط على وصف تكوين أنواع البيانات ؛ يجب تنفيذ جميع العلاقات الأخرى بين عناصر النموذج باستخدام أدوات الحل. من ناحية أخرى ، تسمح أدوات الحل للمطور بتنفيذ أي طريقة للحصول على بيانات كائن وأي علاقة بينهما بشكل مستقل. ولكن ، من ناحية أخرى ، سيتعين عليك محاولة تنفيذ خيارات استعلام أكثر تعقيدًا من ، على سبيل المثال ، الوصول إلى سجل عن طريق المفتاح. من ناحية أخرى ، يوضح مخطط البيانات في GraphQL بوضوح العلاقة بين طبقة واجهة برمجة التطبيقات وطبقة الوصول إلى البيانات. ولكن ، من ناحية أخرى ، فإن الطبقة الأمامية التي يرتبط بها مخطط البيانات هي طبقة API.يتم تعديل محتوى مخطط البيانات إليه ؛ ولن يحتوي على كيانات غير مشتركة في معالجة الطلبات. على الرغم من أن القوة التعبيرية للغة وصف البيانات GraphQL أدنى من هذه اللغات التعريفية الكاملة مثل SQL و Prolog ، فإن شعبية هذا الإطار تُظهر أن أدوات وصف النموذج التصريحي يمكن ويجب أن تكون جزءًا من لغات البرمجة الحديثة.



سوف ألخص



PL / SQL هي لغة ملائمة لوصف نموذج المجال في شكل جداول وطرق عرض ، ومنطق العمل معه. المكونات التصريحية والإجرائية متكاملة ومتكاملة بإحكام. المشكلة الرئيسية هي أن هذه اللغة مرتبطة ارتباطًا وثيقًا بموقع تخزين البيانات ، ولا يمكن تنفيذها إلا على جانب خادم قاعدة البيانات ، ومنطق تنفيذ الاستعلام يقتصر على نموذج البيانات العلائقية.



على جانب التطبيق ، يمكنك استخدام تقنيات مثل LINQ و GraphQL لوصف النموذج في شكل تعريفي. باستخدام مخطط بيانات GraphQL ، يمكنك وصف بنية نموذج المجال بوضوح ووضوح شديد ، وتداخل مفاهيمه. ووقت التشغيل قادر على جمع العناصر المطلوبة تلقائيًا. لسوء الحظ ، يجب تنفيذ جميع العلاقات والصلات الأخرى بين المفاهيم ، باستثناء تداخلها ، في طبقة وظائف المحلل. LINQ له مزايا وعيوب معاكسة. تمنحك بنية SQL المرنة مزيدًا من المرونة لوصف العلاقات بين المفاهيم. ولكن خارج الطلب ، ينتهي الإعلان ، وكائنات الطلب هي عناصر من عالم OOP. يجب إنشاؤها ، وتخصيصها للمتغيرات ، واستخدامها بأسلوب الأمر.



أرغب في الجمع بين مزايا كل من LINQ و GraphQL. بحيث يكون وصف بنية المفاهيم واضحًا كما هو الحال في GraphQL ، ويمكن تعيين العلاقات بينها بناءً على المنطق كما في SQL. وهكذا تكون تعريفات المفاهيم متاحة مباشرة بالاسم كفئة ، دون الحاجة إلى إنشاء كائناتها بشكل صريح ، وتخصيصها للمتغيرات ، وتمرير المراجع إليها ، وما إلى ذلك.



سأبدأ في تصميم مثل هذا الحل من خلال تطوير لغة لوصف نموذج المجال. ولكن لهذا من الضروري إجراء نظرة عامة على اللغات الحالية لتمثيل المعرفة. لذلك ، في المنشور التالي أريد أن أتحدث عن البرمجة المنطقية ، RDF ، OWL ولغات منطق الإطار ، ومقارنتها ومحاولة العثور على مثل هذه الميزات التي ستكون مثيرة للاهتمام للغة المصممة لوصف منطق الأعمال.



بالنسبة لأولئك الذين لا يرغبون في انتظار إصدار جميع المنشورات على Habré ، هناك نص كامل بأسلوب علمي باللغة الإنجليزية ، متاح على الرابط: Hybrid Ontology-Oriented Programming for Semi-Structured Data Processing .



روابط للمنشورات السابقة:

تصميم لغة برمجة متعددة النماذج. الجزء 1 - ما الغرض منه؟



All Articles