احصل على كتلة تهيئة ثابتة لتشغيلها في جافا دون تحميل الفصل

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

سؤال

لدي بعض الفصول كما هو موضح هنا

public class TrueFalseQuestion implements Question{
    static{
        QuestionFactory.registerType("TrueFalse", "Question");
    }
    public TrueFalseQuestion(){}
}

...

public class QuestionFactory {

 static final HashMap<String, String > map =  new HashMap<String,String>();

 public static void registerType(String questionName, String ques ) {
     map.put(questionName, ques);
     }
 }



public class FactoryTester {
    public static void main(String[] args) {
        System.out.println(QuestionFactory.map.size());
        // This prints 0. I want it to print 1
    }
}

كيف يمكنني التغيير TrueFalseQuestion الفصل بحيث يتم تشغيل الطريقة الثابتة دائمًا حتى أحصل على 1 بدلاً من 0 عندما أقوم بتشغيل طريقتي الرئيسية؟ لا أريد أي تغيير في الطريقة الرئيسية.

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

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

المحلول

لتسجيل TrueFalseQuestion فئة مع المصنع ، يجب استدعاء التهيئة الثابتة. لتنفيذ التهيئة الثابتة لـ TrueFalseQuestion الفصل ، يحتاج الفصل إلى الإشارة أو يجب تحميله عن طريق التفكير من قبل QuestionFactory.map.size() يسمى. إذا كنت تريد مغادرة main الطريقة التي لم تمسها ، عليك الرجوع إليها أو تحميلها عن طريق التفكير في QuestionFactory التهيئة الثابتة. لا أعتقد أن هذه فكرة جيدة ، لكنني سأجيب فقط على سؤالك :) إذا كنت لا تمانع QuestionFactory معرفة جميع الفصول التي تنفذ Question لإنشاءها ، يمكنك فقط الرجوع إليها مباشرة أو تحميلها من خلال التفكير. شيء مثل:

public class QuestionFactory {

 static final HashMap<String, String > map =  new HashMap<String,String>();

 static {
    this.getClassLoader().loadClass("TrueFalseQuestion");
    this.getClassLoader().loadClass("AnotherTypeOfQuestion"); // etc.
 }

 public static void registerType(String questionName, String ques ) {
     map.put(questionName, ques);
     }
 }

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

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

نصائح أخرى

تستطيع الاتصال:

Class.forName("yourpackage.TrueFalseQuestion");

سيؤدي ذلك إلى تحميل الفصل بدون لمسه فعليًا ، وسيقوم بتنفيذ كتلة التهيئة الثابتة.

لا يمكن تنفيذ المُعزد الثابت بالفئة إذا لم يتم تحميل الفصل أبدًا.

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

طريقة واحدة للقيام بهذا الأخير هي استخدام ServiceLoader.

مع ال ServiceLoader يمكنك ببساطة وضع ملف في META-INF/services/package.Question وسرد جميع التطبيقات. بإمكانك أن تأخذ مضاعف مثل هذه الملفات ، واحد لكل ملف .jar. بهذه الطريقة يمكنك شحنها بسهولة Question التطبيقات منفصلة عن البرنامج الرئيسي.

في ال QuestionFactory يمكنك بعد ذلك الاستخدام ببساطة ServiceLodaer.load(Question.class) للحصول على ServiceLoader, الذي ينفذ Iterable<Question> ويمكن استخدامها مثل هذا:

for (Question q : ServiceLoader.load(Question.class)) {
    System.out.println(q);
}

من أجل تشغيل المهيئات الثابتة ، يجب تحميل الفئات. لكي يحدث هذا ، يجب أن تعتمد فئتك "الرئيسية" (بشكل مباشر أو غير مباشر) على الفصول ، أو يجب أن تتسبب بشكل مباشر أو غير مباشر في تحميلها ديناميكيًا ؛ على سبيل المثال باستخدام Class.forName(...).

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

هذا يتركك بديلين:

  • اكتب بعض التعليمات البرمجية الفوضوية للتكرار على أسماء الموارد في بعض الحزمة ، ثم استخدمها Class.forName(...) لتحميل تلك الموارد التي تشبه فصولك. هذا النهج صعب إذا كان لديك classpath معقد ، ومن المستحيل إذا كان classpath الفعال يتضمن urlClassloader مع عنوان URL عن بُعد (على سبيل المثال).

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

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