چیزی که ما تا الان شنیدم در مورد شی گرایی اینه که خیلی خوبه و همیشه کار راه انداز هستش, ولی واقعیت اینه که همیشه این طوری نیست که بخواد کار راه انداز باشه و باعث کاهش حجم کد بشه اتفاقا بعضی جاها ممکنه استفاده از ویژگی های شی گرایی باعث افزایش حجم کد و سخت تر شدن نگه داری کد بشه
فرض رو میزاریم که به ما گفتن قراره یه سیستم نرم افزاری جدید طراحی کنیم( اینجا سعی میکنم اول با یه بازی شروع کنیم تا بعدا اگه فرصت شد توی دنیای واقعی این مسائل رو مطرح کنیم) وقتی میریم و در مورد این سیستم صحبت میکنیم متوجه میشیم که قراره یه بازی خیلی ساده طراحی کنیم
حالا این بازی چی هستش ؟ قرار شده یه بازی جنگ ستارگان طراحی کنیم(حالا چرا این بازی؟ آبجکت هایی که داریم داخل بازی به مرور هم پیچیده تر میشن هم سلاح های جدید به بازی اضافه میشه همون چیزی که باعث میشه ارث بری عملا باعث افزونگی کد ها و سخت تر شدن نگه داری برنامه بشه)
خب توی این بازی ما نیاز به یه سری اسلحه این بازی داریم که ما اسمشو میزاریم Weapon, چیزی که تا اینجا مشخص هستش اینه که اولین متدی که توی این کلاس لازم داریم متدی برای شلیک هستش
الان ما خیلی راحت میتوینم کلاس های مختلف اسلحه خودمون رو داشته باشیم و از این کلاس ارث بری کنیم, ولی نه صبر کنید, اگه ما یه سبک اسلحه جدید بخوایم به سیستم اضافه کنیم که یه متد دیگه لازم داشته باشه باید چیکار کنیم؟ آیا باید به این کلاس پایه اضافه کنیم یا باید یه کلاس جدید تعریف کنیم و از اون ارث بری کنیم؟برای ساده تر شدن درک این مسئله در نظر بگیریم که اسلحه های کلاسیکی که میخوایم استفاده کنیم تنها نیاز به شلیک دارن ولی اسلحه های مدرن کلاس 1 نیاز به Init شدن دارن, بعد از مدتی کلاس 2 هم به این دسته اضافه میشن که به غیر از Init شدن نیاز دارن تا متد دیگه ای به اسم Targeting بهشون اضافه بشه که وظیفش هدف گیری خودکار هستش, چه اتفاقی میفته اگه کلاس اسلحه های لیزری به این دسته اضافه بشه که به غیر از Shoot نیاز به یه متد دیگه به اسم Connect داشته باشه؟(اسم متد ها همه نمونه هستش خیلی روی عناوین متد ها حساسیت به خرج ندین)
تا اینجای کار ما به چند حالت مختلف رسیدیم: 1) متدی که توی همه زیر کلاس هایی که از Weapon ارث بری کرده لازم هستش, 2) متد هایی که فقط تو یه سری از کلاس ها فقط لازم هستش و ممکنه توی شاخه های مختلف بعضی از کلاس ها اصلا به متد هایی که Parent اون کلاس لازم داشته نیاز پیدا نکنه
اینجا همون جایی هستش که مشکل به وجود میاد و باعث میشه به فکر یه راهکار دیگه باشیم و سعی کنیم از ارث بری استفاده نکنیم
خب از اونجایی که فکر میکنم ممکنه خود این مثال یه خورده فهم مسئله رو خیلی آسون نکنه از یه مثال دیگه که فقط تصویرش پایین هستش استفاده میکنم:
توی مثال بالا فرض کنین که فقط Ryu توی این بازی قادر هستش که پرش کنه و عملا این متد برای Ken و ChunLi اضافه هستش, یا ChunLi و Ryu فقط نیاز به متد Kick دارن, خب الان یه مشکل دیگه ای که بهش برمیخوریم این هستش که ضرباتی که قراره ChunLi بزنه باعث انتقال برق به بدن حریف بشه در حالی که ضربات Ryu ساده هستش
اگه کمی به این دو تا مثال بیشتر دقت کنید متوجه میشن که مشکل از کجا شروع میشه, اگه بخوایم توی یه جمله خلاصه کنیم:
اگه توی ساختار ارث بری به متد هایی برسیم که فقط توی یه درصد خاصی از کلاس ها به اون ها نیاز داشته باشیم این مشکل بروز میکنه.
بنابراین یه بار دیگه کد هاتون رو نگاه کنین و اگه جایی دیدین که مجبور شدین متدی رو پیاده سازی بکنین که بر فرض مثال در 40 درصد کلاس ها استفاده بشه و یا به متدی رسیدین که مجبور شدین توی چند کلاس مختلف کپی پیست کنین ادامه این آموزش رو بخونین چون میخوایم این مشکل رو حل کنیم.
کلید گم شده این مسئله مفهومی هستش به اسم Composition, در مواردی که نیاز داشته باشیم تا یک سری کد رو فقط توی کلاس های مشخصی استفاده بکنیم به جای ارث بری وظیفه اجرای کد های مورد نظر رو به استراتژی میسپاریم, قبل از این که بخوام توضیحات اضافه تری بدم نمودار مربوط به این الگو رو نگاه کنید:
برای رفع این مشکل ما اومدیم و رفتار هایی که میدونستیم باعث بروز این مشکل میشند رو جدا کردیم و برای اونها اینترفیس های مربوطه رو طراحی کردیم و یک آجکت از اینترفیس رو توی کلاس پایه قرار دادیم, در مرحله بعدی رفتارهایی که نیاز داشتیم رو توی کلاس های جدیدی طراحی کردیم که قراره بعدا به کلاسی که نیاز به این رفتار داره تزریق کنیم, از اونجایی که قراره استراتژی به صورت Interface و یا Abstract class پیاده سازی بشه ما هر رفتاری که نیاز داشته باشیم میتونیم به کلاس های استفاده کنه تخصیص بدیم تا در زمان اجرا بسته به شرایط رفتار مشخصی اجرا بشه.
صادقانه بخوایم برخورد کنیم این روش هم مشکلات خاص خودش رو داره:
اولی مشکلات رو گفتیم الان بریم ببینیم چه مزایایی رو در اختیار ما قرار میده:
چون مطلب طولانی شد فقط برای تموم کردن این مطلب یه تصویر از یه مسئله واقعی که میتونه به کمک این الگو پیاده سازی بشه رو قرار میدم.
این فقط یک نمونه از استفاده از این الگو در دنیای واقعی هستش که میتونیم در نظرش بگیریم(مسئله محاسبه هزینه رفتن از مقصد A به مقصد B) و توی پایین چند دسته مسئله که باید از این الگو استفاده کنیم:
اگر فرصتی شد توی یک مثال عملی این الگو رو توی مقاله دیگه ای می نویسم.