سؤال

يتيح لك Java إنشاء نوع فرعي جديد تمامًا Throwable, ، على سبيل المثال:

public class FlyingPig extends Throwable { ... }

حاليا، نادرا جدا, ، قد أفعل شيئًا كهذا:

throw new FlyingPig("Oink!");

وبالطبع في مكان آخر:

try { ... } catch (FlyingPig porky) { ... }

أسئلتي هي:

  • هل هذه فكرة سيئة؟ وإذا كان الأمر كذلك ، فلماذا؟
    • ما الذي يمكن القيام به لمنع هذا النوع الفرعي إذا كانت فكرة سيئة؟
    • نظرًا لأنه لا يمكن الوقاية منه (على حد علمي) ، ما هي الكوارث التي يمكن أن تنتج؟
  • إذا لم تكن هذه فكرة سيئة ، فلماذا لا؟
    • كيف يمكنك أن تجعل شيئًا مفيدًا من حقيقة أنه يمكنك extends Throwable?

السيناريو المقترح #1

سيناريو حيث كنت حقًا إغراء القيام بشيء مثل هذا لديه الخصائص التالية:

  • "الحدث" هو شيء إرادة يحدث في النهاية. إنها مُتوقع. بالتأكيد ليس Error, ، ولا يوجد شيء Exception-الأشكال عند حدوثها.
    • لأنه مُتوقع, ، سيكون هناك catch ينتظر. لن "تنزلق" بعد أي شيء. لن "يهرب" من أي محاولة catch جنرال لواء Exception و/أو Error.
  • يحدث "الحدث" نادرا للغاية.
  • عندما يحدث ذلك ، عادة ما يكون هناك تتبع عميق.

لذلك ربما يكون من الواضح الآن ما أحاول قوله: FlyingPig هل نتيجة بحث عودية شاملة.

الكائن الذي يجب البحث عنه موجود: إنها مجرد مسألة العثور عليها في البحر الكبير وهي مساحة البحث. ستكون عملية البحث طويلة ، وبالتالي فإن تكلفة معالجة الاستثناءات باهظة الثمن نسبيًا لا تذكر. في الواقع ، بناء تدفق التحكم التقليدي بديل لاستخدام أ boolean isFound قد يكون العلم أكثر تكلفة ، لأنه يجب فحصه بشكل مستمر طوال عملية البحث ، على الأرجح في كل مستوى من مستوى العودية. سوف يفشل هذا الشيك بنسبة 99.99 ٪ من الوقت ، ولكن من الضروري للغاية نشر حالة الإنهاء. بطريقة ما ، فعال, ، الشيك هو غير فعال!

ببساطة throw-ing أ FlyingPig عندما يتم العثور على الكائن المطلوب ، لا يتعين عليك فوضى الرمز مع إدارة boolean isFound علَم. لا يقتصر الأمر على نظافة الكود في هذا الصدد ، ولكن قد يتم تشغيله بشكل أسرع بسبب هذا الإغفال.

لتلخيص ، الاختيار بين هذين:

  • نهج التدفق التقليدي
    • إستخدم boolean isFound, ، فحص بشكل مستمر
    • 99.99 ٪ من الوقت ، الشيك هو "مضيعة" ، لأنه لا يزال سيكون false
    • عندما يصبح في النهاية true, ، تتوقف عن التكرار وعليك التأكد من أنه يمكنك الاسترخاء بشكل صحيح للمكالمة الأولية.
  • FlyingPig يقترب
    • لا تهتم بأي boolean isFound.
    • إذا وجدت ، فقط throw new FlyingPig(); ؛ إنه مُتوقع, ، لذلك سيكون هناك أ catch لذلك.
    • لا إدارة boolean العلم ، لا تحقق ضائعا إذا كنت بحاجة إلى الاستمرار ، لا مسك الدفاتر للاسترخاء يدويا العودية ، إلخ.

أسئلة:

  • هل هذه التقنية (AB) باستخدام استثناء صالحة؟ (هناك اسم لذلك؟)
  • إذا ساري ، ينبغي FlyingPig extends Throwable, ، أو Exception بخير؟ (على الرغم من أنه لا يوجد شيء استثنائي حول ظروفه؟)
هل كانت مفيدة؟

المحلول

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

لا أستطيع التفكير في أي سبب وجيه للتمديد Throwable. تمديد Exception أو RuntimeException في حين أن.

تعديل - استجابة للسيناريو المقترح لل OP.

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

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

باختصار ، أ FlyingPig قد لا يكون خطأ ، لكن هذا ليس سببًا لعدم إعلانه كنوع فرعي لـ Exception.

نصائح أخرى

هل هذه التقنية (AB) باستخدام استثناء صالحة؟ (هناك اسم لذلك؟)

في رأيي ، إنها ليست فكرة جيدة:

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

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

أنظر أيضا:

إذا ساري ، ينبغي FlyingPig يمتد Throwable, ، أو Exception بخير؟ (على الرغم من أنه لا يوجد شيء استثنائي حول ظروفه؟)

قد تكون هناك مواقف تريدها قبض على Throwable ليس فقط التقاط Exception ولكن أيضا Error لكن هذا نادر ، عادة لا يصطاد الناس Throwable. لكني فشلت في العثور على موقف تريد يرمي فئة فرعية من Throwable.

وأعتقد أيضًا أن التمديد Throwable لا تجعل الأمور تبدو أقل "استثناء", ، يجعلونها تبدو أسوأ - والتي تهزم النية تمامًا.

لذلك في الختام ، إذا كنت تريد حقًا رمي شيء ما ، رمي فئة فرعية من Exception.

أنظر أيضا:

إليك منشور مدونة من John Rose ، وهو مهندس معماري للنقطة الساخنة:

http://blogs.oracle.com/jrose/entry/longjumps_considered_inexpmance

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

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

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

ال org.junit.Test الشروح يشمل None الفصل الذي يمتد Throwable ويستخدم كقيمة افتراضية ل expected معلمة التعليقات التوضيحية.

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

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

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

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

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

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

لذا ، لنفترض أن لديك فئات الاستثناء التالية في قاعدة الشفرة الخاصة بك:

public class Pig extends Throwable { ... }
public class FlyingPig extends Pig { ... }
public class Swine extends Pig { ... }
public class CustomRuntimeException extends RuntimeException { ... }

وبعض الطرق

public void foo() throws Pig { ... }
public void bar() throws FlyingPig, Swine { ... }
// suppose this next method could throw a CustomRuntimeException, but it
// doesn't need to be declared, since CustomRuntimeException is a subclass
// of RuntimeException
public void baz() { ... } 

الآن ، يمكن أن يكون لديك بعض التعليمات البرمجية التي تسمي هذه الأساليب مثل هذه:

try {
    foo();
} catch (Pig e) {
    ...
}

try {
    bar();
} catch (Pig e) {
    ...
}

baz();

لاحظ أنه عندما ندعو bar(), ، يمكننا فقط التقاط Pig, ، منذ كليهما FlyingPig و Swine تمديد Pig. هذا مفيد إذا كنت تريد أن تفعل الشيء نفسه للتعامل مع أي من الاستثناءات. ومع ذلك ، يمكنك التعامل معهم بشكل مختلف:

try {
    bar();
} catch (FlyingPig e) {
    ...
} catch (Swine e) {
    ...
}

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

أنا سطحي قليلا على التفاصيل. يمكنك النظر من خلال رمز اللعب! إطار عمل للحصول على التفاصيل.

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