لماذا تجنب استخدام الاستثناءات للتحكم في التدفق في Java

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

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

  1. هذا يقلل من أداء وسرعة التعليمات البرمجية الخاصة بك.
  2. هذا يجعل رمزك أقل قابلية للقراءة.

لنبدأ بالنظر إلى مثال. هنا يتم استخدام الاستثناء للتحكم في تدفق التنفيذ:

public static int findAge(String name) { try { String ageAsString = findUser(name); return ageAsString.length(); } catch (NameNotFoundException e) { return 0; } } private static String findUser(String name) { if(name==null) { throw new NameNotFoundException(); } return name; } 

إذا كان المستخدم يوفر قيمة غير صفرية للاسم ، فسوف تُرجع طريقة findAge طول هذا الاسم ، ولكن إذا كان اسم المستخدم فارغًا ، فإن طريقة findUser ستلقي اسم NameNotFoundException وفي هذه الحالة ، فإن طريقة findAge ستُرجع 0.

كيف يمكننا تحويل هذا الرمز دون استخدام استثناءات؟ بشكل عام ، هناك العديد من الخيارات ، فكر في واحد منها:

 public static int findAgeNoEx(String name) { String ageAsString = findUserNoEx(name); return ageAsString.length(); } private static String findUserNoEx(String name) { if(name==null) { return ""; } return name; } 

لمعرفة تأثير الاستثناء على الأداء ، قمت بإعداد التعليمة البرمجية التالية التي تستدعي كلا الخيارين 10 مليون مرة: مع وبدون استثناء.

 public class ControlFlowWithExceptionOrNot { public static class NameNotFoundException extends RuntimeException { private static final long serialVersionUID = 3L; } private static final int TRIAL = 10000000; public static void main(String[] args) throws InterruptedException { long start = System.currentTimeMillis(); for (int i = 0; i < TRIAL; i++) { findAgeNoEx(null); } System.out.println("Duration :" + (System.currentTimeMillis() - start)); long start2 = System.currentTimeMillis(); for (int i = 0; i < TRIAL; i++) { findAge(null); } System.out.println("Duration :" + (System.currentTimeMillis() - start2)); }; public static int findAge(String name) { try { String ageAsString = findUser(name); return ageAsString.length(); } catch (NameNotFoundException e) { return 0; } } private static String findUser(String name) { if (name == null) { throw new NameNotFoundException(); } return name; } public static int findAgeNoEx(String name) { String ageAsString = findUserNoEx(name); return ageAsString.length(); } private static String findUserNoEx(String name) { if (name == null) { return ""; } return name; } } 

الاستنتاج:

 Duration :16 Duration :6212 

كما ترى ، فإن استخدام الاستثناء كلفنا آلاف الميلي ثانية على جهاز Intel Core i7-3630QM.

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

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

آمل أن يكون هذا المقال مثيراً لك ، وقد يكون مفيدًا.

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


All Articles