Home برمجة جافا قل لا ل Constructor بمعاملات كثيرة
قل لا ل Constructor بمعاملات كثيرة

قل لا ل Constructor بمعاملات كثيرة

103
9

ما هو عدد دوال البناء اذا أردت عمل overloading لأكثر من 4 معاملات بترتيب مختلف؟؟

هذه المرة الثانية نشن هجوماً على دالة البناء constructor، في مقالنا الأول (هل مللت من دالة البناء ) فضلنا دالة static factory method على ال constructor بسبب مقروئيتها وامكانية دعمك للCaching لو أردت في وقت لاحق.

عندما تريد انشاء كائن من كلاس به متغيرات ضرورية وقت الانشاء وبها اختياريه فسوف نقول أيضاً: لا لل constructors ولا حتى لل static factory methods.

للتوضيح: كما نعلم أنه لا يمكن انشاء كائن بدون استدعاء دالة البناء بشكل او أخر، فعندما نقول لا لدالة البناء فنحن نقصد لا بجعلها متاحة للكلاينت public constructor ولكن نريد اخفائها private constructor عن الكلاينت، في هل مللت من دالة البناء قمنا بجعل دالة البناء private وقمنا باستدعائها من دالة public static في نفس الكلاس، بينما هنا في موضوعنا هذه المقالة سوف نخفى دالة البناء private constructor ونقوم بانشاء الكائن من خلال inner class في نفس الكلاس، لماذا؟ اكمل القرائة الآن.

فدوال البناء العادية constructor ودوال الstatic factory قد لا يستطيعوا تقديم الحل الأفضل في حالة كان لديك العديد من المعاملات المرسلة لدالة البناء وخصوصاً لو كانت هناك معاملات اختيارية ،، على سبيل المثال لو أردنا عمل كلاس يمثل بيتزا يقوم العميل بتخصيص طلبها على حسب ما يريد ، ففي البدايه عليه باختيار الحجم ثم يحدد ما يريده من الخيارات المتاحة هي جبنة وهوت دوق وببروني ،،

بطريقة دوال البناء العادية سوف يخرج لدينا كلاس بهذا الشكل:

الحل أعلاه (وهو يسمى Telescoping Constructor) قد يكون مقبول الأن ، لكن ماذا لو كانت عدد الخيارات هي 20 مثلاً؟ هل ستقوم بكتابة الكم الهائل من دوال البناء هذه .. ناهيك عن صعوبة قرائه الكود الذي ينشئ الكلاس.. ناهيك عن الأخطاء الناتجة من تمرير هذا الكم الهائل من المعاملات للدالة، فقد يخطئ ممرر القيم ويمرر true للهوت دوق وfalse للجنبه بينما المستخدم يريد العكس (بكل تأكيد المترجم سعيد ولن يصدر رسالة خطأ لهذا الخطأ)، وكل ذلك بسبب صعوبة تذكر هذا الكم من المعاملات.. بنفس الأمر ال static factory لن ينفع في هذه الحالة لأنك ستحتاج لعمل overloads لهذا العدد من الدوال..

الحل الثاني الممكن هو استخدام اسلوب جافا بينز Java Beans وذلك عن طريق انشاء الكائن فارغ أولاً، ثم وضع القيم التي يريدها المبرمج، المثال التالي يوضح ذلك:

هذا الحل تلافي مشكلة دوال البناء Telescoping Constructor ، فهو سهل القرائة وواضح جداً.. لكن هناك مشكلتين متعلقات بJava Beans Pattern أولها أن الكائن بعد انشائه سوف يكون في حالة غير مستقره حيث أنه لا يستطيع وقت انشاء الكائن التحقق من القيم المدخلة فيه، وبالتالي من الممكن ان ينشىء الكائن وبعد ذلك تمرر له قيم خاطئة ويكون في حالة غير سليمة inconsistent state. المشكلة الثانية هي أن الكلاس بعد استخدام هذا الأسلوب لا يمكن أن يكون كلاس Immutable ويتطلب جهداً من المبرمج للتأكد من أن الكلاس مؤمن من مشاكل الThreads (بمعنى Thread-Safety).

الحل الصحيح هو بجمع الأمان والتحقق الذي تحققه دوال البناء و مقروئية الJava Beans Pattern وذلك باستخدام الBuilder Pattern. وفكرتها هي بدلاً من انشاء الكائن مباشرة، سوف تقوم باستدعاء دالة بناء (أو دالة static factory -تذكرها أولاً قبل دالة البناء!-) بالقيم الأساسية المطلوبة وستحصل على كائن Builder وبعد ذلك سوف تستدعي دوال داخل هذا الكائن يمكنك أن تقول بأنها setter method للقيم الاختياريه وأخيراً تقوم باستدعاء الدالة build الموجودة داخل الbuilder لكي ترجع لك الكائن (والذي يكون Immutable) . المثال:

انظر لكيفية تكوين الكائن:

وهكذا حققنا المقروئية والسهوله ويتم تحقيق الأمان والتحقق عن طريق فحص المتغيرات في دالة البناء Pizza ومن ثم عمل throw لException اذا وجد هناك خطأ وفي داخل داله الbuild يتم عمل catch اذا وجد هذا الexception.

مثال أخر لتوضيح الفرق لبناء كائن بطريقة telescoping و builder pattern من كلاس يحتوي على ثلاث متغيرات :

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

في العالم الحقيقي أستخدم الBuilder Patterns كثيراً جداً فهو اسهل واكثر مقروئية وأفضل للكلاينت، والآن نسخه Netbeans 7.2 أصبحت تضع خيار Replace Constructor with Builder من ضمن قائمة ال Refactoring، ولو ألقيت نظرة على مكتبات الجافا الحديثة مثلاً Guava ستجد الكثير جداً من ال Builder Pattern والتي تفضل على دوال البناء العادية..

لذلك اذا كان لديك مثل هذه الكومة وحتى ان لم يكن لديك overloading :

فقم بتحويلها الى Builder Pattern واستمتع بال New Look والمزايا الجديدة لكلاسك.

وكخلاصة: يفضل استخدام الBuilder Pattern عن تصميمك لكلاس يحتوي على دالة بناء أو static factory تستقبل أكثر من 4 متغيرات وما فوق وخصوصاً لو كانت هذه المتغيرات اختياريه ، سوف تكسب المقروئية من الكود أكثر من Telescoping Constructor وأكثر أماناً من استخدام Java Beans Pattern.

(103)

وجدي عصام مهندس برمجيات مهتم بعلوم الحاسب وبالأخص مجال الخوارزميات وهندسة البرمجيات وحماية التطبيقات،

Comment(9)

  1. بسم الله الرحمن الرحيم

    جزاكم الله كل خير -تم اضافة المقالة للكتاب وتنسيقها بفضل الله تعالى -اكمل اخى الفاضل متابعيين بأذن الله تعالى

    1. هذه طريقة Java Beans تقوم بعمل كلاس بدالة بناء لا تقوم باستقبال اي معامل ومن ثم تستخدم ال setters لاعطاء القيم، ولقد فضلناها بطريقة ال builder pattern ،

      الفكرة هنا أننا سوف ننشىء الكلاس ولكن من خلال الكلاس الداخلي Inner Class (والذي سنقوم بانشائه من خلال دالة بناء عادية أو دالة static factory ) وسنمرر له القيم المطلوبة :

      بعد ذلك سنستخدم الbuilder ونقوم بوضع القيم التي نريدها :

      ومن ثم ابني الكائن باستخدام الدالة build والتي سترجع الأن الكائن أخيراً:

      هكذا اعتمدنا على الbuilder لانشاء الكائن وحصلنا عليه ،، الميزة هي ان الكلاس لا يوجد فيه setter وبالتالي حصلنا على Immutable class (كلاس لا تتغير قيمته بعد الانشاء) وهكذا حصلنا على نفس اداء دالة البناء العادية وسهوله طريقة ال setter .

      تحياتي،،

  2. رحم الله والديك في الدنيا والآخرة ،

    مقالتان عن الباني حلت الكثير من الألغاز التي كنت أراها في أكواد الجافا

    وفقك الله يا أخ وجدي .

  3. الحل :

  4. السلام عليكم أخي وجدي ربنا يبارك فيك ويجزيك عنا خير
    لدي سؤالان:
    هل الBuilder Pattern يتوافق مع مفهوم الـMVC ؟
    كيف يمكن بطريق الـBuilder Pattern تغيير حجم البيتزا في وقت لاحق؟

    1. اهلأً اخي

      بالنسبة للتوافق فنعم يمكن استخدام النمطين مع بعض، حيث ان ال MVC هي طريقة لتقسيم المشروع Architectural Pattern، بينما ال Builder Pattern هي طريقة لانشاء الكائن Construction Pattern.. مثلاً كان ال Model لديك فيه كائن يحتاج لقيم الزامية وقيم ليست كذلك ، فيمكنك استخدام ال Builder لذلك الكائن وتبنيه من ال Controller بشكل اوضح من دالة البناء الكبيرة.

      بالنسبة للتغيير اي Attribute في الكائن، فتستطيع ذلك من خلال اي Setter ، حيث ان ال Builder تبني لك الكائن فقط وتعيده لك، لكن تستطيع تغيير اي قيمه فيه متى ما شئت الا لو كائن الكائن هو Immutable ففي تلك الحالة تحتاج لبناء كائن بالقيم القديمة مع التحديث الذي تريده.

      شكراً لك.

LEAVE YOUR COMMENT

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

مشاركة