تقابل مفاهیم SOLID روی کدمون ؟؟

مروری بر مفاهیم OOP و تفاوت هایکه پیروی کردن از اونها روی یک کد ایجاد میکنه.

چهار مفهوم میخوایم برسی بکنیم

- Abstraction (Abstract)

- Concretion (Concrete)

- Instantiation (Instance)

- Injection (Dependency Injection)

که هر چهار مورد رابطه مستقیمی برهم دارن که اگه این موارد رعایت بکنیم یک کد خیلی خوب و با کیفیتی خواهیم داشت طبق نموداری که براتون کشیدم

برای اینکه بتونیم این مفاهیم بصورت منسجم توضیح بدیم بیاید یک بازی ساده فرض کنیم تو این بازی ساده ما دوتا موجودیت داریم

- Player (بازیکن):

این موجودیت نشون دهنده بازیکنان بازی هستش که می‌تونن یا ربات یا انسان باشن.

این موجودیت ویژگی‌هایی مثل اسم (name) و نوع (type) داره.

پلیر می‌تونه وظایفی مثل حرکت کردن، ضربه و... انجام بده براساس نوعی که انتخاب شده براش.

- Playground (زمین بازی):

این موجودیت نشون دهنده محیط بازی هستش که دوتا Player میتونن داخلش بازی بکنن.

زمین بازی مسئول مدیریت بازی و انجام اعمال مربوط بهش هستش، مثل شروع بازی، اتمام بازی و...

خب این پایه ترین حالت ممکن هستش درواقع تمام دردسر ها از اینجایی شروع میشن که شما چطور این دوتا قراره به هم وصل و پیاده سازی بکنید و اینجا جای هستش که مفاهیمی که بالا تر اشاره کردم در مقابل هم یا کنار هم قرار میگیرن.

ولی قبل اینکه چطور براساس این مفاهیم کد خودمون بنویسیم بیاید ببینیم چطور این مفاهیم در مقابل و یا کنار هم قرار میگیرن.


Concretion VS Abstraction:

این دو مفهوم خیلی باهم رابطه مستقیمی دارن و زیاده روی کردن تو هرکدومش میتونه معایب خودش داشته باشه ولی یک نقل قول معروف داریم که میگه :

Depend on abstractions, not on concretions

فارسیش میشه: "بر انتزاعات وابستگی داشته باشید، نه به تجسم‌ها."

در واقع چیزیکه این کوت میخواد بهمون برسونه این هستش طوری کد نزنیم که با یک تغییر کوچیک یا بزرگ کل سیستم از کار بیوفته و کدمون مثل سنگ سفت و غیرانعطاف پذیر باشه, و نیاز باشه خیلی جاها اپدیت بکنیم.

زیاد نمیخوام درمورد این موضوع بپردازم چون قبلا درموردش چندتا مقاله نوشتم و با جزییات درموردش بحث شده که بالاتر میتونید پیدا بکنیدشون قبل از ادامه دادن.

[لینک مقاله]


Instantiation VS Injection:

منظور از instance و inject چیه ؟ در واقع توی بازی که داشتیم ما یک Playground داشتیم که نیاز به دوتا Player داشت خب کاریکه این Playground میکنه این هستش که میاد دوتا instance از Player داخل خودش ایجاد میکنه و ازشون استفاده میکنه و موقع ساخت Playground از ما میخواد در کنار اطلاعاتی که برای ساخت خودش نیازه اطلاعات دوتا player هم بهش پاس بدیم تا بتونه ازشون Instance بسازه و استفاده بکنه. تو اکثر موارد اینکار بشدت غیرحرفه ای هستش و باعث میشه کد و سیستمی که اینطوری طراحی شده بشدت توسعه پذیری و تست نویسی براش مشکل و تو مواردی هم غیر ممکن باشه.


فرض کنید Player های ما از فردا یک ورودی جدید به اسم color که رنگ پلیر ما مشخص میکنه میگیره, خب اینجا نیازه تمام کد بروزرسانی بکنیم و حتی خود Playground هم اپدیت بشه, در صورتیکه اگه از Injection استفاده میشد هیچکدوم از این مشکلات ایجاد نمیشد و خب چطور ؟

خیلی ساده ما بجای اینکه داخل Playground بیایم instance از پلیر های خودمون ایجاد بکنیم میایم instance هایکه بیرون از Playground ساختم به عنوان ورودی بهش پاس میدیم,

و دیگه Playground هیچ مسئولیتی برای ایجاد Player نداره.

و اگه فردا نیاز بود برای Playground تست نویسی هم بشه ما میتونیم بهش Player های فیک بدیم و درگیر ایجاد Instance از Player نشیم, و حتی باگ پذیریی کد هم بشدت پایین میاد و Playground فقط یک کار میکنه.

میتونیم درمورش خیلی بیشتر صحبت کنیم ولی برای تاپیک الا همینثقدر کافیه.

در واقع تو پاراگراف بالا به خیلی از موارد SOLID اشاره شدش و خوندنش بی نصیب نیست.


حالا که یک تعریف مختصر از مفاهیمی که مهمن داریم بیاید روی نمودار ببینیم کدمون چطور میشه و انالیزش بکنیم. بیاید اول از سمی ترین حالت ممکن شروع کنیم


Concretion ➕ Instantiation:

Instantiation:

خب همینطور که میبینید ما موقع ساخت Playground اطلاعات دوتا Player هم میگیریم تا بتونیم توی ابجکت ساخته شده دوتا instance از پلیر هامون با اون اطلاعات بسازیم و توی Playground مون استفاده کنیم ازش

پ ن: تصور کنید از فردا نیازه سه تا Player داشته باشیم :)

Concretion:

خب از اونطورف هم Player میبینید که توی بدنه اش اومده و شرط گذاشته که نوعش چیه و براساس اون الگوریتم های متفاوت اجرا بکنه مثلا برای نوع انسان باید منتظر ورودی از سمت کاربر باشه ولی برای نوع ربات نیازی به اینکار نیست و سریع میتونیم حرکت بعدی محاسبه کنیم

پ ن: تصور کنید که Playground هم براساس نوع پلیر یکسری شرط های داره



Concretion➕Injection:

خب توی این روش میبنید که ما بجای اینکه داخل خود Playground بیایم instance های پلیرهامونو بسازیم اومدیم و بیرون از اون ساختم, و مسئولیت ساخت پلیر ها دیگه Playground برعهده نداره, و ما فقط پلیر های ساخته شده بهش پاس میدیم موقع ساخت و اگه تعداد پلیر ها بیشتر بشه فقط نیازه که instance های بیشتری بسازیم و بهش پاس بدیم,

در همین حین ویژگی های جالبی مثل اشتراک گذاشتن Player ها بین اجزای مختلف کد داریم مثلا به چند Playground متفاوت میتونیم پلیر های یکسانی بفرستیم, یا مثلا موقع تست نوشتن برای Playground های خودمون یا اگه موجودیتی به اسم Team داشتیم که نیاز به دریافت پلیر ها برای ساخت تیم داشت ازشون استفاده بکنیم.

دیدید که مشکل Instantiation چطور با تکنیک Injection حل شد و ویژگی های خوبی به کدمون اضافه کرد ولی ما هنوز مشکل Concretion داریم



Concretion➕ Abstraction:

خب اگه به تصویر نگاه کنید دیگه ما توی Player شرطی به شکل اگه نوع ربات بود اینکار بکن یا نوع انسان بود اینکار بکن نداریم, بلکه با abstract کردن Player و ارث بری از Player ما دو نوع مختلف پلیر که هرکدوم الگوریتم ها و حتی پراپرتی های متفاوتی, مثل میزان سلامتی سرعت و قدرت میتونن داشته باشن ساختیم, و موقع ساخت ما نوع پلیر به عنوان ورودی دریافت نمیکنیم بلکه از ابجکتی که اون نوع میسازه instance ایجاد میکنیم این یکی از مهمترین و پایه ترین ويژگی های هستش که توی OOP داریم و قبلا چندین مقاله اموزشی درموردش نوشتم که بالاتر میتونید پیدا بکنید.

[لینک مقاله]


ولی همینطور که میبنید Playground اوقات سختی داره میگذرونه چون الا باید نه تنها اطلاعات ساخت Player ها موقع ساختش دریافت بکنه بلکه باید با توجه به نوع پلیر که براش ارسال کردیم از کلاس درست برای ساخت instance استفاده بکنه, که خودش باعث ایجاد باگ و افزایش هزینه نگهداری ازش میشه و اگه فردا پلیر نوع GOD اضافه بکنیم به سیستم باید Playground هم اپدیت بکنیم و همه چیز پیچیده تر میشه



Injection ➕ Abstraction:

خب حالا چی میشه ما همزمان هم از تکنیک injection و abstraction استفاده کنیم ؟؟ این میشه که ما هم خدا داریم و هم خرما :)

خب توی تصویر میبینیم که مشکل ما بشدت ساده شده و دیگه خبری از اون همه شرط و ورودی موقع ساخت نیست, و حتی Playground میتونه با هر نوع Player بدون اپدیت و دستکاری کردنش کار بکنه, و تنها شرطش هم این هستش که پلیری که بهش میدید از Player ارث بری یا نوعش Player باشه.

و اگه فردا پلیر God به سیستم اضافه کردید اصلا نیازی به اپدیت کردن Playground ندارید در کنار تمام فوایدی که قبلا درموردش صحبت کردیم

و این ترکیب وقتی استفاده میکنیم به این کوت میرسیم

Program to interfaces NOT implementations



نتیجه گیری:

اگه همه این مواردی که صحبت کردیم درموردش روی نمودار بزاریم با این تصویر روبه رو میشیم

کانال تلگرامی من : لینک

ایدی کانال: EXPLAIN_TO_YOURSELF