احصل على كتلة تهيئة ثابتة لتشغيلها في جافا دون تحميل الفصل
-
24-09-2019 - |
سؤال
لدي بعض الفصول كما هو موضح هنا
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(...)
لتحميل كل واحد.