Design Patterns - Strategy Pattern





الشرح هيكون بطريقة بسيطة بحيث اي حد يقدر يفهم ان شاء الله. وكل تدوينة بتشرح pattern هتكون self-contained لوحدها يعني مش هتحتاج تقرا حاجه تانيه قبلها , الا بس لو لسه مش عارف يعني ايه design pattern  وايه فايدتها وليه تستخدمها اقرا الـintro   الاول من هنا:
 Design patterns - intro

الـ Strategy Pattern

ندخل فالموضوع علي طول , احسن طريقة من وجهة نظري لفهم اي design pattern  هي مثال عليه. فالأول هنشوف مثال وهنشوف الدنيا من غير design pattern  هتبقي عاملة ازاي و ايه مشاكلها وازاي الـstrategy  هتحلها و تديك فوايد تانيه عليها كمان. الأمثلة هتكون جافا بس لو متعرفش جافا متقلقش الدنيا هتكون بسيطة يكفي معرفتك بالـ OOP


المثال:  افترض ان عندك class dog وواحد تاني bird و ليهم parent  للحيوانات عموما اسمها animal الاتنين subclasses  منها.
و animal  فيها الصفات المشتركة للحيوانات عموما زي ان كلها ليها اسم و طول و وزن وسرعة وهكذا وكمان عندها التصرفات behavior  المشتركة بتاعة الحيوانات زي انها بتتحرك وبتاكل وتتنفس مثلا.
وعملنا override  لـلـ move( ) function  في الـ bird class  لأن الطائر بيتحرك بطريقة مختلفة غير بقية الحيوانات و عملنا extend  لـ fucntion  جديدة في dog class  بتخليه يحفر حفرة dig hole( )
دي مجموعة حاجات عشوائية هنبني عليها المثال بتاعنا.
Animal.java


Bird.java


Dog.Java


عاوزين ندي الـ bird والحيوانات اللي كلها subclasses  من animal  القدرة عالطيران
دلوقتي هنتعامل بحاجات الـ oop اللي احنا عارفينها كأننا منعرفش design patterns و دي الجزئية اللي هنتخيل فيها الـ Bad code

الطبيعي و اول حاجه اي حد هيفكر فيها هي انه يضيف method fly( )  في animal class  اللي هي الـ parent  اللي كل الـحيوانات بتـ inhert  منها.



طيب ليه ده يعتبر bad code  ؟

اوعي ابدا تضيف اي method  لـ parent  زي animal  لو مش بتنطبق علي كل الـ childs  بتوعها , لازم تفرق بين الاختلافات بتاعة الـ childs  ومابين الـ parent  بتاعها.

ممكن حد يفكر طيب ما انا ممكن اضيفها فالـ parent  عادي و اجي في class  زي dog اللي مبيطرش و أجي اعمل override للـ method  دي و احط implementation  مختلف !






لأن مش صح انك تفرض
action  معين علي كل الـ childs  اللي معندهمش القدرة علي الطيران وكمان لأننا هنضطر كل اما نعمل اي حيوان جديد اننا نعمل override  للـ method  دي. (فكر دايما في Less code  )



واحد هيسأل سؤال تاني: طيب ليه منحطش الـ method  دي جوا الـ bird  نفسها و نريح دماغنا ؟

ببساطة لأن هيبقي عندك كود متكرر كتير جدا لو في animals  تانيه هيكون عندها القدرة علي الطيران , وبكده تبقي بتنافي مبدأ انك كـ professional  تكتب less code  وكمان لو اي حيوان مالحيوانات تطور وبقي عنده القدرة عالطيران هتفضل تعدل فيه يبقي انت كده بتنافي مبدأ تاني انك تكون professional  وهو الـ maintainablilty  , واحد هيقولي وهو الكلب هيطير ؟

ده احتماله ضعيف فالـ real world  فعلا بس افترض معايا انك بتبرمج لعبة وفي level  ما الكلب بقي عنده القدرة عالطيران ؟ ساعتها هتضطر تكرر نفس الكود.
واحد تاني ممكن يفكر اعمق شوية ويقول انا ممكن اعمل interface  و ليكن flys  مثلا واخلي كل الـ childs ينفذوه بطريقتهم وده برضه غلط مش لنفس السبب.

مبدأين أساسيين لازم تحطهم في دماغك دايما وانت بتكتب كود:

  •         ابعد عن اي حاجه تخليك تعمل تكرار فالكود.
  •         ابعد عن اي حاجه تخلي ان تغير في class  يأثر علي بقية الـ classes  , اي تغيير فالـ parent  المفروض ميبوظش الكود بتاع الـ childs  والعكس صحيح.

لو لاقيت اي موقف بتعمل فيه حاجه من الاتنين دول يبقي انت بتفكر فالموضوع بطريقة مش صح وبتكتب bad code.


طيب الـ Strategy Pattern  ممكن تحللنا المشكلة دي ازاي ؟

اول حاجه هنعمل interface  بس هنستخدمه بطريقة مختلفة خالص وهنسيمه flysوهنعمل كمان 2 classes  واحدة اسمها ItFlys  والتانيه CantFly و الاتنين هيعملوا implement  للـ interface  اللي اسمه flys  بس كل واحد بطريقته والكود هيبقي شكله كده:



 
ليه دي فكرة حلوة  ؟الفكرة هنا اننا غلفنا كلalgorithm  بتاع behavior معي زي  اللي يقدر يطير واللي ميقدرش كل واحد جوا class  لوحده وبكده نقدر نعمل مليون طريقة للطيران بدون ما نأثر علي animal  او اي subclass  ليها , ببساطة لأننا فصلنا الـ behavior  عن الـ clients  اللي هما الـ classes بتوع الحيوانات في حالتنا دي.

دلوقتي نقدر نضيف Instance Variable  من نوع الـ interface flys  جوا الـ parent class  اللي هي  animal  ونقدر لكل child class  نديله القدرة او عدم القدرة عالطيران  اللي هما ال 2 classes اللي عملناهم ItFlys  و CantFly كالأتي:



اول حاجه عملت Instance Variable  في الـ parent  من نوع الـ interface Flys و سميته flyingTybe.

معلومة سريعة: دي حاجه اسمها composition  وهو ان الـ class  بدل ما تاخد الـ abilities  بتاعتها عن طريق الـ inheritance  بتشيل objects  من انواع تانيه موجودة فيها الـ abilities  دي built-in.
الـ composition  ليه ميزة تانيه رهيبة جدا وهو انه بيمكنك تغير الـ behavior  في اي وقت اثناء الـ run time  و بدون اي side- effect  علي الـ subclasses  او الـ super classes
يعني لو حيوان ما معندوش القدرة عالطيران وفجأة طلعله جناجات تقدر تغير الـ flyintTybe  بتاعه من CantFly  تخليه ItFlys من غير ما تحتاج تعدل اي حاجه تانيه.

تاني حاجه اننا عملنا  method  وسميناها tryToFly( )
وجواها ناديت علي  function fly( ) بتاعة الـ flyingTybe object  ايا كان نوعه بقي لأن فالنهاية كل الـ algorithms  اللي هيا CantFly  و ItFlys  كلهم subclasses  من الـ interface Flys.
وبعد كده عملت setter  للـ flyingTybe  وسميته setFlyingAbility  بياخد object  من النوع Flys  او اي subclass  ليه

بعد كده كل اللي محتاج اعمله سوا ء في Dog calss  او Bird class  بسيط جدا وهو اني اساوي قيمة الـ flyingTybe  بنوع من اتنين يا ItFlys  يا CantFly  و هنا تظهر قيمة الـ polymorphism  لأن flyingTybe  هو object  من نوع الـ interface Flys  لكن في نفس الوقت لما اساويه بأي subclass  للـ interface  و انادي علي method fly( )  هينادي الـ method  بتاعة الـ subclass
فببساطة كل اللي هنعمله اننا هندخل جوا الـ constructors  بتاع كلا من bird , dog  وهكتب سطر كود جوا كل واحد فيهم (less code)   وهيبقي شكلهم كالأتي:
Dog.java

Bird.java

ودلوقتي بقي نجرب الكلام ده فالـ main  ونعمل run  ونشوف الناتج

 Main.java

عملت 2 objects  من نوع animal  وعلمت واحد بـ new dog  وسميته sparky  و واحد بـ new bird  وسميته tweety وبعد كده طبعت الناتج بتاع الـ TrytoFly( )  بتاعة sparky  و tweety  وده كان الـ output:

Dog: I Can’t fly
Bird: Flying High

طيب لو فجأة الكلب قرر يطور من نفسه ويطير ؟ هنا تظهر ميزة تانيه للـ strategy pattern  وهي انك تقدر تغير الـ behavior  بتاع الـ objects  فالـ runtime  كالاتي:

مجرد اني ناديت الـ method  اللي اسمها setFlyingAbility( )  واديتها new ItFlys  الكلب بقي عنده القدرة يطير
(Thanks to polymorphism) 
وده شكل الـ out put :
Dog: I Can’t fly
Bird: Flying High
Dog: Flying High

كده المثال خلص , نبص عليه بصة شاملة عن طريق Uml diagram  وبعدين نطلع الـ concept  بتاع الـ strategy pattern  من المثال.


الملخص:  عندنا parent  اسمه animal  فيه instance variable  اسمه flyingTybe  من نوع الـ interface Flys و الـ + فالـ uml  يعني public  وعندنا 2 childs اسمهم Bird & Dog و عملنا 2 Algorithms  وعملنالهم encapsulate  جوا 2 classes  اللي هما CantFly & ItFlys و بقوا interchangeable  يعني الحيوانات تقدر تبدل بينهم بطريقة seamless  فالـ runtime.

الـ Strategy Concept

ببساطة هو إنك تعرف مجموعة من الـAlgorithms  وتـ encapsulate  كل واحدة لوحدها وتبدل بينهم dynamically  مع اختلاف الـ scenario . يعني بتمكنك تغير الـ Algorithm  بطريقة مستقلة بعيدا عن الـ clients  اللي بيستخدموها.


طيب امتي تستخدم الـ Strategy Pattern  ؟

لما تكون محتاج تعرف Class  هيكون عندها behavior  مشابه لـ مجموعة behaviors  تانيه. يعني في المثال بتاعنا الحاجه المشتركة هي فكرة الطيران سواء القدرة او عدم القدرة عليهم او مثلا انهم بيطيروا بطرق مختلفة.
خليني اوضحها بطريقة ابسط شوية
استخدم الـ strategy pattern  لما تكون عاوز تدي الـ objects  بتاعتك القدرة علي انها تختار بين مجموعة behaviors  زي مثلا
  •          Fly with wings
  •           Don’t fly
  •           Flay fast

واكيد تستخدم الـ Strategy  لما تكون محتاج تغير بين الـ behaviors  بطريقة  dynamic
وكمان شوية هتحس بيها مع الاستخدام حاجات تانيه زي:
  •          بتقلل استخدمك لـ conditions  طويلة
  •           بتقلل تكرار الكود
  •           لما بتغير في class  مبتأثرش علي بقية الـ  classes
  •           تقدر تخفي secret code  جواها عن الـ user
عيبها الوحيد انها بتزود عدد الـ classes  و الـ objects وده ميعتبرش عيب مؤثر.


لو عندك أي تعليق او سؤال ممكن تعمل كومنت بالـ facebook بتاعك تحت التدوينة و ممكن تبعتلي علي الـ facebook بتاعي من هنا. ولو الموضوع ممكن يفيد حد من اصدقائك اعمله share.
Share on Google Plus

About Ahmed Mabrook

Software engineer. Mobile applications developer,books worm and i write every once in a while.
    Blogger Comment
    Facebook Comment