هل تكتب استثناءات لقضايا محددة أم استثناءات عامة؟

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

  •  09-06-2019
  •  | 
  •  

سؤال

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

emailUtil.sendEmail(userId, "foo");

public void sendEmail(String userId, String message) throws MailException {
    /* ... logic that could throw a MailException */
}

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

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

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

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

هل سيكون من الأفضل أن يكون هناك تطبيق استثناء لكل واحدة من هذه المشكلات أو استثناء شامل واحد يحتوي على شيء داخلي فيه (يقول التعداد) والذي يسمح للكود بتمييز نوع المشكلة.

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

المحلول

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

مثال من Java-API هو IOException، الذي يحتوي على فئات فرعية مثل FileNotFoundException أو EOFException (وأكثر من ذلك بكثير).

بهذه الطريقة تحصل على مزايا كليهما، ولن يكون لديك عبارات رمي ​​مثل:

throws SpecificException1, SpecificException2, SpecificException3 ...

جنرال

throws GeneralException

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

نصائح أخرى

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

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

لذلك باستخدام المثال الخاص بك، إذا كنت تريد تشغيل بعض المنطق الخاص عندما لا يتم تكوين خادم البريد الإلكتروني، فقد ترغب في إضافة طريقة إلى كائن emailUtil مثل:

المنطق العام هوEmailConfigured()

...اتصل بذلك أولاً، بدلاً من البحث عن استثناء محدد.

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

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

@كريس.ليفلي

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

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

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

يعتمد ذلك على ما يفعله تطبيقك.قد ترغب في طرح استثناءات فردية في حالات مثل

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

في معظم الحالات، أود أن أقول ما عليك سوى تسجيل نص الاستثناء ولا تضيع وقتك في تفصيل الاستثناءات الدقيقة بالفعل.

أعتقد أن الجمع بين ما سبق سيعطيك أفضل نتيجة.

يمكنك طرح استثناءات مختلفة اعتمادًا على المشكلة.على سبيل المثالعنوان البريد الإلكتروني المفقود = ArgumentException.

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

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

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

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

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

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

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

تحديث: الجمع بين الإجابات

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

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

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

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

منذ بضعة أشهر أنا مدون حول فكرة كيفية تدويل الاستثناءات.ويتضمن بعض الأفكار المذكورة أعلاه.

بينما يمكنك التمييز بين تنفيذ التعليمات البرمجية، فإن البحث عن الاستثناء لا يهم إذا تم ذلك عن طريق "وضع التسلسل الهرمي لنوع الاستثناء" أو عن طريق "إذا (...) آخر... وضع رمز الاستثناء"

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

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

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

سأذهب فقط

throw new exception("WhatCausedIt")

إذا كنت تريد التعامل مع الاستثناءات الخاصة بك، فيمكنك تمرير رمز بدلاً من "WhatCausedIt" ثم الرد على الإجابات المختلفة باستخدام عبارة التبديل.

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