در طراحی نرمافزارهای پیچیده، نحوه ارتباط کلاسها با یکدیگر تعیینکننده طول عمر پروژه است. یکی از اشتباهات رایج، استفاده بیش از حد از وراثت (Inheritance) است که منجر به کدهای خشک و غیرقابل تغییر میشود. در مقابل، Clean Code ما را به سمت ترکیبگرایی و رعایت اصل دمتر هدایت میکند.
در این بخش، یاد میگیریم که چگونه با کاهش وابستگیها، کدی بنویسیم که در برابر تغییرات زانو نزند.
بخش اول: ترکیب (Composition) به جای وراثت
وراثت اغلب باعث ایجاد رابطهای صلب و سخت (is-a) میشود. اما در دنیای واقعی، رفتارها بیشتر شبیه به قطعات پازل هستند که با هم ترکیب میشوند (has-a). وقتی از وراثت استفاده میکنید، کلاس فرزند تمام خصوصیات کلاس پدر را به ارث میبرد، حتی اگر به آنها نیاز نداشته باشد.
چرا Composition بهتر است؟
- انعطافپذیری بالا: میتوانید رفتارها را در زمان اجرا تغییر دهید.
- تستپذیری بهتر: جایگزین کردن وابستگیها (Mocking) در تستهای واحد بسیار سادهتر است.
- جلوگیری از انفجار کلاسها: نیاز نیست برای هر ترکیب جدید از رفتارها، یک کلاس فرزند جدید بسازید.
// روش سنتی و خشک (وراثت)
public class SmartPhone : Camera { … }
// روش Clean Code (ترکیب)
public class SmartPhone
{
private readonly ICamera _camera;
public SmartPhone(ICamera camera)
{
_camera = camera;
}
}
در روش دوم، گوشی هوشمند لزوماً «یک دوربین نیست»، بلکه «یک دوربین دارد». این یعنی میتوانید هر نوع دوربینی را بدون تغییر در کلاس SmartPhone به آن تزریق کنید.
بخش دوم: اصل دمتر (Law of Demeter)
این اصل که به «اصل کمترین دانش» نیز معروف است، میگوید: «یک واحد فقط باید با واحدهای نزدیک خود صحبت کند و از ساختار داخلی غریبهها بیخبر باشد.»
اگر در کد خود با زنجیرهای از فراخوانیها (مثل a.GetB().GetC().DoSomething()) مواجه شدید، شما اصل دمتر را نقض کردهاید. این کار باعث میشود کلاس A به شدت به ساختار داخلی B و C وابسته شود.
// نقض اصل دمتر (کد شکننده)
var street = user.GetAddress().GetCity().GetStreetName();
// رعایت اصل دمتر (کد تمیز)
var street = user.GetStreetName();
در روش تمیز، کلاس User وظیفه دارد آدرس را مدیریت کند و ما نیازی نداریم از جزئیات داخلی Address یا City باخبر باشیم. این کار باعث میشود اگر فردا ساختار Address تغییر کرد، فقط کلاس User اصلاح شود، نه تمام بخشهایی که به نام خیابان نیاز داشتند.
بخش سوم: مزایای فنی کاهش وابستگی
وقتی از Composition و اصل دمتر استفاده میکنید، سیستم شما به سمت Decoupling یا جداسازی حرکت میکند. نتایج این رویکرد عبارتند از:
- نگهداری آسان: تغییر در یک ماژول باعث خرابی دومینووار در سایر بخشها نمیشود.
- تستپذیری: به راحتی میتوانید با استفاده از Interfaceها، بخشهای مختلف را ایزوله و تست کنید.
- کد خواناتر: هر کلاس فقط مسئولیتهای نزدیک به خود را مدیریت میکند.
نتیجهگیری بخش چهارم
ارتباطات تمیز بین اشیاء، تفاوت اصلی بین یک پروژه حرفهای و یک پروژه آماتور است. همیشه از خود بپرسید: «آیا این کلاس واقعاً نیاز دارد که بداند آن کلاس دیگر چگونه کار میکند؟» اگر پاسخ منفی است، وابستگی را قطع کنید.
در مقاله بعدی و پایانی، به سراغ فلسفه سادگی و حذف پیچیدگیهای غیرضروری میرویم تا پرونده این مجموعه مقالات را ببندیم.