ليست لغة برمجة أخرى. الجزء 3: الفيزياء



الجزء الثالث والأخير من سلسلة من المقالات حول لغة lsFusion (روابط إلى الجزأين الأول والثاني )

سوف يركز على النموذج المادي: كل ما لا يرتبط بوظيفة النظام ، ولكن يرتبط بتطويره وتحسين أدائه ، عندما يكون هناك الكثير من البيانات.

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

في هذه المقالة ، سنفعل بدون صورة (ليس هناك مكدس مثل هذا) ، لكننا سنقوم بعمل جدول محتويات ، كما هو مطلوب في المقالات السابقة:


تحديد البند


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

  • مساحات الأسماء - فصل الاسم إلى كامل وقصير ، والقدرة على استخدام اسم قصير فقط عند الوصول إلى عنصر
  • الكتابة الصريحة (على نحو أكثر دقة ، التحميل الزائد للوظائف) - القدرة على تسمية الخصائص (والإجراءات) بالطريقة نفسها ، ثم ، عند الوصول إليها ، اعتمادًا على فئات الوسائط ، تحدد تلقائيًا الخاصية التي سيتم استدعاء المكالمة

النطاقات


يتكون أي مشروع معقد عادة من عدد كبير من العناصر التي يجب تسمية. وإذا تقاطع نطاقات المجال ، فغالبًا ما تكون هناك حاجة لاستخدام نفس الاسم في سياقات مختلفة. على سبيل المثال ، لدينا اسم فئة أو نموذج فاتورة (فاتورة) ، ونريد استخدام هذا الاسم في كتل وظيفية مختلفة ، على سبيل المثال: شراء (شراء) ، بيع (بيع) ، عودة الشراء (PurchaseReturn) ، Return of sale (SaleReturn). من الواضح أنه يمكن تسمية الفئات / النماذج PurchaseInvoice و SaleInvoice وما إلى ذلك. لكن أولاً ، ستكون هذه الأسماء بحد ذاتها ضخمة للغاية. وثانيا ، في كتلة واحدة وظيفية ، المكالمات ، كقاعدة عامة ، انتقل إلى عناصر نفس الكتلة الوظيفية ، مما يعني أنه عند تطوير ، على سبيل المثال ، كتلة وظيفة الشراء من التكرار المستمر للكلمة "شراء" ، فإنها ببساطة سوف تموج في عينيك. لمنع حدوث ذلك ، لدى النظام الأساسي مفهوم مثل مساحة الاسم. يعمل على النحو التالي:

  • يتم إنشاء كل عنصر في النظام الأساسي في بعض مساحة الاسم
  • إذا كانت عملية إنشاء عنصر ، تتم الإشارة إلى عناصر أخرى ، وتكون الأسبقية للعناصر التي تم إنشاؤها في نفس مساحة الاسم
MODULE PurchaseInvoice;
NAMESPACE Purchase;
CLASS Invoice ' ()' ;
MODULE SaleInvoice;
NAMESPACE Sale;
CLASS Invoice ' ()' ;
MODULE PurchaseShipment;
REQUIRE PurchaseInvoice, SaleInvoice;
NAMESPACE Purchase;
// Invoice Purchase.Invoice, Sale.invoice
// namespace Purchase namespace Purchase
shipment(Invoice invoice) = AGGR ShipmentInvoce WHERE createShipment(invoice);
يتم تعيين مساحات الأسماء في إصدار اللغة الحالي للوحدة بأكملها مباشرةً في رأس الوحدة النمطية. بشكل افتراضي ، إذا لم يتم تحديد مساحة اسم ، فسيتم إنشاؤه ضمنيًا مع اسم يساوي اسم الوحدة النمطية. إذا كنت بحاجة إلى الوصول إلى عنصر من مساحة اسم غير ذات أولوية ، يمكنك القيام بذلك عن طريق تحديد الاسم الكامل للعنصر (على سبيل المثال ، Sale.Invoice).

كتابة صريحة


تعتبر مساحات الأسماء مهمة ، ولكنها ليست الطريقة الوحيدة لجعل الشفرة أقصر وأكثر قابلية للقراءة. بالإضافة إلى ذلك ، عند البحث عن الخصائص (والإجراءات) ، من الممكن أيضًا مراعاة فئات الوسائط التي تم تمريرها إليها عند الإدخال. لذلك على سبيل المثال:
sum = DATA NUMERIC [ 10 , 2 ] (OrderDetail);
sum = GROUP SUM sum(OrderDetail od) BY order(od);
// , Order
// OrderDetail
CONSTRAINT sum(Order o) < 0 MESSAGE ' ' ;
هنا ، بالطبع ، قد يطرح السؤال التالي: ما الذي سيحدث إذا لم تكن مساحة اسم العقار المرغوب فيه أولوية ، لكنها مناسبة بشكل أفضل للفصول؟ في الواقع ، خوارزمية البحث العامة معقدة للغاية (وصفها كاملاً هنا ) وهناك الكثير من الحالات "الغامضة" ، لذلك في حالة عدم اليقين ، يوصى إما بتحديد مساحات / فئات أسماء الخاصية المطلوبة بشكل صريح ، أو التحقق مرة أخرى في IDE (باستخدام الانتقال إلى الإعلان - CTRL + B) أن الخاصية التي تم العثور عليها هي بالضبط المقصود.

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

نمطية


تعد Modularity واحدة من أهم خصائص النظام ، حيث تسمح بضمان القابلية للتوسعة ، وإعادة استخدام الكود ، بالإضافة إلى التفاعل الفعال لفريق التطوير.

يوفر LsFusion نمطية مع آليات اثنين التالية:

  • الامتدادات - القدرة على توسيع (تغيير) عناصر النظام بعد إنشائها.
  • الوحدات النمطية - القدرة على تجميع بعض الوظائف معًا لإعادة استخدامها.

توسع


يدعم lsFusion القدرة على تمديد الطبقات والنماذج ، وكذلك الخصائص والإجراءات ، من خلال آلية تعدد الأشكال الموصوفة في المقالة الأولى.
sum = DATA NUMERIC [ 10 , 2 ] (OrderDetail);
sum = GROUP SUM sum(OrderDetail od) BY order(od);
// , Order
// OrderDetail
CONSTRAINT sum(Order o) < 0 MESSAGE ' ' ;
CLASS ABSTRACT Shape;
CLASS Box : Shape;

CLASS Quadrilateral;
EXTEND CLASS Box : Quadrilateral; //

CLASS ShapeType {
point '' ,
segment ''
}

EXTEND CLASS ShapeType { //
circle ''
}

CLASS ItemGroup;
name = DATA ISTRING [ 100 ] (ItemGroup);

itemGroup = DATA ItemGroup (Item);

EXTEND FORM items
PROPERTIES (i) NEWSESSION DELETE //

OBJECTS g = ItemGroup BEFORE i //
PROPERTIES (g) READONLY name
FILTERS itemGroup(i) == g // , ,
;
أيضًا ، نلاحظ أن جميع تصميمات المنصة الأخرى تقريبًا (على سبيل المثال ، المستكشف وتصميم النماذج) قابلة للتمديد حسب التعريف ، وبالتالي لا يوجد منطق امتداد منفصل لها.

وحدات


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

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

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

التبعيات بين الوحدات هي متعدية. بمعنى ، إذا كانت الوحدة C تعتمد على الوحدة B ، وتعتمد الوحدة B على الوحدة A ، عندئذٍ تعتبر الوحدة C تعتمد أيضًا على الوحدة A.

تعتمد أي وحدة دائمًا تلقائيًا على نظام وحدة النظام ، بغض النظر عما إذا كان يتم الإشارة إليها صراحة أم لا.
MODULE EmployeeExample; //

REQUIRE Authentication, Utils; // , Employee
NAMESPACE Employee; //

CLASS Employee '' ; //
CLASS Position '' ; //

employeePosition(employee) = DATA Position (Employee); //

metaprogramming


Metaprogramming هو نوع من البرامج المرتبطة بكتابة رمز البرنامج ، والذي ينتج عنه نتيجة لذلك رمز برنامج آخر. LsFusion يستخدم metacodes يسمى metaprogramming.

يتكون الكوداك من:

  • اسم ميتاكود
  • معلمات metacode
  • نص رمز metacode - كتلة تعليمات برمجية تتكون من التعريفات و / أو امتدادات عناصر النظام (الخصائص ، الإجراءات ، الأحداث ، الرموز الأخرى ، إلخ)

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

إعلان:
META addActions(formName)
EXTEND FORM formName
PROPERTIES () showMessage, closeForm
;
END
استخدام:
@addActions (documentForm);
@addActions (orderForm);
الرمز الناتج:
EXTEND FORM documentForm
PROPERTIES () showMessage, closeForm
;
EXTEND FORM orderForm
PROPERTIES () showMessage, closeForm
;
بالإضافة إلى استبدال معلمات metacode ببساطة ، فإن النظام الأساسي يسمح لك أيضًا بدمج هذه المعلمات مع المعرفات الحالية / سلسلة الأحرف (أو مع بعضها البعض) ، على سبيل المثال:

إعلان:
META objectProperties(object, caption)
object ## Name ' ' ## caption = DATA BPSTRING [ 100 ](object);
object ## Type ' ' ## caption = DATA Type (object);
object ## Value ' ' ## caption = DATA INTEGER (object);
END
استخدام:
@objectProperties (document, '' );
الرمز الناتج:
DocumentName ' ' = DATA BPSTRING [ 100 ](Document);
DocumentType ' ' = DATA Type (Document);
DocumentValue ' ' = DATA INTEGER (Document);
تشبه الأكواد إلى حد كبير وحدات الماكرو في C ، ولكن على عكس الأخير ، فإنها لا تعمل على مستوى النص (لا يمكنها ، على سبيل المثال ، تمرير الكلمات الرئيسية في المعلمة) ، ولكن فقط على مستوى المعرفات / سلسلة الأحرف (يسمح هذا التقييد ، على وجه الخصوص ، تحليل نص metacode في IDE).

في lsFusion ، تحل برامج metacodes المشكلات المشابهة للأدوية في Java (اجتياز الفئات كمعلمات) و lambda في FP (تمرير الوظائف كمعلمات) ، ومع ذلك ، فإنها لا تفعل ذلك بشكل جميل للغاية. لكن ، من ناحية أخرى ، يفعلون ذلك في حالة أكثر عمومية (أي ، على سبيل المثال ، مع إمكانية الجمع بين المعرفات ، واستخدامها في أي الإنشاءات النحوية - النماذج ، التصميمات ، المستكشف ، إلخ)

لاحظ أن "نشر" التعريفات لا يدعم فقط في النظام الأساسي نفسه ، ولكن أيضًا في IDE. لذلك ، في IDE يوجد وضع خاص Enable meta ، والذي يولد الكود الناتج مباشرة في المصادر وبالتالي يسمح لهذا الكود الذي تم إنشاؤه بالمشاركة في البحث عن الاستخدامات ، الإكمال التلقائي ، إلخ. في هذه الحالة ، إذا تغير نص رمز metacode ، فإن IDE يقوم تلقائيًا بتحديث جميع استخدامات رمز التعريف هذا.



أيضًا ، يمكن استخدام الرموز الأولية للتلقائية فقط ، ولكن أيضًا لإنشاء الشفرة اليدوية (كقوالب). للقيام بذلك ، يكفي أن تكتببدلاً من one @ مباشرة وبعد إدخال سلسلة استخدام metacode بالكامل (حتى الفاصلة المنقوطة) ، سوف يستبدل IDE استخدام metacode هذا بالكود الذي تم إنشاؤه بواسطة هذا metacode:



التكامل


يتضمن التكامل كل ما يتعلق بتفاعل نظام lsFusion مع الأنظمة الأخرى. من وجهة نظر اتجاه هذا التفاعل ، يمكن تقسيم التكامل إلى:

  • الوصول إلى نظام lsFusion من نظام آخر.
  • الوصول من نظام lsFusion إلى نظام آخر.

من وجهة نظر النموذج المادي ، يمكن تقسيم التكامل إلى:

  • التفاعل مع الأنظمة التي تعمل في "نفس البيئة" مثل نظام lsFusion (أي ، في Java Virtual Machine (JVM) لخادم lsFusion و / أو باستخدام خادم SQL نفسه كنظام lsFusion).
  • التفاعل مع الأنظمة البعيدة عبر بروتوكولات الشبكة.

وفقا لذلك ، سيتم استدعاء النظم الأولى الداخلية ، والثانية - الخارجية.

وبالتالي ، هناك أربعة أنواع مختلفة من التكامل في المنصة:

  • نداء إلى نظام خارجي
  • نداء من نظام خارجي
  • نداء إلى النظام الداخلي
  • نداء من النظام الداخلي

نداء إلى نظام خارجي


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

حاليًا ، تدعم المنصة الأنواع التالية من التفاعلات / الأنظمة الخارجية:

HTTP - ينفذ طلب http من خادم ويب.

بالنسبة لهذا النوع من التفاعل ، يجب عليك تحديد سلسلة استعلام (URL) ، والتي تحدد في الوقت نفسه عنوان الخادم والطلب الذي يجب تنفيذه. يمكن نقل المعلمات في سطر الاستعلام (للوصول إلى المعلمة ، يتم استخدام الحرف الخاص $ ورقم هذه المعلمة ، بدءًا من 1) ، وكذلك في النص الأساسي (BODY). من المفترض أن يتم تمرير جميع المعلمات غير المستخدمة في سلسلة الاستعلام إلى BODY. إذا كان هناك أكثر من معلمة واحدة في BODY ، فسيتم تعيين نوع محتوى BODY أثناء الإرسال على متعدد الأجزاء / مختلطة ، ويتم نقل المعلمات كمكونات في هذا BODY.

عند معالجة معلمات فئات الملفات (FILE ، PDFFILE ، إلخ) في BODY ، يتم تحديد نوع محتوى المعلمة اعتمادًا على امتداد الملف (وفقًا للجدول التالي). إذا لم يكن امتداد الملف في هذا الجدول ، فسيتم تعيين نوع المحتوى على التطبيق / <امتداد الملف>.

إذا لزم الأمر ، باستخدام الخيار الخاص (HEADERS) ، يمكنك تعيين رؤوس الطلب المنفذة. للقيام بذلك ، تحتاج إلى تحديد خاصية ذات معلمة واحدة فقط من فئة السلسلة التي سيتم تخزين العنوان فيها ، وقيمة فئة السلسلة التي سيتم تخزين قيمة هذا الرأس فيها.

تتم معالجة نتيجة طلب http بنفس طريقة معاملته ، فقط في الاتجاه المعاكس: على سبيل المثال ، إذا كان نوع محتوى النتيجة إما موجودًا في الجدول التالي أو مساوًا للتطبيق / * ، فسيعتبر أن النتيجة التي تم الحصول عليها هي ملف ويجب كتابتها إلى خاصية ذات قيمة FILE . تتم معالجة رؤوس نتيجة طلب http عن طريق القياس مع رؤوس هذا الطلب نفسه (مع الاختلاف الوحيد هو أن الخيار يسمى HEADERSTO ، وليس HEADERS).
EXTERNAL HTTP GET 'https://www.cs.cmu.edu/~chuck/lennapg/len_std.jpg' TO exportFile;
open(exportFile());

LOCAL headers = STRING ( STRING );
headers( 'Authentication : Bearer' ) <- 'd43ks43ds343dd233' ';
EXTERNAL HTTP 'http://tryonline.lsfusion.org/exec?action=getExamples'
HEADERS headers
HEADERSTO headers
PARAMS JSONFILE ( '\{"mode"=1\}' )
TO exportFile;

IMPORT FROM exportFile() FIELDS () STRING caption, STRING code DO
MESSAGE 'Example : ' + caption + ', code : ' + code;
FOR v = headers( STRING s) DO
MESSAGE 'Result Header is : Key - ' + s + ', Value - ' + v;
SQL - تنفيذ أمر خادم SQL.

بالنسبة لهذا النوع من التفاعل ، يتم تحديد سلسلة الاتصال وأوامر (أوامر) SQL المراد تنفيذها. يمكن تمرير المعلمات في سلسلة الاتصال وفي أمر SQL. للوصول إلى المعلمة ، يتم استخدام الحرف الخاص $ ورقم هذه المعلمة (بدءًا من 1).

يمكن استخدام معلمات فئات الملفات (FILE ، PDFFILE ، وما إلى ذلك) فقط في أمر SQL. في الوقت نفسه ، إذا كان أي من المعلمات أثناء التنفيذ هو ملف TABLE (TABLEFILE أو FILE مع ملحق الجدول) ، فإن هذه المعلمة تعتبر جدولًا في هذه الحالة أيضًا:

  • قبل تنفيذ أمر SQL ، يتم تحميل قيمة كل معلمة كهذه على الخادم في جدول مؤقت
  • عند استبدال المعلمات ، لا يتم استبدال قيمة المعلمة نفسها ، ولكن اسم الجدول المؤقت الذي تم إنشاؤه

نتائج التنفيذ هي: لاستعلامات DML - أرقام تساوي عدد السجلات التي تمت معالجتها ، لاستعلامات SELECT - ملفات تنسيق TABLE (FILE مع جدول التمديد) تحتوي على نتائج هذه الاستعلامات. يتزامن ترتيب هذه النتائج مع ترتيب تنفيذ الاستعلامات المقابلة في أمر SQL.
externalSQL () {
EXPORT TABLE FROM bc=barcode(Article a) WHERE name(a) LIKE '%%' ; // -
EXTERNAL SQL 'jdbc:mysql://$1/test?user=root&password='
EXEC 'select price AS pc, articles.barcode AS brc from $2 x JOIN articles ON x.bc=articles.barcode'
PARAMS 'localhost' ,exportFile()
TO exportFile; // -

// -
LOCAL price = INTEGER ( INTEGER );
LOCAL barcode = STRING [ 30 ] ( INTEGER );
IMPORT FROM exportFile() TO price=pc,barcode=brc;
FOR barcode(Article a) = barcode( INTEGER i) DO
price(a) <- price(i);
}
LSF - استدعاء إجراء لخادم lsFusion آخر.

بالنسبة لهذا النوع من التفاعل ، يتم تعيين سلسلة الاتصال بخادم lsFusion (أو خادم الويب الخاص به ، إن وجد) ، والإجراء الذي يتعين تنفيذه ، وكذلك قائمة الخصائص (بدون معلمات) ، التي سيتم فيها كتابة نتائج المكالمة. يجب أن تتزامن المعلمات المراد نقلها مع العدد والفئة مع معلمات الإجراء الذي يتم تنفيذه.

تتوافق طريقة تعيين الإجراء في هذا النوع من التفاعل تمامًا مع طريقة تعيين الإجراء عند الوصول من نظام خارجي (حول هذا النوع من الوصول في القسم التالي).
externalLSF() {
EXTERNAL LSF 'http://localhost:7651' EXEC 'System.testAction[]' ;
}
بشكل افتراضي ، يتم تطبيق هذا النوع من التفاعل باستخدام بروتوكول HTTP باستخدام الواجهات المناسبة للوصول إلى / من نظام خارجي.

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

نداء من نظام خارجي


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

يمكن تعيين الإجراء المدعو بإحدى الطرق الثلاث التالية:

  • / exec؟ action = <اسم الإجراء> - يعين اسم الإجراء الذي تم استدعاؤه.
  • / eval؟ script = <code> - لتعيين الرمز في lsFusion. من المفترض أنه في هذا الكود ، يوجد إعلان بإجراء مع تشغيل الاسم ، وهذا هو الإجراء الذي سيتم استدعاؤه. إذا لم يتم تحديد معلمة البرنامج النصي ، فمن المفترض أن يتم تمرير الرمز كمعلمة أول BODY.
  • / eval / action؟ script = <code code> - يعين كود الإجراء في lsFusion. للوصول إلى المعلمات ، يمكنك استخدام الحرف الخاص $ ورقم المعلمة (بدءًا من 1).

في الحالتين الثانية والثالثة ، إذا لم يتم تحديد معلمة البرنامج النصي ، فمن المفترض أن يتم تمرير الكود بواسطة المعلمة BODY الأولى.

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

على سبيل المثال ، إذا كان لدينا إجراء:
importOrder( INTEGER no, DATE date, FILE detail) {
NEW o = FOrder {
no(o) <- no;
date(o) <- date;
LOCAL detailId = INTEGER ( INTEGER );
LOCAL detailQuantity = INTEGER ( INTEGER );
IMPORT FROM detail TO detailId, detailQuantity;
FOR imported( INTEGER i) DO {
NEW od = FOrderDetail {
id(od) <- detailId(i);
quantity(od) <- detailQuantity(i);
price(od) <- 5 ;
order(od) <- o;
}
}
APPLY ;
EXPORT JSON FROM price = price(FOrderDetail od), id = id(od) WHERE order(od) = o;
EXPORT FROM orderPrice(o), exportFile();
}
}
ثم يمكنك الوصول إليه باستخدام طلب POST والذي:
  • عنوان URL - http: // server_address / exec؟ الإجراء = importOrder & p = 123 & p = 2019-01-01
  • الجسم - ملف json مع سلاسل الاستعلام

مثال بايثون
 import json import requests from requests_toolbelt.multipart import decoder lsfCode = ("run(INTEGER no, DATE date, FILE detail) {\n" " NEW o = FOrder {\n" " no(o) <- no;\n" " date(o) <- date;\n" " LOCAL detailId = INTEGER (INTEGER);\n" " LOCAL detailQuantity = INTEGER (INTEGER);\n" " IMPORT JSON FROM detail TO detailId, detailQuantity;\n" " FOR imported(INTEGER i) DO {\n" " NEW od = FOrderDetail {\n" " id(od) <- detailId(i);\n" " quantity(od) <- detailQuantity(i);\n" " price(od) <- 5;\n" " order(od) <- o;\n" " }\n" " }\n" " APPLY;\n" " EXPORT JSON FROM price = price(FOrderDetail od), id = id(od) WHERE order(od) == o;\n" " EXPORT FROM orderPrice(o), exportFile();\n" " }\n" "}") order_no = 354 order_date = '10.10.2017' order_details = [dict(id=1, quantity=10), dict(id=2, quantity=15), dict(id=5, quantity=4), dict(id=10, quantity=18), dict(id=11, quantity=1), dict(id=12, quantity=3)] order_json = json.dumps(order_details) url = 'http://localhost:7651/eval' payload = {'script': lsfCode, 'no': str(order_no), 'date': order_date, 'detail': ('order.json', order_json, 'text/json')} response = requests.post(url, files=payload) multipart_data = decoder.MultipartDecoder.from_response(response) sum_part, json_part = multipart_data.parts sum = int(sum_part.text) data = json.loads(json_part.text) ############################################################## print(sum) for item in data: print('{0:3}: price {1}'.format(int(item['id']), int(item['price']))) ############################################################## # 205 # 4: price 5 # 18: price 5 # 3: price 5 # 1: price 5 # 10: price 5 # 15: price 5 

نداء إلى النظام الداخلي


هناك نوعان من التفاعل الداخلي:

جافا التشغيل المتداخل

يتيح لك هذا النوع من التفاعل الاتصال برمز Java داخل خادم JVM lsFusion. للقيام بذلك ، يجب عليك:

  • تأكد من أن فئة Java المترجمة يمكن الوصول إليها في classpath لخادم التطبيق. من الضروري أيضًا أن ترث هذه الفئة lsfusion.server.physics.dev.integration.internal.to.InternalAction.
    مثال فئة جافا
     import lsfusion.server.data.sql.exception.SQLHandledException; import lsfusion.server.language.ScriptingErrorLog; import lsfusion.server.language.ScriptingLogicsModule; import lsfusion.server.logics.action.controller.context.ExecutionContext; import lsfusion.server.logics.classes.ValueClass; import lsfusion.server.logics.property.classes.ClassPropertyInterface; import lsfusion.server.physics.dev.integration.internal.to.InternalAction; import java.math.BigInteger; import java.sql.SQLException; public class CalculateGCD extends InternalAction { public CalculateGCD(ScriptingLogicsModule LM, ValueClass... classes) { super(LM, classes); } @Override protected void executeInternal(ExecutionContext<ClassPropertyInterface> context) throws SQLException, SQLHandledException { BigInteger b1 = BigInteger.valueOf((Integer)getParam(0, context)); BigInteger b2 = BigInteger.valueOf((Integer)getParam(1, context)); BigInteger gcd = b1.gcd(b2); try { findProperty("gcd[]").change(gcd.intValue(), context); } catch (ScriptingErrorLog.SemanticErrorException ignored) { } } } 
  • تسجيل إجراء باستخدام مشغل الاتصال الداخلي الخاص (INTERNAL)
    calculateGCD ' ' INTERNAL 'CalculateGCD' ( INTEGER , INTEGER );
  • يمكن استدعاء إجراء مسجل ، مثله مثل أي إجراء آخر ، باستخدام مشغل الاتصال. في هذه الحالة ، سيتم تنفيذ طريقة executeInternal (lsfusion.server.logics.action.controller.context.ExecutionContext) لفئة Java المحددة.
    //
    FORM gcd ''
    OBJECTS (a = INTEGER , b = INTEGER ) PANEL
    PROPERTIES 'A' = VALUE (a), 'B' = VALUE (b)

    PROPERTIES gcd(), calculateGCD(a, b)
    ;

    //
    run() {
    calculateGCD( 100 , 200 );
    }
تفاعل SQL

يتيح لك هذا النوع من التفاعل الوصول إلى بنيات الكائنات / بناء الجملة الخاصة بخادم SQL المستخدمة من قبل نظام lsFusion المطوَّر. لتنفيذ هذا النوع من التفاعل في المنصة ، يتم استخدام عامل خاص - FORMULA. يتيح لك هذا المشغل إنشاء خاصية تقيم بعض الصيغ بلغة SQL. يتم تحديد الصيغة كسلسلة ، يتم فيها استخدام الحرف الخاص $ ورقم هذه المعلمة للوصول إلى المعلمة (بدءًا من 1). وفقًا لذلك ، سيكون عدد معلمات الخاصية التي تم الحصول عليها مساويًا لأقصى عدد من المعلمات المستخدمة.
round(number, digits) = FORMULA 'round(CAST(($1) as numeric),$2)' ; // :
jumpWorkdays = FORMULA NULL DATE PG 'jumpWorkdays($1, $2, $3)' , MS 'dbo.jumpWorkdays($1, $2, $3)' ; // SQL
يوصى باستخدام هذا المشغل فقط في الحالات التي يتعذر فيها حل المشكلة بمساعدة المشغلين الآخرين ، وأيضًا إذا كان مضمونًا أي خوادم SQL محددة يمكن استخدامها ، أو أن تصميمات بناء الجملة المستخدمة تتوافق مع أحدث معايير SQL.

نداء من النظام الداخلي


كل شيء متماثل لنداء النظام الداخلي. هناك نوعان من التفاعل:

جافا التشغيل المتداخل

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

للوصول إلى عناصر Java لنظام lsFusion مباشرة ، تحتاج أولاً إلى الحصول على رابط لبعض الكائنات التي سيكون لها واجهات للعثور على عناصر Java هذه. عادة ما يتم ذلك بإحدى طريقتين:

  • إذا كانت المكالمة مبدئيًا تأتي من نظام lsFusion (من خلال الآلية الموضحة أعلاه) ، فعندئذٍ "ككائن بحث" يمكنك استخدام كائن الإجراء "من خلاله" هذه الدعوة (يجب أن يتم توريث فئة هذا الإجراء من lsfusion.server.physics.dev.integration. internal.to.InternalAction ، والذي بدوره يحتوي على جميع الواجهات الضرورية).
  • إذا كان الكائن الذي من خلاله من الضروري الوصول إلى نظام lsFusion هو فاصوليا Spring ، يمكن الحصول على رابط لعنصر منطق الأعمال باستخدام حقن التبعية (تسمى الفاصوليا businessLogics ، على التوالي).

مثال فئة جافا
 import lsfusion.server.data.sql.exception.SQLHandledException; import lsfusion.server.data.value.DataObject; import lsfusion.server.language.ScriptingErrorLog; import lsfusion.server.language.ScriptingLogicsModule; import lsfusion.server.logics.action.controller.context.ExecutionContext; import lsfusion.server.logics.classes.ValueClass; import lsfusion.server.logics.property.classes.ClassPropertyInterface; import lsfusion.server.physics.dev.integration.internal.to.InternalAction; import java.math.BigInteger; import java.sql.SQLException; public class CalculateGCDObject extends InternalAction { public CalculateGCDObject(ScriptingLogicsModule LM, ValueClass... classes) { super(LM, classes); } @Override protected void executeInternal(ExecutionContext<ClassPropertyInterface> context) throws SQLException, SQLHandledException { try { DataObject calculation = (DataObject)getParamValue(0, context); BigInteger a = BigInteger.valueOf((Integer)findProperty("a").read(context, calculation)); BigInteger b = BigInteger.valueOf((Integer)findProperty("b").read(context, calculation)); BigInteger gcd = a.gcd(b); findProperty("gcd[Calculation]").change(gcd.intValue(), context, calculation); } catch (ScriptingErrorLog.SemanticErrorException ignored) { } } } 

تفاعل SQL يمكن

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

لاحظ أن هذا التفاعل المباشر (ولكن للقراءة فقط) مناسب بشكل خاص للتكامل مع أنظمة OLAP المختلفة ، حيث يجب أن تحدث العملية برمتها مع الحد الأدنى من الحمل.

الهجرة


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

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

 V< > { 1 ... N } 

التغييرات ، بدورها ، من الأنواع التالية:
DATA PROPERTY oldNS.oldName[class1,...,classN] -> newNS.newName[class1,...,classN]
CLASS oldNS.oldName -> newNS.newName
OBJECT oldNS.oldClassName.oldName -> newNS.newClassName.newName

TABLE oldNS.oldName -> newNS.newName
PROPERTY oldNS.oldName[class1,...,classN] -> newNS.newName[class1,...,classN]
FORM PROPERTY oldNS.oldFormName.oldName(object1,...,objectN) -> newNS.newFormName.newName(object1,...,objectN)
NAVIGATOR oldNS.oldName -> newNS.newName
بالنسبة لترحيل بيانات المستخدم ، تكون الأنواع الثلاثة الأولى فقط من التغييرات ذات صلة (التغييرات في الخصائص الأساسية والفئات والكائنات الثابتة). هناك حاجة إلى الأنواع الأربعة المتبقية من التغييرات:

  • لترحيل البيانات التعريفية (سياسات الأمان ، إعدادات الجدول ، إلخ.)
  • لتحسين ترحيل بيانات المستخدم (حتى لا تعيد حساب المجموعات ولا تنقل البيانات بين الجداول مرة أخرى).

وفقًا لذلك ، إذا لم تكن هناك حاجة إلى ترحيل بيانات التعريف أو لم يكن هناك الكثير من البيانات ، فيمكن حذف هذه التغييرات في برنامج الترحيل.

مثال الهجرة
V0. 3.1 {
DATA PROPERTY Item.gender[Item.Article] -> Item.dataGender[Item.Article] // DATA
PROPERTY System.SIDProperty[Reflection.Property] -> Reflection.dbNameProperty[Reflection.Property] //
FORM PROPERTY Item.itemForm.name(i) -> Item.itemForm.itemName(i)
}

V0. 4 {
FORM PROPERTY Document.documentForm.name(i) -> Document.itemForm.itemName(i)
FORM PROPERTY Item.itemForm.itemName(i) -> Item.itemForm.iname // : iname = itemName(i)
CLASS Date.DateInterval -> Date.Interval
OBJECT Geo.Direction.North -> Geo.Direction.north
TABLE User.oldTable -> User.newTable
}


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

تدويل


في الممارسة العملية ، ينشأ الموقف في بعض الأحيان عندما يكون من الضروري أن تكون قادرًا على استخدام تطبيق واحد بلغات مختلفة. عادةً ما تنقسم هذه المهمة إلى ترجمة جميع بيانات السلسلة التي يراها المستخدم ، وهي: الرسائل النصية ، ورؤوس الممتلكات ، والإجراءات ، والنماذج ، إلخ. يتم تعيين كل هذه البيانات في lsFusion باستخدام حرفية السلسلة (السلاسل في علامات اقتباس مفردة ، على سبيل المثال "abc") ، على التوالي ، يتم تنفيذ تعريبها على النحو التالي:
  • بدلاً من النص المراد ترجمته ، تحدد السلسلة بيانات السلسلة ، والمحاطة بأقواس (على سبيل المثال ، '{button.cancel}').
  • عندما يتم إرسال هذه السلسلة إلى العميل على الخادم ، فإنه يبحث عن جميع المعرفات الموجودة في السلسلة ، ثم يبحث عن كل منهم في جميع ملفات مشروع ResourceBundle في الإعدادات المحلية المطلوبة (أي ، لغة العميل) ، وعندما يتم العثور على الخيار الصحيح ، يتم استبدال المعرف بين قوسين بالنص المقابل.
script '{scheduler.script.scheduled.task.detail}' = DATA TEXT (ScheduledTaskDetail);
CONSTRAINT script(ScheduledTaskDetail d) AND action(d) MESSAGE '{scheduler.constraint.script.and.action}' ;
FORM scheduledTask '{scheduler.form.scheduled.task}' ;
ServerResourceBundle.properties:
 scheduler.script.scheduled.task.detail=Script scheduler.constraint.script.and.action=In the scheduler task property and script cannot be selected at the same time scheduler.form.scheduled.task=Tasks 

ServerResourceBundle_ru.properties
 scheduler.script.scheduled.task.detail= scheduler.constraint.script.and.action=           scheduler.form.scheduled.task= 

تحسين أداء مشاريع البيانات الكبيرة


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

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

  • . ( , ), , , .
  • . , , .
  • . / , . «» .


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

يمكن أن تتحقق خاصية ما إذا كان هناك عدد محدد من مجموعات الكائنات التي لا تكون قيمة هذه الخاصية فيها خالية (NULL).

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

مؤشرات


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

يمكن فقط فهرسة الخصائص الملموسة (من القسم أعلاه).

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

date = DATA DATE (Order);
INDEX date(Order o), o;

INDEX name(Sku s), price(s, DATE d), d;

الجداول


يستخدم LsFusion قاعدة بيانات علائقية لتخزين وحساب قيم الخصائص. يتم تخزين جميع الخصائص الأساسية ، وكذلك جميع الخصائص المجمعة التي تم وضع علامة عليها ، في حقول جداول قاعدة البيانات. لكل جدول ، هناك مجموعة من حقول المفاتيح مع أسماء key0 ، key1 ، ... ، keyN ، حيث يتم تخزين قيم الكائنات (على سبيل المثال ، للفئات المخصصة ، معرفات هذه الكائنات). تقوم جميع الحقول الأخرى بتخزين قيم الخصائص بطريقة تكون في الحقل المقابل لكل صف قيمة الخاصية للكائنات من حقول المفاتيح.

عند إنشاء جدول ، يجب عليك تحديد قائمة فئات الكائنات التي ستكون المفاتيح في هذا الجدول.
TABLE book (Book);

in = DATA BOOLEAN (Sku, Stock);
TABLE skuStock (Sku, Stock); // in

price = DATA NUMERIC [ 10 , 2 ] (Sku, DATE );
TABLE skuDate (Sku, DATE ); // Sku

TABLE sku (Sku);
لكل خاصية ، يمكنك تحديد الجدول الذي يجب تخزينه به. في هذه الحالة ، يجب أن يتطابق عدد مفاتيح الجدول مع عدد معلمات الخاصية ، ويجب أن تتطابق فئات المعلمات مع فئات المفاتيح في هذا الجدول. إذا لم يتم تعيين الجدول الذي سيتم تخزينه فيه صراحةً للخاصية ، فسيتم وضع الخاصية تلقائيًا في جدول "الأقرب" الموجود في النظام (أي ، عدد المفاتيح التي يتزامن مع عدد معلمات الخاصية ، والتي تكون فئات المفاتيح الأقرب لها من فئات المعلمات ).

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

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

استنتاج


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

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

قريباً جداً ، سيكون هناك العديد من المقالات في أسلوب: "لماذا لا ...؟" ، وأنا متأكد من أنها ستكون أكثر إثارة للاهتمام من هذه البرامج التعليمية المملة للغاية.

Source: https://habr.com/ru/post/ar460887/


All Articles