مقالات

بسته / بسته ، طبق اصل SOLID

نهادهای نرم افزاری (کلاس ها ، ماژول ها ، توابع و غیره) باید برای پسوند باز باشند ، اما برای ویرایش بسته هستند.

طراحی نرم افزار: ماژول ها ، کلاس ها و توابع به گونه ای که در صورت نیاز به قابلیت های جدید ، نباید کد موجود را اصلاح کنیم بلکه کد جدیدی بنویسیم که توسط کد موجود استفاده شود. این ممکن است عجیب به نظر برسد ، به خصوص با زبانهایی مانند Java ، C ، C ++ یا C # که در آن نه تنها برای خود کد منبع بلکه برای باینری نیز اعمال می شود. ما می خواهیم ویژگی های جدیدی را به روش هایی ایجاد کنیم که نیازی به توزیع مجدد باینری ها ، اجراها یا DLL های موجود نیست.
OCP در زمینه SOLID

 

SRP و OCP مکمل

ما قبلاً اصل SRP مسئولیت منفرد را دیده ایم که می گوید ماژول فقط یک دلیل برای تغییر دارد. اصول OCP و SRP مکمل یکدیگر هستند. کدی که طبق اصل SRP طراحی شده است نیز به اصول OCP احترام می گذارد. وقتی کدی داریم که فقط یک دلیل برای تغییر دارد ، معرفی ویژگی جدید دلیل ثانویه ای برای آن تغییر ایجاد می کند. بنابراین هر دو SRP و OCP نقض می شوند. به همین ترتیب ، اگر کدی داشته باشیم که فقط هنگام تغییر عملکرد اصلی آن تغییر کند و در صورت اضافه شدن قابلیت جدید بدون تغییر باقی بماند ، بنابراین به OCP احترام می گذارد ، بیشتر SRP نیز احترام می گذارد.
این بدان معنا نیست که SRP همیشه منجر به OCP یا بالعکس می شود ، اما در بیشتر موارد اگر به یکی از آنها پایبند باشید ، دستیابی به مورد دوم کاملاً ساده است.

 

مثالی از نقض اصل OCP

از دیدگاه کاملاً فنی ، اصل باز / بسته بسیار ساده است. یک رابطه ساده بین دو کلاس ، مانند کلاس زیر ، اصل OCP را نقض می کند.

کلاس User مستقیماً از کلاس Logic استفاده می کند. اگر ما نیاز به پیاده سازی کلاس دوم منطق داشته باشیم که به ما امکان استفاده از کلاس جدید و جدید را بدهد ، کلاس منطقی موجود باید تغییر کند. کاربر مستقیماً به اجرای منطق گره خورده است ، هیچ راهی برای ما وجود ندارد که بتوانیم منطق جدید را بدون تأثیر بر منطق فعلی ارائه دهیم. و وقتی در مورد زبانهای تایپ شده ایست صحبت می کنیم ، به احتمال زیاد کلاس User نیز نیاز به تغییراتی دارد. اگر در مورد زبانهای کامپایل شده صحبت کنیم ، مطمئناً هم کاربر قابل اجرا و هم منطق اجرایی یا کتابخانه پویا نیاز به کامپایل و تحویل دارد ، ترجیح داده شود که در صورت امکان از آنها اجتناب شود.

با استناد به طرح قبلی می توان نتیجه گرفت که هر کلاسی که مستقیماً از کلاس دیگری استفاده می کند ، می تواند منجر به نقض اصل Open / Closed شود. 
فرض کنید می خواهیم کلاسی را بنویسیم که بتواند پیشرفت "درصدی" یک فایل بارگیری شده را از طریق برنامه ما ارائه دهد. ما دو کلاس اصلی خواهیم داشت ، یک پیشرفت و یک پرونده ، و من حدس می زنم که ما می خواهیم از آنها به شرح زیر استفاده کنیم:

 

test testItCanGetTheProgressOfAFileAsAPercent () {
     $ file = پرونده جدید ()؛
     $ file-> length = 200؛
     $ file-> sent = 100؛
     $ progress = پیشرفت جدید ($ file)؛
     $ this-> assertEquals (50 ، $ progress-> getAsPercent ()) ؛
}

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

 

کلاس پرونده {
     طول عمومی $؛
     عمومی $ ارسال می شود؛
}

 

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

 

پیشرفت کلاس {

     پرونده خصوصی $؛

     تابع __construction (پرونده $ پرونده) {
          $ this-> file = $ file؛
     }

     تابع getAsPercent () {
          بازگشت $ this-> file-> sent * 100 / $ this-> file-> length؛
     }

}

Progress به سادگی یک کلاس است که یک فایل را در سازنده خود می پذیرد. برای وضوح ، نوع متغیر را در پارامترهای سازنده مشخص کرده ایم. تنها یک روش مفید در Progress وجود دارد ، getAsPercent () ، که مقادیر و طول ارسالی را از File گرفته و آنها را به درصد تبدیل می کند. ساده است و کار می کند.

به نظر می رسد این کد صحیح است ، با این وجود اصل Open / Closed را نقض می کند.

اما چرا؟

و چطور؟

 

بیایید سعی کنیم الزامات را تغییر دهیم

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

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

 

خبرنامه نوآوری
مهم ترین اخبار نوآوری را از دست ندهید. برای دریافت آنها از طریق ایمیل ثبت نام کنید.

راه حل ممکن

زبانهای تایپ شده به صورت پویا از مزیت مدیریت انواع اشیا در زمان اجرا برخوردار هستند. این به ما امکان می دهد نوع نوشتار را از سازنده Progress حذف کرده و کد همچنان به کار خود ادامه می دهد.

پیشرفت کلاس {

     پرونده خصوصی $؛

     تابع __construction ($ file) {
         $ this-> file = $ file؛
     }

    تابع getAsPercent () {
         بازگشت $ this-> file-> sent * 100 / $ this-> file-> length؛
     }

}

اکنون می توانیم هر چیزی را در Progress راه اندازی کنیم. و منظور من از هر چیزی به معنای واقعی کلمه هر چیزی است:

کلاس موسیقی {

طول عمومی $؛
عمومی $ ارسال می شود؛

هنرمند عمومی $؛
آلبوم عمومی $؛
عمومی $ releaseDate؛

تابع getAlbumCoverFile () {
بازگشت "تصاویر / پوشش /". $ this-> هنرمند. '/' $ this-> آلبوم. '.png'؛
}
}

و کلاس موسیقی مانند کلاس فوق کاملاً کار خواهد کرد. ما می توانیم به راحتی آن را با یک تست بسیار شبیه به File تست کنیم.
test testItCanGetTheProgressOfAMusicStreamAsAPercent () {
$ music = new Music ()؛
$ music-> length = 200؛
$ music-> sent = 100؛

$ progress = پیشرفت جدید ($ music) ؛

$ this-> assertEquals (50 ، $ progress-> getAsPercent ()) ؛
}

بنابراین اساساً هر محتوای قابل اندازه گیری را می توان با کلاس Progress استفاده کرد. شاید ما باید با تغییر نام متغیر آن را به صورت کد بیان کنیم:

پیشرفت کلاس {

$ قابل اندازه گیری خصوصی

تابع __construction ($ قابل اندازه گیری) {
$ this-> قابل اندازه گیری: $ $ قابل اندازه گیری؛
}

تابع getAsPercent () {
$ $-> قابل اندازه گیری-> ارسال شده * 100 / $ این-> اندازه گیری قابل محاسبه-> طول را برگردانید.
}

}

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

Uکلاس na که یک روش از کلاس پایه را نادیده می گیرد به طوری که قرارداد کلاس پایه توسط کلاس مشتق شده رعایت نمی شود. 

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

در حالی که نتیجه نهایی در هر دو مورد یکسان است، به این معنی که کد شکسته می شود، اولی یک پیام خوب ایجاد کرد. با این حال، این بسیار تاریک است. هیچ راهی برای دانستن اینکه متغیر چیست - رشته ای در مورد ما - و چه ویژگی هایی جستجو شده و یافت نشد وجود ندارد. اشکال زدایی و رفع مشکل مشکل است. یک برنامه نویس باید کلاس Progress را باز کند، آن را بخواند و بفهمد. قرارداد، در این مورد، زمانی که شما به صراحت تایپ را مشخص نکرده اید، این است defiبا رفتار Progress خدشه دار شد. این یک قرارداد ضمنی است که فقط برای Progress شناخته شده است. در مثال ما اینطور است defiبا دسترسی به دو فیلد send و length در متد getAsPercent() انجام می شود. در زندگی واقعی، قرارداد ضمنی می‌تواند بسیار پیچیده باشد و کشف آن با چند ثانیه در کلاس دشوار باشد.

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

Ercole Palmeri

خبرنامه نوآوری
مهم ترین اخبار نوآوری را از دست ندهید. برای دریافت آنها از طریق ایمیل ثبت نام کنید.

مقالات اخیر

Veeam دارای جامع ترین پشتیبانی از باج افزار، از محافظت تا پاسخ و بازیابی است

Coveware توسط Veeam به ارائه خدمات پاسخگویی به حوادث اخاذی سایبری ادامه خواهد داد. Coveware قابلیت‌های پزشکی قانونی و اصلاحی را ارائه می‌دهد…

آوریل 23 2024

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

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

آوریل 22 2024

تنظیم کننده ضد انحصار بریتانیا هشدار BigTech را در مورد GenAI به صدا در می آورد

CMA انگلستان در مورد رفتار Big Tech در بازار هوش مصنوعی هشداری صادر کرده است. آنجا…

آوریل 18 2024

کاسا گرین: انقلاب انرژی برای آینده ای پایدار در ایتالیا

فرمان "خانه های سبز" که توسط اتحادیه اروپا برای افزایش بهره وری انرژی ساختمان ها تدوین شده است، روند قانونی خود را با…

آوریل 18 2024

نوآوری را به زبان خود بخوانید

خبرنامه نوآوری
مهم ترین اخبار نوآوری را از دست ندهید. برای دریافت آنها از طریق ایمیل ثبت نام کنید.

ما را دنبال کنید