خصائص JavaFX
خصائص JavaFX
إذا سبق لك بناء تطبيق Swing أو AWT الخام فأنت تعرف الإحباط جيدًا: تُحدّث حقلًا في النموذج ثم تستدعي label.setText(...) يدويًا وتُعيد الرسم وتأمل أنك لم تفوّت أي مسار. تحل JavaFX هذه المشكلة على مستوى اللغة من خلال نظام الخصائص — مجموعة من الأغلفة القابلة للملاحظة حول قيم Java العادية. عندما تتغير القيمة تُخطَر كل الأطراف المهتمة تلقائيًا. يتناول هذا الدرس ما هي الخصائص وكيف تعمل من الداخل والأسلوب الاصطلاحي لكتابة فئة نموذج تستخدمها.
ما هي الخاصية القابلة للملاحظة؟
خاصية JavaFX هي كائن يُغلّف قيمة ويوفر ثلاث قدرات:
- وصول للقراءة والكتابة إلى القيمة الأساسية عبر
get()وset(). - إشعار بالتغيير — تُستدعى المستمعات المسجّلة على الخاصية في كل مرة تتغير فيها القيمة.
- دعم الربط — يمكن ربط الخاصية بخاصية أخرى لتبقى متزامنتان دون الحاجة إلى كود مزامنة يدوي.
تأتي JavaFX بتطبيقات جاهزة لكل نوع بدائي ولـ Object:
StringProperty/SimpleStringPropertyIntegerProperty/SimpleIntegerPropertyDoubleProperty/SimpleDoublePropertyBooleanProperty/SimpleBooleanPropertyObjectProperty<T>/SimpleObjectProperty<T>
نمط التسمية ثابت: الواجهة مثل StringProperty تصف العقد، والتطبيق مثل SimpleStringProperty هو ما تُنشئه في فئات النموذج الخاصة بك.
التسلسل الهرمي لخصائص JavaFX
من المفيد فهم التسلسل الهرمي الكامل للأنواع حتى تتمكن من قراءة وثائق JavaFX API دون التباس:
Observable— الواجهة الأساسية؛ تستطيع إطلاق أحداث الإبطال.ObservableValue<T>— تضيفgetValue()ومستمعات التغيير.ReadOnlyProperty<T>— تضيفgetBean()وgetName()(بيانات وصفية عن الكائن المالك واسم الحقل).Property<T>— تضيفsetValue()وطرق الربط.WritableValue<T>— تضيفsetValue()باستقلالية.
الواجهات المتخصصة مثل StringProperty تمتد من كل من Property<String> و WritableStringValue، مضيفةً get() و set(String) المحددين بالنوع لتجنب عمليات الملاكمة.
كتابة فئة نموذج بالخصائص
الاتفاقية في JavaFX لفئة النموذج (تُسمى أحيانًا JavaFX Bean) تعكس اتفاقية Java Beans لكنها تضيف محوّلًا ثالثًا — محوّل الخاصية — الذي يُرجع كائن الخاصية نفسه حتى يتمكن المُستدعون من الربط به.
يُمرر المُنشئ ذو الثلاثة حجج (new SimpleStringProperty(bean, name, initialValue)) بيانات وصفية: this هو الكائن المالك و "name" هو اسم الحقل. هذه البيانات اختيارية لكنها قيّمة عند تصحيح الأخطاء — تظهر في إخراج toString() للخاصية وفي تشخيصات Scene Builder.
xxxProperty(). تستخدم المتحكمات والروابط محوّل الخاصية؛ أما كود الخدمات والتخزين الدائم فيستخدم المحوّلات العادية. خلط الجمهورين عبر محوّل واحد يؤدي إلى كود متشابك يصعب اختباره.
إرفاق مستمعات التغيير
بمجرد امتلاكك كائن الخاصية يمكنك تسجيل ChangeListener عليه. يتلقى المستمع الـ observable نفسه، والقيمة القديمة، والقيمة الجديدة:
لاحظ توقيع اللامدا لـ IntegerProperty: على الرغم من أن القيمة الأساسية من نوع int، يتلقى المستمع Number (النوع الأب الملاكَم). استدع .intValue() لإلغاء الملاكمة.
الخصائص للقراءة فقط
أحيانًا تريد خاصية قابلة للقراءة من الخارج لكن لا يُمكن الكتابة إليها إلا من داخل الفئة — مثل قيمة محسوبة كإجمالي السعر. توفر JavaFX ReadOnlyIntegerWrapper (والمكافئ للأنواع الأخرى) لهذا النمط:
يحمل الغلاف IntegerProperty الحقيقي. يُرجع getReadOnlyProperty() عرضًا يشارك نفس القيمة لكنه لا يعرض أي دالة set(). يمكن للمُستدعين الربط به والاستماع للتغييرات لكن لا يستطيعون تعديله.
xxxProperty() الخاص بك الـ ReadOnlyIntegerWrapper بدلًا من استدعاء getReadOnlyProperty()، يستطيع الكود الخارجي تحويله مجددًا إلى IntegerProperty قابل للكتابة وكسر التغليف. أرجع دائمًا العرض للقراءة فقط من المحوّل العام.
الخصائص وخيط تطبيق JavaFX
يجب أن تحدث جميع تعديلات مشهد JavaFX — بما فيها الكتابة إلى خصائص مرتبطة بعناصر واجهة المستخدم — على خيط تطبيق JavaFX. إذا حدّثت خاصية من خيط خلفي (مثلًا بعد استدعاء شبكي) يجب نقل التحديث:
يضع Platform.runLater() الـ Runnable في طابور نبضات JavaFX. للعمل الخلفي الأثقل، يُقدّم الدرس التالي Task و Service اللذين يتعاملان مع ذلك تلقائيًا.
الخلاصة
خصائص JavaFX هي أغلفة قابلة للملاحظة تفصل منتج القيمة عن كل مستهلك يهتم بها. نمط Bean ذو المحوّلات الثلاثة — محوّل قراءة عادي، ومحوّل كتابة عادي، ودالة xxxProperty() — هو العقد المعياري الذي ينبغي أن تتبعه فئات النموذج. في الدرس التالي سترى كيف تربط خاصيتين معًا باستخدام الربط (Binding)، مما يُزيل كليًا نمط مستمع التغيير لأكثر احتياجات المزامنة شيوعًا.