سؤال

أنا في منتصف القراءة ممتازة كود نظيف

إحدى المناقشات تتعلق بتمرير القيم الخالية إلى إحدى الطرق.

public class MetricsCalculator {
    public double xProjection(Point p1, Point p2) {
        return (p2.x - p1.x) * 1.5;
    }
}
...
calculator.xProjection(null, new Point(12,13));

إنه يمثل طرقًا مختلفة للتعامل مع هذا:

public double xProjection(Point p1, Point p2) {
    if (p1 == null || p2 == null) {
        throw new IllegalArgumentException("Invalid argument for xProjection");
    }
    return (p2.x - p1.x) * 1.5;
}

public double xProjection(Point p1, Point p2) {
    assert p1 != null : "p1 should not be null";
    assert p2 != null : "p2 should not be null";
    return (p2.x - p1.x) * 1.5;
}

لكنني أفضل التأكيدات النهج، ولكن أنا لا أحب حقيقة أن التأكيدات يتم إيقاف تشغيلها بشكل افتراضي.

يقول الكتاب أخيرًا:

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

لا يتعلق الأمر حقًا بكيفية فرض هذا التقييد؟

هل لدى أي منكم آراء قوية في كلتا الحالتين.

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

المحلول

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

  • تتمتع التأكيدات بميزة الأداء حيث يتم تعطيلها عادةً في أنظمة الإنتاج.
  • تتمتع الاستثناءات بميزة السلامة، حيث يتم إجراء الفحص دائمًا.

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

على الرغم من ذلك، من الناحية العملية، أعرف الكثير من المطورين الذين لا يثقون في أنه سيتم تمكين التأكيدات عندما يكون ذلك مناسبًا، لذا اختاروا أمان طرح NullPointerException.

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

نصائح أخرى

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

إقرأ أيضاً مقالاً عن البرمجة الدفاعية

أيضًا ليس للاستخدام الفوري، ولكنه يتعلق بذكر Spec#...هناك اقتراح لإضافة "أنواع خالية من الأمان" إلى إصدار مستقبلي من Java: "المعالجة الخالية المحسّنة - الأنواع الآمنة الخالية".

وبموجب الاقتراح، ستصبح طريقتك

public class MetricsCalculator {
    public double xProjection(#Point p1, #Point p2) {
        return (p2.x - p1.x) * 1.5;
    }
}

أين #Point هو نوع من غيرnull إشارات إلى كائنات من النوع Point.

لا يتعلق الأمر حقًا بكيفية فرض هذا التقييد؟

يمكنك فرض ذلك عن طريق رمي استثناء الوسيطة إذا مروا فارغة.

if (p1 == null || p2 == null) {
    throw new IllegalArgumentException("Invalid argument for xProjection");
}

أفضّل استخدام التأكيدات.

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

المواصفات # تبدو مثيرة للاهتمام للغاية!

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

/**
 * Checks to see if an object is null, and if so 
 * generates an IllegalArgumentException with a fitting message.
 * 
 * @param o The object to check against null.
 * @param name The name of the object, used to format the exception message
 *
 * @throws IllegalArgumentException if o is null.
 */
public static void checkNull(Object o, String name) 
    throws IllegalArgumentException {
   if (null == o)
      throw new IllegalArgumentException(name + " must not be null");
}

public static void checkNull(Object o) throws IllegalArgumentException {
   checkNull(o, "object");
} 

// untested:
public static void checkNull(Object... os) throws IllegalArgumentException {
   for(Object o in os) checkNull(o);  
}

ثم يتحول التدقيق إلى:

public void someFun(String val1, String val2) throws IllegalArgumentException {
   ExceptionUtilities.checkNull(val1, "val1");
   ExceptionUtilities.checkNull(val2, "val2");

   /** alternatively:
   ExceptionUtilities.checkNull(val1, val2);
   **/

   /** ... **/
} 

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

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

وجدت جيت براينز' @Nullable و @NotNull يعد نهج التعليقات التوضيحية للتعامل مع هذا هو الأكثر براعة حتى الآن.إنه خاص بـ IDE، لسوء الحظ، ولكنه نظيف وقوي حقًا، IMO.

http://www.jetbrains.com/idea/documentation/howto.html

سيكون وجود هذا (أو شيء مشابه) كمعيار جافا أمرًا رائعًا حقًا.

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

أعتقد أنه لا يزال قيد التطوير (بواسطة Microsoft) ولكن بعض برامج CTP متاحة ويبدو أنها واعدة.في الأساس يسمح لك بالقيام بذلك:

  public static int Divide(int x, int y)
    requires y != 0 otherwise ArgumentException; 
  {
  }

أو

  public static int Subtract(int x, int y)
    requires x > y;
    ensures result > y;
  {
    return x - y;
  } 

كما يوفر ميزات أخرى مثل أنواع Notnull.إنه مبني على .NET Framework 2.0 وهو متوافق تمامًا.بناء الجملة، كما ترون، هو C#.

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

@wvdschel واو!إذا كانت كتابة الكود تتطلب مجهودًا كبيرًا بالنسبة لك، فيجب عليك البحث في شيء مثل بوستشارب (أو ما يعادله في Java إذا كان متاحًا) والذي يمكنه معالجة التجميعات الخاصة بك وإدراج عمليات التحقق من المعلمات نيابةً عنك.

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

//  allocate null
var name : Option[String]
name = None

//  allocate a value
name = Any["Hello"]

//  print the value if we can
name match {
  Any[x] => print x
  _ => print "Nothing at all"
}

أفضّل عمومًا عدم القيام بأي منهما، لأنه يؤدي فقط إلى إبطاء الأمور.يتم طرح NullPointerExceptions لاحقًا على أي حال، مما سيقود المستخدم بسرعة إلى اكتشاف أنه يقوم بتمرير القيمة null إلى الطريقة.اعتدت على التحقق، ولكن 40٪ من الكود الخاص بي انتهى به الأمر إلى التحقق من الكود، وعند هذه النقطة قررت أن الأمر لا يستحق رسائل التأكيد اللطيفة.

أوافق أو أختلف مع مشاركة wvdschel, ، ذلك يعتمد على ما يقوله على وجه التحديد.

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

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

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

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

وهذا له ميزتان:

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

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

رمي C # ArgumentException, أو جافا IllegalArgumentException في بداية الطريقة تبدو لي أوضح الحلول.

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

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

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

إذا كان يمكن أن يأتي null من الفعلي استثناءفي جميع الأحوال ولكنك لا تريد استخدام استثناء محدد لذلك، فأنت بالتأكيد تريد اتباع مسار IllegalArgumentException في بداية الطريقة.

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