ما هي بعض مزايا كتابة البط مقابل الكتابة؟الكتابة الثابتة؟

StackOverflow https://stackoverflow.com/questions/47972

  •  09-06-2019
  •  | 
  •  

سؤال

أنا أبحث وأجرب المزيد مع Groovy وأحاول أن ألتف حول إيجابيات وسلبيات تنفيذ الأشياء في Groovy التي لا أستطيع/لا أفعلها في Java.لا تزال البرمجة الديناميكية مجرد مفهوم بالنسبة لي منذ أن كنت غارقًا في اللغات الثابتة والمكتوبة بقوة.

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

أطرح هذا السؤال مع وضع Groovy في الاعتبار ولكنني أفهم أنه ليس بالضرورة سؤالًا رائعًا، لذا أرحب بالإجابات من كل معسكر تعليمة برمجية.

هل كانت مفيدة؟

المحلول

التالي أيهما أفضل:إيماكس أو السادس؟وهذه إحدى الحروب الدينية الجارية.

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

ليس الأمر كما لو أن الكتابة الديناميكية هي شيء جديد ومختلف:لغة C، على سبيل المثال، تتم كتابتها ديناميكيًا بشكل فعال، حيث يمكنني دائمًا إرسال ملف foo* إلى أ bar*.هذا يعني فقط أن مسؤوليتي كمبرمج لغة C هي عدم استخدام الكود المناسب على الإطلاق bar* عندما يشير العنوان حقًا إلى أ foo*.ولكن نتيجة للمشكلات المتعلقة بالبرامج الكبيرة، قامت لغة C بتطوير أدوات مثل lint(1)، وعززت نظام الكتابة الخاص بها باستخدام typedef وفي النهاية طورت متغيرًا مكتوبًا بقوة في لغة C++.(وبالطبع، طورت C++ بدورها طرقًا للالتفاف حول الكتابة القوية، مع جميع أنواع القوالب والأسماء العامة/القوالب ومع RTTI.

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

نصائح أخرى

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

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

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

ومع ذلك، فقد حقق لي العمل مع Groovy شيئًا واحدًا - ساعات رائعة قابلة للفوترة!

لا حرج في الكتابة الثابتة إذا كنت تستخدم Haskell، الذي يتمتع بنظام كتابة ثابت مذهل.ومع ذلك، إذا كنت تستخدم لغات مثل Java وC++ التي تحتوي على أنظمة كتابة معيقة للغاية، فإن كتابة البطة تعد بالتأكيد تحسينًا.

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

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

أحدث حجتي المفضلة ضد تأتي كتابة البطة من مشروع Grails DTO:

class SimpleResults {
    def results
    def total
    def categories
}

أين results تبين أن يكون شيئا من هذا القبيل Map<String, List<ComplexType>>, ، والتي لا يمكن اكتشافها إلا من خلال اتباع سلسلة من استدعاءات الأساليب في فئات مختلفة حتى تجد مكان إنشائها.بالنسبة للفضوليين نهائيًا، total هو مجموع أحجام List<ComplexType>رمل categories هو حجم Map

ربما كان الأمر واضحًا للمطور الأصلي، لكن رجل الصيانة السيء (ME) فقد الكثير من الشعر أثناء تتبعه.

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

IMHO، تصبح ميزة كتابة البطة تتضخم عندما تلتزم ببعض الاتفاقيات، مثل تسمية المتغيرات والأساليب بطريقة متسقة.أخذ المثال من كين ج, ، أعتقد أنه سيكون من الأفضل القراءة:

class SimpleResults {
    def mapOfListResults
    def total
    def categories
}

لنفترض أنك حددت عقدًا على بعض العمليات المسماة "calculateRating(A,B)" حيث يلتزم A وB بعقد آخر.في الكود الكاذب، سيكون نصه كما يلي:

Long calculateRating(A someObj, B, otherObj) {

   //some fake algorithm here:
   if(someObj.doStuff('foo') > otherObj.doStuff('bar')) return someObj.calcRating());
   else return otherObj.calcRating();

}

إذا كنت تريد تنفيذ ذلك في Java، فيجب على كل من A وB تنفيذ نوع من الواجهة التي تقرأ شيئًا مثل هذا:

public interface MyService {
    public int doStuff(String input);
}

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

public long calculateRating(MyService A, MyServiceB);

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

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

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

إليك أحد السيناريوهات حيث تؤدي كتابة البطة إلى حفظ العمل.

وهنا فئة تافهة جدا

class BookFinder {
    def searchEngine

    def findBookByTitle(String title) {
         return searchEngine.find( [ "Title" : title ] ) 
    }
}

والآن اختبار الوحدة:

void bookFinderTest() {
    // with Expando we can 'fake' any object at runtime.
    // alternatively you could write a MockSearchEngine class.
    def mockSearchEngine = new Expando()
    mockSearchEngine.find = {
        return new Book("Heart of Darkness","Joseph Conrad")
    }

    def bf = new BookFinder()
    bf.searchEngine = mockSearchEngine
    def book = bf.findBookByTitle("Heart of Darkness")
    assert(book.author == "Joseph Conrad"
}

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

بسبب كتابة البطة، يمكن أن يوفر اختبار الوحدة الخاص بنا أي كائن قديم بدلاً من التبعية، طالما أنه ينفذ الأساليب التي يتم استدعاؤها.

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

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

مع، TDD + تغطية الكود بنسبة 100% + أدوات IDE لإجراء اختباراتي باستمرار، لم أعد أشعر بالحاجة إلى الكتابة الثابتة.مع عدم وجود أنواع قوية، أصبح اختبار الوحدة الخاص بي سهلاً للغاية (ما عليك سوى استخدام الخرائط لإنشاء كائنات وهمية).على وجه الخصوص، عند استخدام الأدوية العامة، يمكنك رؤية الفرق:

//Static typing 
Map<String,List<Class1<Class2>>> someMap = [:] as HashMap<String,List<Class1<Class2>>>

ضد

//Dynamic typing
def someMap = [:]   

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

@ كريس بانش

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

آسف ولكن كان علي أن أفعل ذلك..

رأيي:

اللغات المكتوبة ديناميكيًا أو المكتوبة بالبط هي ألعاب.لا يمكنك الحصول على Intellisense وستفقد وقت الترجمة (أو وقت التحرير - عند استخدام REAL IDE مثل VS، وليس تلك القمامة التي يعتقد الآخرون أنها IDEs) للتحقق من صحة التعليمات البرمجية.

ابتعد عن كل لغة لم تتم كتابتها بشكل ثابت، وكل شيء آخر هو مجرد ماسوشية واضحة.

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

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

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top