اگر چند سالی در دنیای توسعه نرمافزار کار کرده باشید، احتمالاً بارها با کدی مواجه شدهاید که تغییر دادنش شبیه راه رفتن روی مین است؛ یک خط را عوض میکنید و ناگهان چند جای دیگر میترکد. خیلی وقتها ریشه این مشکل چیزی است به نام «وابستگی پنهان» یا به بیان دقیقتر، مفهومی به نام Connascence. این مفهوم شاید به اندازه SOLID یا Clean Architecture معروف نباشد، اما اگر درست فهمیده و استفاده شود، میتواند نگاه مهندسان و معماران نرمافزار را به طراحی کد عمیقتر و عملیتر کند. بیایید کمی در موردش صحبت کنیم.
Connascence به زبان ساده یعنی «چند بخش از سیستم مجبورند با هم یک چیز مشترک را بدانند تا درست کار کنند». هر جا که تغییر در یک بخش، شما را مجبور میکند بخشهای دیگر را هم تغییر دهید، احتمالاً با نوعی از connascence طرف هستید. این مفهوم اولین بار توسط Meilir Page-Jones مطرح شد و هدفش این بود که به ما کمک کند وابستگیها را بهتر بشناسیم، اندازهگیری کنیم و تا حد امکان کنترلشان کنیم.

نکته مهم اینجاست که connascence ذاتاً چیز بدی نیست. هیچ سیستمی بدون وابستگی ساخته نمیشود. مسئله این است که نوع و شدت این وابستگیها چقدر است و در کجا اتفاق میافتند. معماری خوب معماریای نیست که وابستگی نداشته باشد، بلکه معماریای است که وابستگیهایش آگاهانه، محدود و قابل مدیریت باشند. بهتر هست بدونیم که دو دسته اساسی Static Connascences و Dynamic Connascences ها رو داریم که دارای زیر مجموعه هستند. لیست رو نگاه کنیم و بریم برخی از اون ها رو بررسی کنیم.
در بحث Static Connascences
موضوع Name
موضوع Type
موضوع Meaning
موضوع Position
موضوع Algorithm
در بحث Dynamic Connascences
موضوع Execution
موضوع Timing
موضوع Value
موضوع Identity
موضوع Algorithm
بریم برخی از این ها رو باهم بررسی کنیم ...
موضوع connascence of name
یکی از سادهترین و البته خطرناکترین انواع connascence، بحث connascence of name است. یعنی دو بخش از کد باید دقیقاً روی یک اسم به توافق برسند. مثلاً وقتی اسم یک متد، یک متغیر یا یک کلید JSON عوض میشود و کلی جای دیگر میشکند. این نوع وابستگی خیلی رایج است و معمولاً در نگاه اول بیضرر به نظر میرسد، اما اگر در مرز ماژولها یا بین سرویسها باشد، هزینه تغییر را بالا میبرد.
موضوع connascence of type
نوع دیگر connascence of type است. اینجا دو بخش سیستم باید روی نوع داده با هم هماهنگ باشند. مثلاً وقتی یک متد انتظار دارد حتماً یک integer بگیرد و جای دیگر سیستم باید دقیقاً همان نوع را پاس بدهد. این نوع وابستگی نسبت به name کمی بهتر است، چون زبانهای strongly typed میتوانند کمک کنند، اما باز هم اگر در سطح معماری کنترل نشود، دردسرساز میشود.
موضوع connascence of meaning
connascence of meaning یا semantic connascence یکی از خطرناکترینهاست. اینجا کدها روی «معنا» با هم توافق دارند، نه روی اسم یا نوع. مثلاً عدد ۱ یعنی active و ۰ یعنی inactive، بدون اینکه این معنا جایی بهصورت صریح بیان شده باشد. این نوع وابستگی معمولاً با عددهای جادویی و قراردادهای نانوشته به وجود میآید و فهم و نگهداری سیستم را سخت میکند.
موضوع connascence of position
نوع دیگری که خیلی در کدهای قدیمی دیده میشود connascence of position است. یعنی ترتیب پارامترها یا دادهها اهمیت دارد. مثلاً متدی که سه پارامتر میگیرد و اگر جای دوتای آنها عوض شود، سیستم بههم میریزد. هرچقدر تعداد پارامترها بیشتر شود، احتمال خطا هم بالاتر میرود. استفاده از objectها یا named parameterها معمولاً راه فرار از این نوع وابستگی است.
موضوع connascence of algorithm
connascence of algorithm زمانی اتفاق میافتد که چند بخش از سیستم باید دقیقاً از یک الگوریتم یا منطق یکسان استفاده کنند. اگر این منطق در چند جا کپی شده باشد، با کوچکترین تغییر باید همهجا را اصلاح کنید. اینجا refactor کردن و متمرکز کردن منطق مشترک میتواند وابستگی را کمهزینهتر کند.
موضوع connascence of name
در سطح معماری، connascence of timing هم اهمیت پیدا میکند. یعنی دو بخش سیستم باید در زمان مشخصی نسبت به هم عمل کنند. مثلاً یک سرویس باید حتماً قبل از سرویس دیگر بالا بیاید یا یک متد باید قبل از متد دیگر صدا زده شود. این نوع وابستگی معمولاً در سیستمهای توزیعشده خیلی دردناک است و اگر درست مدیریت نشود، سیستم شکننده میشود.
یکی از نکات کلیدی در بحث connascence این است که فقط نوع آن مهم نیست، بلکه محل وقوعش هم اهمیت داره. connascence داخل یک متد یا یک کلاس معمولاً قابل قبولتر از connascence بین ماژولها، لایهها یا سرویسهاست. هرچقدر فاصله اجزای وابسته بیشتر باشد، هزینه تغییر هم بالاتر میرود. به همین دلیل است که میگویند وابستگیهای قوی را تا جای ممکن به کوچکترین محدوده ممکن هل بدهید.
connascence همچنین به ما یک ابزار ذهنی برای refactor دادن میدهد. وقتی میخواهید کدی را بهبود دهید، به این فکر کنید که اگر این بخش تغییر کند، چه جاهای دیگری مجبور به تغییر میشوند و چرا. خیلی وقتها با تبدیل یک connascence of meaning به connascence of name یا type، سیستم قابل فهمتر و امنتر میشود.
در نهایت، connascence بیشتر از اینکه یک قانون خشک باشد، یک لنز برای نگاه کردن به طراحی نرمافزار است. اگر بهعنوان مهندس یا معمار نرمافزار عادت کنیم وابستگیها را با این دید بررسی کنیم، تصمیمهایمان آگاهانهتر میشود. کدهایی مینویسیم که تغییرپذیرترند، راحتتر تست میشوند و در بلندمدت کمتر اعصاب تیم را خرد میکنند. شاید اسم connascence خیلی دهانپرکن نباشد، اما اثرش در کیفیت نرمافزار کاملاً واقعی و ملموس است. اگر خیلی علاقه مند هستید که موضوع رو از دید مرجع اصلی و خیلی اساسی تر داشته باشید، میتونید به سر به این مرجع بزنید. این مقاله تلاش کرد تا سرنخ رو به شمای معمار و برنامه نویس بده.
در مقاله بعدی میریم سراغ کد هایی که معمولا درسرساز میشند در شرکت ها ...