السلام عليكم ورحمه الله ويركاته ،،
مقدمه :
=-=-=-=
إن موضوع التحليل والتصميم بالكائنات من أهم المواضيع الني ينبغي لأي مبرمج أو مطور يستخدم لغه كائنيه أن يجيده ، حيث على المبرمج أن يقوم بتحليل البرنامج أولا ويعرف ما هي المشكله التي يريد أن يحلها Analysis ومن ثم يبدأ هذا المبرمج بطرح الحلول وكيف يمكن أن تتفاعل هذه الكائنات مع بعضها البعض design ، ومن ثم يقوم بعمليه الCoding ويقوم بتطبيق التصميم الذي طرحه في المرحله السابقه ، وأخيرا يقوم باجراء الTesting للبرنامج والتأكد من مطابقته لمتطلبات الزبون Customer Requirement .
العمليات السابقه التي ذكرتها (Analysis,Design,Coding,Testing) تعرف بدوره حياه المشروع Software Development Life Cycle . وبالطبع أي مشروع يجب أن يمر على جميع هذه المراحل بترتيب معين حيث أن عمليه التطوير يجب تكون على منهجيه معينه وباتباع طريقه ما Methodology والا ستعم الفوضي ولن تستطيع حتى كتابه سطر واحد صحيح من البرنامج .
باتباع هذه المهجيات سوف تضمن أن عمليه تطويرك للبرنامج تسير في المنحنى الصحيح ، كما أنها توفر لك كل ما تحتاجه من وقت البدء في تحليل المشروع الى وقت التسليم والصيانه للمشروع ، اضافه الى توضيح نوعيه الوثائق والمخططات التي ستنتجها كل مرحله Activities & Artifacts وليس هذا فقط فهناك منهجيات توفر لك نصائح وارشادات في عمليه جدوله المشروع واداره الفريق وما الى ذلك من العمليات الإداريه Management Task .
قديما كانت تجري هذه العمليات SDLC بالترتيب وبشكل متنازل وهو ما يعرف بنموذج الشلال Waterfall Model .. حيث يقوم أولا المحلل بتحليل البرنامج بالكامل وكتابه جميع الوثائق المطلوبه ومن ثم يمرر هذه الوثائق للمصمم الذي يقوم بتصميم النظام ووضع حلوله أيضا ، ومن ثم تمر بالمبرمج الذي يقوم بكتابه الكود وعندما ينتهي تجرى الأختبارات على هذا البرنامج وأخيرا يسلم الى الزبون ..
ولكن للأسف هذا النموذج لا يصلح بتاتا الا في المشاريع الصغيره والتي لا يوجد فيها خطوره ما (مثلا فريق التطوير قد قام بمشروع مشابه للمشروع الحالى ، حينها يمكن أن يستخدم هذا النموذج) .. مشكله أخرى وهي عدم وجود وثائق موحده تمر علي جميع فريق التطوير فالمحلل لديه اساليب خاصه فيه والمصمم كذلك وكل منهم بحاجه الى مزيد من الوقت لكي يفهم ماذا كان يقصد الشخص الذي كتب هذه الوثائق ..
ليس هذا فقط فعند الدخول في المشاريع الكبيره تبدأ المشاكل الأكبر بالظهور، فنموذج الشلال لا يسمح يتغيير المتطلبات ، حيث يجب على المحللين في البدايه أن يقوموا بتحليل النظام بشكل كااامل ومن ثم تبدأ مرحله التصميم وهكذا لجميع المراحل حيث لا عوده للخلف بتاتا .. السؤال هنا ماذا لو تغيرت متطلبات هذا الزبون بعد مرور 3 أشهر من التحليل والتصميم وبالبدء بالكود ، كارثه !! وحتى ولو لم تتغير المتطلبات ففي حال استخدم نموذج الشلال في مشروع كبير فهذا يعني تحليله بالكامل ثم تصميمه بالكامل وبرمجته بالكامل وهذا غير منطقى خصوصا في البرامج الكبيره حيث يجب أن تقسم لأجزاء صغيره يتم العمل على كل منها على حده . الصوره التاليه تشبه عمليه تطوير مشروع ضخم باستخدام نموذج الشلال بعمليه أكل فيل من غير تقسيم :
ناهيك عن الوقت المستغرق في كل مرحله ، حيث لأنها يجب أن تكون كامله وشامله يبدأ الخوف لدي المحللين فيأخذوا وقت أكثر من المفترض أن يكون به ويبدأ في ذكر "يبدوا أن هناك شيء ناقص ، سنعيد تحليل هذه النقطه" ، "يجب أن نراجع التحليل مره أخرى ، حيث اذا حدثت مشكله الأن سوف تبقى على اكتافنا" وهكذا يصاب المحلل ب Analysis Paralysis ، بنفس الأمر عند المصمم والمبرمج والشخص الذي يقوم بالأختبار كل منهم يصاب بهذه العقده خاصه ان كان قد فشل في مشروع سابق .
مع وجود كل هذه العيوب في هذا النموذج الا أنه يقدم تسلسل منطقى في عمليه تطوير (تحليل ثم تصميم ثم برمجه ثم اختبار) وبالتالي يمكن أن يستفاد من هذه التسلسل في منهجيات أخرى أكثر مرونه في الشلال وهذا ما حصل بالفعل .
النموذج الحلزوني Spiral كان من أول المحاولات في تطوير الشلال ، حيث يمكن أن نطور المشروع في أكثر من دوره (بمعنى أكثر من نموذح شلال في اَن واحد) .. وبنفس المراحل الموجوده في الشلال .. وبالتالى كان يمكن لفريق التطوير أن يقوموا بأخذ متطلبات غير كامله ويبدؤا بتحليلها وتصميمها وبرمجتها ، وهكذا تنتهي الدوره الأولى .. وتبدأ الدوره الثانيه وهكذا الى أن ينتهي تطوير المشروع بالكامل .
Resized to 93% (was 697 x 277) - Click image to enlarge
النموذج الحلزوني قلل من المشاكل بشكل كبير ، ولكن يمكن أن تظهر نفس المشكله في التي كانت تظهر في الشلال ، حيث في حال أكتشف المبرمج أن هناك خطأ أو نقص في التصميم لا يمكن العوده للخلف ويجب الانتظار حتى البدء في الدوره الثانيه .. النموذج جيد ولكن نريد أن نسمح بالعوده للخلف ..
النموذج المتكرر Iterative Methodology كان هو الحل لهذه المشكله ، حيث يمكن في هذا النموذج أن نعود خطوه للوراء ونصلح ذلك الخلل ونكمل السير في هذه الدوره
بالتالى أصبحت عمليه التطوير هي عباره عن عده دورات ، كل منها تستطيع التحرك خطوه للأمام أو الخلف على حسب ما تريده .. ولكن ما زالت المشاكل بالظهور فما زلت تقوم بأكل الفيل من مره واحده ،، جيث الدورات السابقه كانت لاصلاح خلل وغالبا ما تكون 4 دورات .. نحن نريد تقسيم المشروع لأكثر من دوره وفي كل مره نقوم بتطوير جزئيه معينه وهذا هو النموذج المتصاعد ، الصوره التاليه تبين تطوير الاصدارات مع تقدم الزمن ..
Resized to 97% (was 670 x 154) - Click image to enlarge
في حال أنك لاحظت أن جميع هذه الطرق (الشلال ، الحلزوني ، المتصاعد ، المتكرر) كل منها توجد فيه خاصيه مفيده في عمليه التطوير ، وبالتالى يمكن أن نجمع جميع هذه الطرق مع بعضها البعض وبالتالى نستفيد من جميع المزايا في كل واحده من الطرق
بعد جمع هذه الطرق جميعها ، مازال ينقص شيء مهم جدا ، وهو توضيح مالذي يجب أن نفعله في مرحله التحليل ، ما هي نوعيه الوثائق التي يجب أن تنتج ؟ ما هو شكل الأختبارات التي يجب أن تكون ؟ ما هي المخططات التي يجب أن تستخدم في مرحله التصميم ؟ نعم نحن بحاجه الى لغه موحده للرسومات ومنهجيه معينه في التطوير تسمح لي باتباع خطواتها للوصول للمشروع الناجح ان شاء الله ..
من هذه النقطه ظهرت العديد المنهجيات التي توضح كيفيه تطوير المشاريع مثل RUPو Ripple و Agile ، بالاضافه الى العديد من الطرق المستخدمه في وصف ورسم العلاقات والطرق المستخدمه وكانت أشهرها وأفضلها وأصبحت الأن هي المقياس هي Unified Modeling Language .
RUP وUML كانت نتيجه مجهودات الأصدقاء الثلاثه Grady Booch و Ivar Jacobson و James Rumbaugh حيث كان لكل منهم لغه نمذجه خاصه فيه اضافه الى منهجيه معينه للتطوير ، فاجتعموا على تطوير لغه نمذجه موحوده ومن هنا كانت بدايه لUML ، وبعدها أصحبت معيار رسمي وسجلت لدى OMG .. أما RUP فتعتبر من أشهر المنهجيات للتطوير ومن أصعبها أيضا على المبتدئين ..
Agile methodology هي مجموعه من المنهجيات الجيده و التي ترحب بتغييرات المتطلبات في أي لحظه في دوره حياه المشروع ، كما أنها تتسم بالسرعه والخفه .. أحد أشهر هذه المنهجيات هي eXtreme Programmingوهي تعمل بمبدأ test-driven development و pair programming بمعنى ان المبرمج سيقوم قبل كتابه الكود بكتابه أختبار سيجريه على الكود الذي سيكتبه وعند الأنتهاء يختبر الكود فاذا نجح فينتقل للخطوه التاليه (كود الأختبار قبل الكود) .. أما pair programming فتعني أنه يجب أن يكتب الكود من قبل مبرمجين اثنين يكونوا أما الشاشه في نفس اللحظه .. أحدهم يراقب الكود والأخر يستلم عجله القياده (الكيبورد) .. فاذا تعب يتم التبديل وهكذا ... مناصروا الXP يروا أن استخدام هذه الطريقه يزيد من كفائه المبرمجين وخاصه في حال جلس مبرمج مبتدأ مع أخر خبير بالمجال..
أما Ripple فهي أيضا أحد المنهجيات والتي هي عباره عن نسخه مبسطه من الRUP ، الصوره التاليه تبين لك المخرجات الناتجه في كل مرحله من مراحل التطوير باستخدام Ripple ..
لاحظ أن جميع هذه المنهجيات أصبحت تستخدم UML كلغه موحوده في جميع المشاريع ، وبالتالى على المبرمج أن يكون على بهذه المخططات البسيطه ( هي 13 مخطط فقط) .. ملاحظه اخرى وهى أن هذه المخططات في النهايه هي عباره عن مخططات فقط !! فهي لن ترشدك في عمليه التطوير ولن تريك ما الذي يمكن أن تفعله في هذه المرحله فهي من مسؤوليه المنهجيه .. بالتالى على المبرمج أن يتعلم هذه المنهجيات ويعرف الفرق فيما بينهم وما هي المنهجيه التي ستفيده في مشروعه الحالى ..
بالطبع تعلم منهجيه ثقيله كRUP أو حتى ripple بالنسبه للمبتدئين أمر غير جيد حيث سيصعب على المبتدئ التعلم من خلال هذه المنهجيات ، لذلك كانت هناك منهجيات خفيفه lightweight process يمكن أن تستخدم كمثال عملي على التطوير باستخدام منهجيه ما . وهناك الكثير من المنهجيات الخفيفه بل ربما يمكنك أن تنشئ منهجيتك الخاصه في التطوير .. الصوره التاليه توضح خطوات منهجيه خفيفه سوف نستخدمها في الدروس المقبله ان شاء الله .
مقدمه في UML :
=-=-=-=-=-=-=-=
لغه UML تقدم وصف لكيفيه بناء المشروع البرمجي ، وتقدم وصف دقيق لمعماريه مشروعك Architecture ، فكما يقوم البنائيين ببناء المنازل من خلال اتباع مخطط Blueprint قام بعملها المهندس المعماري ، يقوم المبرمج باتباع مخططات UML قام بها المحللين والمصممين للبرنامج. هذه اللغه كما ذكرنا أصبحت المقياس في وصف المخططات عند بناء أي مشروع برمجي . وهي حوالي 13 مخطط . سوف نستعرض أهمهم الأن بشكل مبسط بدون الدخول في التفاصيل وهم : use-case , sequence , collaboration , activity , state , package , component .
هذه المخططات يمكنك استخدامها وقت ما تشاء كيفما تشاء ، فلغه UML لم تحدد وتفصل مالذي يجب أن تستخدمه مثلا في مرحله التصميم أو التحليل .. فهذه من مسوؤليه المنهجيه .. لغه UML هي فقط مجموعه من المخططات عليك أن تعرف كيف ومتى يجب أن تستخدمهم .. لذلك موضوع المنهجيه مهم جدا لأنه بدون منهجيه ولو كانت بسيطه فلن تستطيع معرفه ما المخطط المطلوب في مرحله جمع المتطلبات أو مرحله التحليل وهكذا سيكون أستخدام UML وبالا عليك (ماعدا في Toys-example كما سنرى بعد قليل ) .
Use-Case Diagram يستخدم هذا المخطط لوصف متطلبات النظام بشكل High-level حيث يوضح المهام الذي يجب أن يقوم بها المستخدم (سواء كان انسان Customer أم نظام فرعي Subsystem) . ومن خلال النظر في المخطط يمكن للمبرمج أن يعرف ماهي المهام التي يجب أن يؤديها النظام . حيث تمثل الدائره المهمه أو الوظيفه التي يجب أن يقوم بها النظام ، ويمثل اللاعب Actor الشخص أو النظام الذي سوف يقوم بهذه المهمه .
Class Diagram يوضح هذا المخطط الModel الذي سوف تقوم ببنائه ، بالاضافه الى العلاقه بين كل من هذه الكلاسات .. ويستخدم عاده في مرحلتين التحليل والتصميم ، حيث في المرحله الأولى نقوم بتوضيح اسم الكلاس وعلاقته مع البقيه ، أما في مرحله التصميم فنوضح جميع الخصائص والعمليات التي يقوم بها هذا الكلاس بالتفصيل ..
مثال على class diagram في مرحله التحليل :
وهنا مثال على class diagram في مرحله التصميم :
Sequence Diagram وهو أحد مخططات التفاعل Interaction Diagram يوضح هذا المخطط طريقه عمل Use-Case ما خلال الزمن ، وستشاهد أن هناك خطوط متقطعه تمثل الزمن ، ومستطيل صغير خلال هذه الخطوط يمثل زمن البدء في عمل هذا الكائن ويسمى activation .
Collaboration Diagram أيضا هو أحد مخططات التفاعل Interaction Diagram وهو يشبه مخطط التتابع ولكنه لا يوضح التسلسل الزمني للأحداث ، المثال التالى لمخطط تفاعل لاسترجاع كلمه مرور .
Activity Diagram يوضح هذا المخطط سلوك النظام بشكل عام أو أحد مهمات النظام Use-Case وكيف تنتقل من نشاط لأخر . وهو يشبه الflow chart .
وغيرها من المخططات والتي يستخدم كل منهم لأمر معين .. يمكنك البحث والقرائه في كتب UML للمزيد عنها . سوف نستعرض الأن موضوع مهم وهو العلاقات بين الكائنات ونأخذ مثالين على ذلك ..
Modeling Class Relationship
من أهم المخططات في UML هي مخططات الكلاس ، حيث ستكون الأساس في عمليه البرمجه ، وهناك الكثير من البرامج تستطيع توليد الكود من خلال مخططات الكلاس ، وأيضا هناك برامج تقوم بالبعكس فهي تخرج المخطط من الكود ، نقوم بالتفصيل الأن في هذا المخطط المهم .
مخطط الكلاس في أبسط حالته يكون بالشكل التالى :
حيث يحتوي على المتغيرات التي توجد في الكلاس ، بالاضافه الى الدوال التي تعمل على ذلك الكائن ، ويمكن أن تستخدم note للاشاره الى المخطط الذي تريده وللشرح بشكل أوضح .. وكما هو الحال في الكلاسات العاديه فيجب تحديد الرؤيه Visibility لأي متغير أو داله في الكلاس وذلك باستخدام الاشارات (-و+،#) .. الصوره التاليه توضح ذلك :
نقوم بالتطبيق مثلا على حساب بنكي يحتوي على رصيد ودوال للعمليات عليه ، وبالتالى يكون شكل المخطط كالتالي :
بعض الأحيان قد تحتاج لأن تقوم بتوضيح مصطلح جديد غير موجود في مخطط الكلاس ، وهو يسمى ب Steretype ويكون بالشكل << >> وتكتب بداخل القوسين المصطلح الجديد ، مثلا abstract , accessors , interface وغيرها .
يكفي هكذا ولنرى أنواع العلاقات بين الكلاسات بشكل مبسط ونأخذ مثالين عمليين على نوعين من العلاقات .. بشكل عام العلاقات بين الكلاسات هي اعتماديه Dependency ، وارتباط association ، ووراثه Generalization .
الاعتماديه تشير الى أن الكائن من كلاس يعتمد في عمله على كائن أخر ، مثلا لدينا كائن X يستقبل كائن أخر Y كمعامل في أحد الدوال ، وفي هذه الداله نقوم باستدعاء داله في Y من خلال الكائن Y . هنا أصبح الكلاس X يعتمد في عمله على الكائن Y ، ففي حال تغيرت الداله في الكائن Y (سواء اسمها أو معاملاتها) يجب اجراء التعديل في الكائن X . وبشكل عام يفضل تقليل الاعتماديه الى أقل ما يمكن في البرنامج . وتوصف الاعتماديه من خلال سهم متقطع ، بهذا الشكل :
النوع الأخر من العلاقات وهو ال Association وهو يوضح علاقه احتواء كائن لكائن أخر (Composition , Aggregation) ، أو يوضح الدور الذي يمكن يلعبه الكائن مع الأخر Plain Association . ويمكن توضيح العلاقه من خلال سهم عادي ، ومن ثم نقوم بكتابه اسم العلاقه ، المثال التالي يوضح أن الشخص يستطيع أن يقترض من البنك ..
ويمكن أيضا أن توضح الدور الذي يلعبه كل كائن في العلاقه ، المثال التالي يوضح أن borrower هو الذي يلعب دور الشخص وأن Lender هو الذي يلعب دور البنك :
يمكن أيضا توضيح عدد الكائنات التي تلعب الدور المعين وذلك من خلال الMultiplicity ، الشكل التالي يوضح أن البنك يتعامل مع شخص واحد أو أكثر ، بينما الشخص يستطيع التعامل مع 0 أو أكثر من بالبنوك ..
كما ذكرنا يمكن أن تصف هذه العلاقه عما اذا كان الكائن يحتوي على كائن أخر Has-A ، وهناك نوعين منهم وهم الComposition و Aggregation وسبق أن ذكرناهم هنا في هذا الموضوع.
أخيرا النوع الثالث من العلاقات وهو الوراثه generalization وبالتأكيد الجميع يعرف هذا المفهوم ، ويكون شكل المخطط كالتالي :
نبدأ الأن بأخذ مثال على علاقه الأحتواء Has-a (مثال Bank Account) وسوف نستخدم سي++ للتوضيح هنا .. ومن ثم نأخذ مثال أخر لتوضيح علاقه الIs-a (مثال Company Employee) وسوف نستخدم جافا لكي يتابعوا معنا مبرمجي جافا .
سنقوم بحل هذه المشكلتين بشكل مبسط ولن نتطرق لموضوع المنهجيه أو حتى رسم المخططات بالكامل .. هذا الحديث في المقاله القادمه ان شاء الله ..
مثال على برنامج Bank Account
=--=-=-=-=-=-=-=-=-=-=-=-=
نأخذ مثال على الأن على برنامج بسيط لبنك يقوم بانشاء حساب للزبائن ويستطيع الزبون بايداع أو سحب النقود من حسابه كما يمكنه الاطلاع على حسابه ومعرفه كم يوجد به من نقود .. أيضا يستطيع عامل البنك من معرفه مجموع النقود في جميع الحسابات ..
يمكنك أن تجلب ورقه وقلم وتقوم برسم المخططات بشكل يدوي أو استخدام برنامج لهذا الأمر ، وحاليا بما أنك ترسم هذه المخططات لأول مره يفضل ورقه وقلم .. وتبدأ عمليه عصر الدماغ brainstorming .
بعد تحديدك لمشكله البرنامج أو الMission Statement ، يجب أن تبدأ في تحديد المهمات الأساسيه في البرنامج من خلال المشكله أعلاه ..وبالطبع هذه العمليه ربما تصعب عليك من البدايه ولكن مع حل العديد من المشاكل تزداد خبرتك وتستطيع تحديد المهمات بكل سهوله ..
حاليا المهمات ( الUse-case ) في البرنامج المطلوب وهي :
* انشاء حساب جديد
* القيام بعمليه ايداع في الحساب
* القيام بعمليه سحب من الحساب
* معرفه الرصيد الحالى للزبون
* معرفه مجموع النقود في جميع الحسابات .
واللاعب الذي يلعب الدور (يقوم بالمهمه) في كل من هذه الUse-Case هو العامل في البنك . حيث هو الذي يقوم بانشاء الحساب وهو الذي يقوم باخراج الرصيد الحالى للزبون وهكذا لبقيه العمليات .
يمكنك الأن أن تقوم برسم مخططات الUase-Case فهي توضح لك متطلبات البرنامج والمهام التي يجب القيام بها بشكل رسومي سهل ، وأيضا توضح من الذي يفترض أن يقوم بذلك الدور . الشكل التالي يوضح use-case diagram
بعدها نقوم بعمل توضيح Realization لكل من هذه الUase-Case وذلك باستخدام مخططات الActivity Diagram و Seqeunce Diagram و Collaboration Diagram .(كل منهجيه تستخدم نوع معين من المخططات أثناء مرحله التحليل والتصميم) حاليا نحن نعمل بدون منهجيه معينه لغرض توضيح كيفيه استخدام هذه المخططات بشكل عام .
نقوم الأن باستخدام مخطط Activity لتوضيح أي من الUse-Case التي استخرجناها سابقا .. ومخطط النشاط هذا يوضح لي النشاطات التي ستجرى على الuse-case المحدد .. وهنا سنجد أننا لكي نقوم بعمل حساب جديد يجب أن نقوم بفتح الحساب (ويكون موافي للشروط) ومن ثم نقوم بتسجيل هذا الحساب في البنك .
يمكنك الأن بشكل مبدئي ان تقوم برسم الكلاسات التي سوف تستخدمها في برنامجك ، حاليا من خلال المهام يتضح لدينا أن هناك Bank وهناك Account و هناك Employee (فقط عليك برسمهم من غير الدخول في التفاصيل ، سنقوم برسمهم في الخطوه التاليه ) . بعدها لكي يتم توضيح خطوات اي use-case بترتيبها الزمني سنستخدم ال Seqeunce Diagram بالشكل التالي :
وهكذا سنقوم بتطبيق المخططات Activity و sequence و collaboration على جميع الuse-case لكي نوضح طريقه عمل كل منهم ..
الأن من خلال هذه المخططات يتضح أن لدينا الكلاسات التاليه Account و Bank و Employer .. ولكن الأخير Employer لن نحتاجه في البرنامج حيث هو ليس من ضمن المشكله التي نريد حلها ، هو فقط سيكون المسبب أو المستخدم لكل من المهمات في النظام . وبالتالى ينتج لدينا الكلاسين التالين Account,Bank . (عمليه استخلاص الكلاسات من أصعب المهمات على الاطلاق والخبره هي الفيصل دائما في مشاكل استخلاص الكلاسات) .
ومن خلال المشكله نفسها ، يمكن أن نعرف أن الAccount يحتوي على رصيد ورقم للعميل بالاضافه الى اسمه .. اما الbank فهو الذي سيجري جميع هذه المهمات على الaccount . هكذا نكون قد عرفنا الخصائص والعمليات على هذين الكلاسين .. اما العلاقه التي ستربط الكلاسين ببعضهم هي Composition أي أن الBank سوف يحتوي على صفر أو أكثر من الaccounts . الصوره التاليه توضح مخطط الكلاس الذي يوضح العلاقه بالاضافه لمحتويات كل من الكلاسين ..
الى هنا قمنا بتحليل المشكله وتصميم الكلاسات ، بقي أن نبدأ بعمليه البرمجه . وسوف نقوم بكتابه النظام بسي++ بشكل مبسط .. لاحظ الداله الرئيسيه main حيث قمنا باختبار كل واحد من هذه الuse-case وعدى الأختبار بنجاح بالتالي يمكنك الأن بناء التطبيق النهائي سواء باستخدام واجهه GUI أو قائمه من الخيارات في console .
// Example of Bank Account Application
#include <iostream>
#include <vector>
using namespace std;
class Account; // decleration for class
typedef vector<Account> :: iterator ITR;
class Account {
public :
Account (int id, string str, double amount) :
idNumber(id),name(str),balance(amount) { }
// Accessores function (setter & getter)
void setName (const string str) { name = str; }
string getName () const { return name; }
int getIdNumber () const { return idNumber; }
double getBalance () const { return balance; }
void addBalance (double amount ) { balance += amount; }
void subBalance (double amount ) { if ( balance >= amount) balance -= amount; }
friend ostream& operator << (ostream& ostr , const Account& rhs);
private :
string name;
int idNumber;
double balance;
};
ostream& operator << (ostream& ostr , const Account& rhs) {
ostr << "Account Number : " << rhs.getIdNumber() << endl
<< "Account Name : " << rhs.getName() << endl
<< "Account Balance: " << rhs.getBalance() << endl;
return ostr;
}
class Bank {
public :
Bank(string str) : name(str) { }
void openNewAccount (int idNumber , string name, double balance);
void creditAccount (int idNumber , double balance );
void depositAccount (int idNumber , double balance );
double getBalance (int idNumber );
double getTotalBalance ();
string getName () const { return name; }
friend ostream& operator << (ostream& ostr, const Bank& bank);
private :
// prevent pass by value & assignment operator
Bank (const Bank& lhs);
Bank operator = (const Bank& lhs);
private :
vector<Account> accounts;
ITR itr;
string name;
};
void Bank :: openNewAccount (int idNumber , string name , double balance ) {
Account tmp(idNumber,name,balance);
accounts.push_back(tmp);
}
void Bank :: creditAccount (int idNumber ,double balance ) {
for ( itr = accounts.begin(); itr != accounts.end(); ++itr) {
if ( itr->getIdNumber() == idNumber ) {
itr->addBalance(balance);
break;
}
}
}
void Bank :: depositAccount (int idNumber ,double balance ) {
for ( itr = accounts.begin(); itr != accounts.end(); ++itr) {
if ( itr->getIdNumber() == idNumber ) {
itr->subBalance(balance);
break;
}
}
}
double Bank :: getBalance (int idNumber ) {
for ( itr = accounts.begin(); itr != accounts.end(); ++itr) {
if ( itr->getIdNumber() == idNumber ) {
return itr->getBalance();
}
}
}
ostream& operator << (ostream& ostr , const Bank& bank ) {
ostr << "Welcome To : " << bank.getName() << endl;
ostr << "-----------------------------" << endl << endl;
for (vector<Account> :: const_iterator itr = bank.accounts.begin(); itr != bank.accounts.end(); ++itr) {
ostr << *itr << endl << endl;
}
return ostr;
}
double Bank :: getTotalBalance () {
double sum = 0;
for (itr = accounts.begin(); itr != accounts.end(); ++itr) {
sum += itr->getBalance();
}
return sum;
}
int main (int argc, char* argv[]) {
Bank bank("ArabTeam2000");
// open new account use case test
bank.openNewAccount(1,"Wajdy essam",2000);
bank.openNewAccount(32,"Ali Ahmed",425);
bank.openNewAccount(42,"Omer Khalid",6000);
// credit account use case test
bank.creditAccount(1,100);
// deposit account uses case test
bank.depositAccount(42,1000);
// display all accounts use case test
cout << bank << endl;
// get balance use case test
cout << "Balance for customer ID (42) : " << bank.getBalance(42) << endl;
// get All balance use case test
cout << "Total Bank balance : " << bank.getTotalBalance() << endl;
return (0);
}
مثال على برنامج Company Employer
=-=-=-=-=-=-=-=-=-==-=-=-=-=-=
سنستعرض الأن مثال على النوع الأخر من العلاقات وهو الوراثه Specialization ولن نهتم الأن سوى بمخطط الكلاسات فقط .. حيث أن المشكله قريبه من المشكله السابقه ..
ليكن لدينا شركه برمجيه تحتوي على العديد من المبرمجين .. المطلوب كتابه تطبيق يخرج لي معلومات حول معلومات المبرمج (المعرف ، الراتب ، لغه البرمجه التي يستخدمها) . مع العلم أنه يمكن للمبرمج أن يغير لغته البرمجيه .
من خلال المشكله السابقه قد نستنتج نوع العلاقه وهي أن الشركه تحتوي على العديد من المبرمجين ، أي العلاقه هنا احتواء has-a وتكون في مثالنا هنا Aggregation . الشكل التالي يوضح مخطط الكلاس لهذه المشكله :
[img ]http://www.sudancs.com/wajdy/OOAD8.PNG[/img ]
الأن سوف نبدأ مباشره بعمليه البرمجه ، ويمكن أن يكون شكل الكلاس Programmer بهذا الشكل :
public class Programmer {
private String name;
private double salary;
private int idNumber;
private String language
public Programmer (int id , String name , double salary,Sting lang) {
this.name = name;
this.idNumber = id;
this.salary = salary;
this.language = lang;
}
public int getId () {
return idNumber;
}
public String getName () {
return name;
}
public double getSalary () {
return salary;
}
public String getLanguage () {
return language;
}
public void setLanguage (String lang) {
language = lang
}
public String toString() {
return idNumber + " : " + name + " , Language : " + language + " take " + salary + " $";
}
}
بعد انتهائك من كتابه الكلاس ، قد سمعت بأن هناك أحتمال دخول موظفين أخرين لهذه الشركه مثلا مدير أو موظف عادي ، لذلك في مثل هذه الحالات يمكن أن تلجأ لأسلوب الوراثه من كلاس .. وبما أن المبرمج أو المدير هو في النهايه موظف في الشركه ، فسوف نقوم بعمل كلاس Empolyer ونرث منه ما نريد وقت الحاجه ونضيف الأمور التي تميز المبرمج عن الموظف العادي (وهي في حالتنا هنا لغه البرمجه المستخدمه) . وبالتالى يكون شكل المخطط هو :
[img ]http://www.sudancs.com/wajdy/OOAD9.PNG[/img ]
نقوم الأن باجراء التعديلات التاليه للكلاس السابق :
public class Employee {
private String name;
private double salary;
private int idNumber;
public Employee (int id , String name , double salary) {
this.name = name;
this.idNumber = id;
this.salary = salary;
}
public int getId () {
return idNumber;
}
public String getName () {
return name;
}
public double getSalary () {
return salary;
}
public String toString() {
return idNumber + " : " + name + " take " + salary + " $";
}
}
public class Programmer extends Employee {
private String language;
public Programmer (int id , String name ,double salary , String language) {
super(id,name,salary);
this.language = language;
}
public String getLanguage () {
return language;
}
public String toString () {
return super.toString() + " ,language : " + language;
}
}
الى الأن لم نكمل في البرنامج للأخير وتبقى الكلاس Software House .. وقبل أن نبدأ به حصل تغيير في المتطلبات مره أخرى ، وهذه المره ذكر العميل أن مبرمج جافا يمنح 20% زياده لراتبه .. أيضا أي مجموعه من المبرمجين يجب أن يداروا من قبل مدير للمشروع Project Leader ، هذا المدير لديه راتب شهري اضافه الى أنه يحصل على 10% زياده على كل مبرمج في فريقه .
التغيير الأول بسيط ، حيث كل ما علينا اعاده تعريف داله الراتب في الكلاس Programmer ونضيف التغيير الجديد ،، أما بالنسبه للمدير فهذا يتطلب وجود كلاس جديد في النظام .. وبما أن أي مدير يدير مجموعه من المبرمجين فإن يمكن أن تكون العلاقه أحتواء أيضا has-a . بالاضافه الى أن الشركه أصبحت تحتوي أيضا على مجموعه من المديرين (بالاضافه الى المبرمجين ) . الصوره التاليه توضح مخطط الكلاس :
Resized to 89% (was 731 x 368) - Click image to enlarge
بما أن الكلاس Software House يحتوي على Container من الكائنات تمثل المبرمجين وأخرى تمثل المديرين . فاننا اذا أردنا أن نطبع جميع محتويات هذه الشركه قد نحتاج لأن نمر على جميع عناصر الContainer الأول ونقوم بطباعتها وهكذا بالنسبه للContainer الثاني .
بالرغم من أن التصميم السابق صحيح لكنه يحتوي على تكرار في الكود ، ويمكن أن نحسنه قليلا عن طريق استخدام مفهوم تعدد الأشكال Polymorphism ، حيث سنجعل المدير والمبرمج من نوع empolyee وفي داله الطباعه سنقوم بطباعه الكائن الحالى الموجود في الcontainer والذي سيطبع الكائن المناسب .
نقوم بتعديل التصميم قليلا ، ويخرج لدينا المخطط التالي :
[img ]http://www.sudancs.c...ajdy/OOAD11.PNG[/img ]
نقوم الأن بالبدء في البرمجه :
// Employee.java
public abstract class Employee {
private String name;
private double salary;
private int idNumber;
public Employee (int id , String name , double salary) {
this.name = name;
this.idNumber = id;
this.salary = salary;
}
public int getId () {
return idNumber;
}
public String getName () {
return name;
}
public double getSalary () {
return salary;
}
public String toString() {
return idNumber + " : " + name + " take " + salary + " $";
}
}
public class Programmer {
private String name;
private double salary;
private int idNumber;
private String language
public Employee (int id , String name , double salary,Sting lang) {
this.name = name;
this.idNumber = id;
this.salary = salary;
this.language = lang;
}
public int getId () {
return idNumber;
}
public String getName () {
return name;
}
public double getSalary () {
return salary;
}
public String getLanguage () {
return language;
}
public void setLanguage (String lang) {
language = lang
}
public String toString() {
return idNumber + " : " + name + " , Language : " + language + " take " + salary + " $";
}
}
// Programmer.java
public class Programmer extends Employee {
private String language;
public Programmer (int id , String name ,double salary , String language) {
super(id,name,salary);
this.language = language;
}
public String getLanguage () {
return language;
}
public String toString () {
return super.toString() + " ,language : " + language;
}
public double getSalary () {
double value = super.getSalary();
if ( language.equals("java") )
value *= 0.2;
return value;
}
}
// ProjectLeader.java
import java.util.Iterator;
import java.util.ArrayList;
public class ProjectLeader extends Programmer {
private ArrayList<Employee> team;
public ProjectLeader (int id ,String name , double salary,String language) {
super(id,name,salary,language);
team = new ArrayList<Employee>();
}
public void addMember (Employee p) {
team.add(p);
}
public String toString () {
String str = super.toString();
str += "\nMember Information : \n";
Iterator itr = team.iterator();
while ( itr.hasNext() ) {
Employee tmp = (Employee) itr.next();
str += tmp.toString() + "\n";
}
return str;
}
public double getSalary () {
double value = super.getSalary();
int size = team.size();
double tmp = value * size * 0.1;
return value + tmp;
}
}
// SoftwareHouse.java
import java.util.ArrayList;
import java.util.Iterator;
public class SoftwareHouse {
private ArrayList<Employee> staff;
private String name;
public SoftwareHouse (String name) {
this.name = name;
staff = new ArrayList<Employee>();
}
public void addMember (Employee p) {
staff.add(p);
}
public String toString () {
String str = "Staff : " + name + "\n";
Iterator itr = staff.iterator();
while ( itr.hasNext() ){
Employee tmp = (Employee) itr.next();
str += tmp.toString() + "\n";
}
return str;
}
public double getTotalSalary () {
double sum = 0;
Iterator itr = staff.iterator();
while ( itr.hasNext() ) {
Employee tmp = (Employee) itr.next();
sum += tmp.getSalary();
}
return sum;
}
}
ملف الأختبار :
public class Main {
public static void main (String args[]) {
SoftwareHouse app = new SoftwareHouse("SudanCS");
Programmer p1 = new Programmer(1,"Wajdy",4000,"java");
Programmer p2 = new Programmer(2,"Geek",5000,"Assembly");
Programmer p3 = new Programmer(3,"Ahmed",3200,"C++");
Programmer p4 = new Programmer(4,"romansy",3000,"Php");
ProjectLeader pld = new ProjectLeader(10,"Mohammed",9000,"C++");
ProjectLeader pld2 = new ProjectLeader(20,"Ossma",5000,"pascal");
pld.addMember(p1);
pld.addMember(p4);
pld2.addMember(p3);
app.addMember(p1);
app.addMember(p2);
app.addMember(p3);
app.addMember(p4);
app.addMember(pld);
app.addMember(pld2);
System.out.println(app);
System.out.println("total salary : " + app.getTotalSalary());
}
}
الأمثله أعلاه مثالين صغيرين جدا ، وبالتالى من خلال الخبره يمكن لأي مبرمج البدء والشروع في هكذا مشروع من غير الحاجه للمخططات .. المخططات تستخدم في حال كنت تريد فهم المشكله لأنها متداخله وكبيره ، كنت تريد أن يفهم المبرمجين والمستخدمين طريقتك في الحل ، كنت تصمم البرنامج لكي يبرمجه غيرك ، كنت تعمل في مشروع كبير وبالتالى يجب عليك تطوير البرنامج بشكل متصاعد increment-iterative فبالتأكيد سوف تحتاج الى مخططات بعد الأنتهاء من دوره تطوير واحده one iteration.
المره القادمه باذنه تعالى نتناول مشكله معينه ونقوم بحلها ونستخدم المنهجيه الخفيفه التي سبق أن عرضناها سابقا ، بالاضافه الى عرض مفاهيم SOLIDفي التطوير ..
نقطه النهايه :
=-=-=-=-=-=-=
المبرمج المحترف لا يقتصر عمله في الCoding ، المبرمج المحترف يجيد موضوع الDesign بشكل كبير ، المبرمج المحترف يجيد تطبيق الاختبارات على الوحدات التي يكتبها Testing ، المبرمج المحترف يجيد تتبع البرامج وايجاد الأخطاء بها Debugging ، المبرمج المحترف يعرف مستوى أداء برنامجه Performance Analysis ويستطيع ايجاد نقطه الخلل والبطء فيه Performance Bottleneck كما يستطيع تحسين الكود Optimization بالاضافه الى كتابه برامج ذات كفائه عاليه Efficient code .
عالم البرمجه الموجهه كبير للغايه ، عليك أن تستفيد وتتعلم من أفكار غيرك ، عليك أن تعرف أهم المشاكل التي تحصل في مرحله التصميم وكيف يمكن تجنبها Design Pattern ، عليك أن لا تخترع العجله من جديد وأن تقوم باعاده استخدام الكود وخاصه الCollections التي تقدمها لغه البرمجه -ان كانت تقدم لك ذلك- .
موضوع التحليل والتصميم الكائني موضوع متقدم لذلك حاول أن تتعلم بالتدريج ، تعلم منهجيه معينه وكيف يمكن أن تستخدم UML بها ، ثم انتقل لغيرها بعد مده ، وبالتالى تزداد خبرتك في حل المشاكل وتطوير الحلول الكائنيه التوجه .
مصادر الموضوع :
=-=-=-=-=-=-=
Object-Oriented Design with UML and Java
Object-Oriented Analysis and Design - Understanding System Development with UML 2.0
Sams Teach Yourself Object Oriented Programming in 21 Days
Professional C++