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

  • 2021-12-29

Yiğit Kemal Erinç

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

The SOLID Principles of Object-Oriented Programming Explained in Plain English

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

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

این مقاله هر آنچه را که برای اعمال اصول SOLID در پروژه های خود نیاز دارید به شما آموزش می دهد.

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

پس یک فنجان قهوه یا چای بردارید و بیایید همان جا بپریم!

زمینه

اصول SOLID برای اولین بار توسط دانشمند معروف کامپیوتر رابرت جی. مارتین (با نام مستعار عمو باب) در مقاله خود در سال 2000 معرفی شد. اما مخفف SOLID بعدها توسط مایکل فیرز معرفی شد.

عمو باب همچنین نویسنده کتاب های پرفروش Clean Code و Clean Architecture است و یکی از شرکت کنندگان "Agile Alliance" است.

بنابراین، جای تعجب نیست که همه این مفاهیم کدنویسی تمیز، معماری شی گرا و الگوهای طراحی به نحوی با یکدیگر مرتبط و مکمل باشند.

همه آنها به یک هدف عمل می کنند:

"برای ایجاد کد قابل فهم، خوانا و قابل آزمایش که بسیاری از توسعه دهندگان بتوانند به طور مشترک روی آن کار کنند."

بیایید یک به یک به هر اصل نگاه کنیم. پس از مخفف SOLID، آنها عبارتند از:

  • اصل مسئولیت واحد
  • ای قلم بسته اصل
  • اصل جایگزینی L iskov
  • I nterface Segregation Principle
  • اصل وابستگی D

اصل مسئولیت واحد

اصل مسئولیت واحد بیان می کند که یک کلاس باید یک کار را انجام دهد و بنابراین باید تنها یک دلیل برای تغییر داشته باشد.

برای بیان این اصل به صورت فنی تر: فقط یک تغییر بالقوه (منطق پایگاه داده، منطق ورود به سیستم و غیره) در مشخصات نرم افزار باید بتواند بر مشخصات کلاس تأثیر بگذارد.

این به این معنی است که اگر یک کلاس یک محفظه داده است، مانند یک کلاس Book یا یک کلاس Student، و دارای فیلدهایی در مورد آن موجودیت است، تنها زمانی باید تغییر کند که مدل داده را تغییر دهیم.

پیروی از اصل مسئولیت واحد مهم است. اول از همه ، از آنجا که بسیاری از تیم های مختلف می توانند روی یک پروژه کار کنند و به دلایل مختلف همان کلاس را ویرایش کنند ، این می تواند به ماژول های ناسازگار منجر شود.

دوم ، کنترل نسخه را آسان تر می کند. به عنوان مثال ، می گویند ما یک کلاس پایداری داریم که عملیات پایگاه داده را انجام می دهد ، و ما شاهد تغییر در آن پرونده در تعهدات GitHub هستیم. با دنبال کردن SRP ، ما می دانیم که مربوط به ذخیره سازی یا موارد مرتبط با بانک اطلاعاتی است.

درگیری ادغام نمونه دیگری است. آنها وقتی تیم های مختلف همان پرونده را تغییر می دهند ظاهر می شوند. اما اگر SRP دنبال شود ، درگیری های کمتری ظاهر می شود - پرونده ها یک دلیل واحد برای تغییر دارند و درگیری هایی که وجود دارند ، برطرف می شود.

مشکلات و ضد الگوهای مشترک

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

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

این یک کلاس کتاب ساده با برخی زمینه ها است. چیز خاصی نیست. من زمینه ها را خصوصی نمی کنم تا نیازی به برخورد با گیرنده ها و تنظیم کننده ها نداریم و به جای آن می توانیم روی منطق تمرکز کنیم.

حال بیایید کلاس فاکتور را ایجاد کنیم که شامل منطق ایجاد فاکتور و محاسبه قیمت کل باشد. در حال حاضر ، فرض کنید که کتابفروشی ما فقط کتاب و چیز دیگری نمی فروشد.

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

  • روش Calculatetotal ، که قیمت کل را محاسبه می کند ،
  • روش printinvoice ، که باید فاکتور را برای کنسول چاپ کند ، و
  • روش SAVETOFILE ، مسئول نوشتن فاکتور به یک پرونده.

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

خوب پس اینجا چه خبر است؟کلاس ما اصل مسئولیت واحد را از جهات مختلف نقض می کند.

اولین تخلف روش PrintInvoice است که شامل منطق چاپ ما است. SRP اظهار داشت که کلاس ما فقط باید یک دلیل واحد برای تغییر داشته باشد و این دلیل باید تغییر در محاسبه فاکتور برای کلاس ما باشد.

اما در این معماری ، اگر می خواستیم قالب چاپ را تغییر دهیم ، باید کلاس را تغییر دهیم. به همین دلیل ما نباید منطق چاپ را با منطق تجارت در همان کلاس آمیخته کنیم.

روش دیگری وجود دارد که SRP را در کلاس ما نقض می کند: روش SaveTofile. همچنین این یک اشتباه بسیار رایج است که منطق ماندگاری را با منطق تجارت مخلوط کنید.

فقط از نظر نوشتن به یک پرونده فکر نکنید - این می تواند در یک پایگاه داده ، برقراری تماس API یا سایر موارد مربوط به پایداری ذخیره شود.

بنابراین چگونه می توانیم این عملکرد چاپ را برطرف کنیم ، ممکن است بپرسید.

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

ما 2 کلاس ایجاد می کنیم ، فاکتور و فاکتور فاکتور و روش ها را جابجا می کنیم.

اکنون ساختار کلاس ما از اصل مسئولیت واحد پیروی می کند و هر کلاس مسئول یک جنبه از برنامه ما است. عالی!

اصل بسته

اصل بسته بسته مستلزم آن است که کلاس ها برای تمدید و بسته به اصلاح باز باشند.

اصلاح به معنای تغییر کد یک کلاس موجود است و پسوند به معنای افزودن عملکرد جدید است.

بنابراین آنچه این اصل می خواهد بگوید این است: ما باید بتوانیم بدون لمس کد موجود برای کلاس ، قابلیت های جدیدی را اضافه کنیم. این امر به این دلیل است که هر زمان که کد موجود را اصلاح می کنیم ، خطر ایجاد اشکالات احتمالی را در نظر می گیریم. بنابراین ما باید در صورت امکان از دست زدن به کد آزمایش شده و قابل اعتماد (بیشتر) استفاده کنیم.

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

اکنون که اصول اولیه را پوشش داده ایم ، بیایید آن را در برنامه فاکتور خود اعمال کنیم.

بیایید بگوییم رئیس ما به ما آمد و گفتند که آنها می خواهند فاکتورها در یک پایگاه داده ذخیره شوند تا بتوانیم به راحتی آنها را جستجو کنیم. ما فکر می کنیم خوب ، این رئیس ساده Peasy است ، فقط یک ثانیه به من بدهید!

ما پایگاه داده را ایجاد می کنیم ، به آن وصل می شویم و یک روش ذخیره را به کلاس فاکتور خود اضافه می کنیم:

متأسفانه ، ما به عنوان توسعه دهنده تنبل برای فروشگاه کتاب ، کلاس ها را طراحی نکردیم تا در آینده به راحتی قابل گسترش باشند. بنابراین برای افزودن این ویژگی ، ما کلاس فاکتور را اصلاح کرده ایم.

اگر طراحی کلاس ما از اصل بسته باز پیروی می کرد ، نیازی به تغییر این کلاس نداریم.

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

ما نوع فاکتور را برای رابط و اضافه کردن یک روش ذخیره تغییر می دهیم. هر کلاس پایداری این روش ذخیره را پیاده سازی می کند.

بنابراین ساختار کلاس ما اکنون به این شکل است:

SOLID-Tutorial-1-1024x554

اکنون منطق پایداری ما به راحتی قابل گسترش است. اگر رئیس ما از ما بخواهد پایگاه داده دیگری اضافه کنیم و 2 نوع پایگاه داده مانند MySQL و MongoDB داشته باشیم ، ما به راحتی می توانیم این کار را انجام دهیم.

ممکن است فکر کنید که ما فقط می توانیم چندین کلاس بدون رابط ایجاد کنیم و یک روش ذخیره به همه آنها اضافه کنیم.

اما بیایید بگوییم که ما برنامه خود را گسترش می دهیم و چندین کلاس پایداری مانند فاکتور بودن ، کتابفروشی داریم و یک کلاس مداوم ایجاد می کنیم که تمام کلاسهای پایداری را مدیریت می کند:

اکنون می توانیم هر کلاس را که رابط فاکتور را با کمک پلی مورفیسم به این کلاس پیاده سازی می کند ، منتقل کنیم. این انعطاف پذیری است که رابط ها ارائه می دهند.

اصل تعویض لیسکوف

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

این بدان معنی است که ، با توجه به اینکه کلاس B یک زیر کلاس کلاس A است ، ما باید بتوانیم یک شیء کلاس B را به هر روشی که انتظار یک شیء کلاس A را دارد منتقل کنیم و روش نباید در این حالت خروجی عجیب و غریب ارائه دهد.

این رفتار مورد انتظار است ، زیرا وقتی از میراث استفاده می کنیم ، فرض می کنیم که کلاس کودک هر آنچه را که ابرقهرمان دارد به ارث می برد. کلاس کودک رفتار را گسترش می دهد اما هرگز آن را باریک نمی کند.

بنابراین ، هنگامی که یک کلاس از این اصل پیروی نمی کند ، منجر به برخی از اشکالات نامطبوع می شود که تشخیص آن دشوار است.

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

ما یک کلاس مستطیل ساده و یک عملکرد GetArea داریم که ناحیه مستطیل را برمی گرداند.

اکنون تصمیم می گیریم کلاس دیگری برای مربع ها ایجاد کنیم. همانطور که ممکن است بدانید ، یک مربع فقط یک نوع خاص از مستطیل است که عرض آن برابر با ارتفاع است.

کلاس مربع ما کلاس مستطیل را گسترش می دهد. ما ارتفاع و عرض را به همان مقدار در سازنده تنظیم می کنیم ، اما ما نمی خواهیم مشتری (کسی که از کلاس ما در کد خود استفاده می کند) قد یا وزن را به گونه ای تغییر دهد که بتواند خاصیت مربع را نقض کند.

بنابراین ما تنظیم کننده ها را نادیده می گیریم تا هر زمان که یکی از آنها تغییر کند ، هر دو ویژگی را تنظیم کنیم. اما با این کار ما به تازگی اصل تعویض لیسکوف را نقض کرده ایم.

بیایید یک کلاس اصلی برای انجام تست در عملکرد GetArea ایجاد کنیم.

تستر تیم شما به تازگی با عملکرد تست GetAreatest روبرو شده است و به شما می گوید که عملکرد GetArea شما نتوانسته است آزمون اشیاء مربع را پشت سر بگذارد.

در آزمایش اول ، یک مستطیل ایجاد می کنیم که عرض آن 2 و ارتفاع 3 است و با GetAreatest تماس بگیرید. خروجی 20 همانطور که انتظار می رود ، 20 است ، اما وقتی در مربع می گذریم همه چیز اشتباه می شود. این امر به این دلیل است که فراخوانی به عملکرد Setheight در آزمون ، عرض را نیز تنظیم می کند و منجر به خروجی غیر منتظره می شود.

اصل تفکیک رابط

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

در این اصل آمده است که بسیاری از رابط های خاص مشتری بهتر از یک رابط کاربری عمومی هستند. مشتریان نباید مجبور شوند عملکردی را که نیازی به آن ندارند ، پیاده سازی کنند.

این یک اصل ساده برای درک و کاربرد است ، بنابراین یک مثال را ببینید.

ما یک پارکینگ بسیار ساده را مدل کردیم. این نوع پارکینگ است که در آن هزینه ساعتی پرداخت می کنید. حال در نظر بگیرید که ما می خواهیم یک پارکینگ رایگان را اجرا کنیم.

رابط پارکینگ ما از 2 چیز تشکیل شده است: منطق مربوط به پارکینگ (ماشین پارک ، ماشین Unpark ، ظرفیت دریافت) و منطق مربوط به پرداخت.

اما خیلی خاص است. به همین دلیل ، کلاس FreeParking ما مجبور شد روشهای مربوط به پرداخت را که بی ربط هستند ، پیاده سازی کنند. بیایید رابط ها را جدا یا جدا کنیم.

SOLID-Tutorial-1024x432

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

اکنون مدل ما بسیار انعطاف پذیر ، قابل گسترش تر است و مشتری ها نیازی به اجرای منطق بی ربط ندارند زیرا ما فقط عملکردهای مرتبط با پارکینگ را در رابط پارکینگ ارائه می دهیم.

اصل وارونگی وابستگی

اصل وارونگی وابستگی بیان می کند که کلاسهای ما به جای کلاس ها و توابع بتونی باید به رابط ها یا کلاس های انتزاعی بستگی داشته باشند.

در مقاله خود (2000) ، عمو باب این اصل را به شرح زیر خلاصه می کند:

این دو اصل در واقع مرتبط هستند و ما قبلاً از این الگوی استفاده کرده ایم در حالی که در مورد اصل بسته بسته بحث می کردیم.

ما می خواهیم کلاس های ما به صورت تمدید باز باشد ، بنابراین ما وابستگی های خود را دوباره سازماندهی کرده ایم تا به جای کلاس های بتونی به رابط ها بستگی داشته باشیم. کلاس PersistenceManager ما به جای کلاس هایی که آن رابط را پیاده سازی می کنند به فاکتور بودن بستگی دارد.

نتیجه

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

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

من پیشنهاد می کنم این اصول را هنگام طراحی ، نوشتن و اصلاح کد خود در نظر داشته باشید تا کد شما بسیار تمیز تر ، قابل گسترش و قابل آزمایش باشد.

اگر علاقه مند به خواندن مقالات بیشتر از این دست هستید ، می توانید در لیست پستی وبلاگ من مشترک شوید تا هنگام انتشار مقاله جدید به آن اطلاع دهید.

ثبت دیدگاه

مجموع دیدگاهها : 0در انتظار بررسی : 0انتشار یافته : ۰
قوانین ارسال دیدگاه
  • دیدگاه های ارسال شده توسط شما، پس از تایید توسط تیم مدیریت در وب منتشر خواهد شد.
  • پیام هایی که حاوی تهمت یا افترا باشد منتشر نخواهد شد.
  • پیام هایی که به غیر از زبان فارسی یا غیر مرتبط باشد منتشر نخواهد شد.