وابستگی کامپوننت‌ها (Component Coupling)

احتمالاً باید شروع کنم به فکر کردن درباره‌ی اینکه کامپوننت‌ها چطور با هم تعامل دارن و به هم وابسته‌ان، درسته؟
بله، و الان زمان مناسبیه برای پرداختن به این موضوع.
وقتی کامپوننت‌های اصلی اولیه رو شناسایی می‌کنی، مهمه که مشخص بشه این کامپوننت‌ها چطور با هم تعامل دارن.
این موضوع با عنوان «اتصال کامپوننت‌ها» شناخته می‌شه: یعنی درجه‌ای که کامپوننت‌ها از همدیگه اطلاع دارن و به هم وابسته‌ان.
هرچه تعامل بین کامپوننت‌ها بیشتر باشه، اتصال سیستم بیشتر می‌شه و نگهداری اون سخت‌تر خواهد بود.

بهش می‌گن «توپ بزرگ گِلی» (‌big ball of mud) چون تعامل‌ها و وابستگی‌های بین کامپوننت‌ها اون‌قدر زیاد می‌شن که نمودار معماری شبیه یه توپ گِل یا حتی یه کاسه اسپاگتی به نظر می‌رسه.
برای همین خیلی مهمه که به نحوه‌ی تعامل کامپوننت‌ها و وابستگی‌های بینشون توجه کنیم.

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

  • اتصال ورودی (Afferent Coupling)
  • اتصال خروجی (Efferent Coupling)

اتصال ورودی (Afferent Coupling)

بچه‌ها برای خیلی چیزها به والدین‌شون وابسته‌ان—مثل تأمین غذا، داشتن جای امن برای زندگی، بردن به تمرین فوتبال، یا حتی دادن پول توجیبی برای خرید خوراکی یا کتاب کمیک.
در واقع، والدین به‌صورت ورودی (Afferent) به بچه‌ها و حتی سگ خانواده متصل هستن، چون همه‌ی اون‌ها به والدین برای چیزی وابسته‌ان.

در معماری نرم‌ افزار، اتصال ورودی یعنی درجه و نحوه‌ی وابستگی کامپوننت‌های دیگر به یک کامپوننت هدف (در این مثال، مادر).
گاهی بهش “fan-in” یا اتصال ورودی گفته می‌شه، و در بیشتر ابزارهای تحلیل کد با نماد CA نمایش داده می‌شه.

برای درک نحوه‌ی عملکرد اتصال ورودی (Afferent Coupling)، به تعامل بین سه کامپوننت در معماری Adventurous Auctions نگاه کن.

کامپوننت‌های Auction Registration و Automatic Payment هر دو به کامپوننت Bidder Profile وابسته‌ان تا اطلاعات پروفایل خریدار رو دریافت کنن.
در این سناریو، کامپوننت Bidder Profile سطح اتصال ورودی برابر با ۲ داره، چون دو کامپوننت برای انجام کار خودشون به اون وابسته‌ان.

اتصال خروجی (Efferent Coupling)

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

اتصال خروجی دقیقاً نقطه‌ی مقابل اتصال ورودیه، و با تعداد کامپوننت‌هایی که یک کامپوننت هدف به اون‌ها وابسته‌ست اندازه‌گیری می‌شه.
بهش “fan-out coupling” یا اتصال خروجی هم گفته می‌شه، و در ابزارهای تحلیل کد معمولاً با نماد CE نمایش داده می‌شه.

خب، اتصال خروجی در کامپوننت‌های منطقی چه شکلیه؟
بیایم دوباره به سیستم Adventurous Auctions نگاه کنیم—این بار با تمرکز روی فرآیند دریافت پیشنهاد از کاربر برای یک سفر.

چون کامپوننت Bid Capture برای پردازش پیشنهاد به کامپوننت‌های Bid Streamer و Bid Tracker وابسته‌ست، به این کامپوننت‌ها اتصال خروجی داره.
سطح اتصال خروجی اون برابر با ۲ هست—یعنی به دو کامپوننت دیگه وابسته‌ست.

اندازه‌گیری اتصال (Coupling)

می‌تونی میزان اتصال یک کامپوننت خاص رو به سه روش اندازه‌گیری کنی:

  • اتصال ورودی (CA): تعداد کامپوننت‌هایی که به این کامپوننت وابسته‌ان
  • اتصال خروجی (CE): تعداد کامپوننت‌هایی که این کامپوننت به اون‌ها وابسته‌ست
  • اتصال کل (CT): مجموع اتصال ورودی و خروجی (یعنی CA + CE)

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

اما چطور می‌تونم اتصال بین کامپوننت‌ها رو کاهش بدم تا سیستم‌هایی با اتصال ضعیف (loosely coupled) بسازم؟

سؤال خیلی خوبیه.
به توسعه‌دهنده‌ها آموزش داده می‌شه که به‌دنبال ساخت سیستم‌هایی با اتصال ضعیف باشن، اما معمولاً روش انجام این کار رو یاد نمی‌گیرن.
ما اینجا با معرفی تکنیکی به نام «قانون دمتر» (Law of Demeter) نشون می‌دیم چطور می‌شه این کار رو انجام داد.

قانون دمتر که با عنوان «اصل کمترین دانش» هم شناخته می‌شه، از دمتر، الهه‌ی یونانی کشاورزی، نام‌گذاری شده.
دمتر همه‌ی غلات مورد نیاز انسان‌ها رو تولید می‌کرد، اما هیچ اطلاعی از اینکه انسان‌ها با اون غلات چه کار می‌کنن نداشت.
به همین دلیل، دمتر اتصال ضعیفی با دنیای انسان‌ها داشت.

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

سیستمی با اتصال شدید (Tightly Coupled System)

بیایم ببینیم چطور می‌شه با استفاده از قانون دمتر (Law of Demeter) سیستم‌ها رو از حالت اتصال شدید خارج کرد—با بررسی معماری منطقی یک سیستم معمولی ثبت سفارش.

اعمال قانون دمتر (Law of Demeter)

سطح کلی اتصال در سیستم خیلی نگران‌کننده نیست.
چیزی که واقعاً نگران‌کننده‌ست، اتصال شدید کامپوننت Order Placement (با CT=5)، عدم تعادل در اتصال کامپوننت‌ها، و میزان دانش زیاد این کامپوننت درباره‌ی فرآیند ثبت سفارش هست.

نکته: کامپوننت Order Placement بیش از حد کنترل رو به‌دست گرفته.

برای رفع این مشکلات، بیایم قانون دمتر رو اعمال کنیم و دانش مربوط به «کمبود موجودی» رو به کامپوننت Inventory Management منتقل کنیم.

با انتقال دانش مربوط به اقدامات لازم در شرایط «کمبود موجودی» به کامپوننت Inventory Management، میزان دانش کامپوننت Order Placement درباره‌ی سیستم کاهش پیدا کرد و در نتیجه سطح اتصال اون هم کمتر شد.

اما در عوض، دانش کامپوننت Inventory Management بیشتر شد و بنابراین اتصال اون افزایش یافت.

این دقیقاً جوهره‌ی قانون دمتره:
دانش کمتر یعنی اتصال کمتر؛ دانش بیشتر یعنی اتصال بیشتر.

اتصال (Coupling) یعنی اینکه یک کامپوننت چقدر درباره‌ی سایر بخش‌های سیستم اطلاع و آگاهی داره.

نکته‌:
متوجه شدی که با وجود کاهش اتصال کامپوننت Order Placement، سطح اتصال کل سیستم تغییری نکرد؟
دلیلش اینه که ما دانش رو از سیستم حذف نکردیم—فقط اون رو به کامپوننت دیگه‌ای منتقل کردیم: Inventory Management.

یک تعادل ظریف

یادت هست «قانون اول معماری نرم‌ افزار» چی بود؟ دوباره تکرارش می‌کنیم (چون خیلی مهمه):

همه‌چیز در معماری نرم‌ افزار یک مبادله است.

اتصال ضعیف (Loose Coupling) هم از این قاعده مستثنا نیست.
بیایم دو معماری‌ای که تا الان دیدیم رو با هم مقایسه کنیم و مبادله‌های (trade-offs) بینشون رو تحلیل کنیم.

در معماری با اتصال شدید (Tightly Coupled)، اگه بخوای بفهمی وقتی مشتری سفارشی ثبت می‌کنه چه اتفاقی می‌افته، فقط کافیه به کامپوننت Order Placement نگاه کنی تا کل جریان کاری رو ببینی.
مزیت این مدل اینه که دانش متمرکز شده و فهمیدن فرآیند ساده‌تره.
اما نقطه‌ی ضعفش اینه که کامپوننت‌های مثل Item Pricing و Supplier Ordering دیگه تأثیری روی Order Placement ندارن—یعنی انعطاف‌پذیری کمتره.

در مقابل، معماری با اتصال ضعیف (Loosely Coupled) دانش رو بین چند کامپوننت پخش می‌کنه، طوری که هیچ کامپوننتی همه‌ی مراحل رو نمی‌دونه.
برای درک کامل فرآیند ثبت سفارش، باید به چندین کامپوننت مختلف مراجعه کنی.
این باعث افزایش انعطاف‌پذیری و قابلیت نگهداری می‌شه، اما نقطه‌ی ضعفش اینه که Order Placement حالا به چهار کامپوننت دیگه وابسته‌ست—و تغییر در هرکدوم ممکنه باعث اختلال بشه.

دو کامپوننت زمانی به هم متصل (coupled) هستن که تغییر در یکی ممکنه باعث تغییر در دیگری بشه.

نکات پایانی درباره‌ی کامپوننت‌ها

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

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

نکات کلیدی

  • کامپوننت‌های منطقی بلوک‌های سازنده‌ی عملکردی یک سیستم هستن.
  • یک کامپوننت منطقی با ساختار دایرکتوری نمایش داده می‌شه—یعنی پوشه‌ای که کد منبع در اون قرار می‌گیره.
  • هنگام نام‌گذاری کامپوننت، از نامی توصیفی استفاده کن تا عملکرد اون کامپوننت به‌وضوح مشخص باشه.
  • ایجاد معماری منطقی شامل چهار مرحله‌ی پیوسته‌ست: شناسایی کامپوننت‌ها، تخصیص نیازمندی‌ها، تحلیل مسئولیت‌های کامپوننت، و تحلیل ویژگی‌های معماری.
  • می‌تونی از رویکرد جریان کاری (workflow) برای شناسایی کامپوننت‌های منطقی اولیه استفاده کنی—با تخصیص مراحل داستان اصلی مشتری به کامپوننت‌ها.
  • می‌تونی از رویکرد بازیگر/عمل (actor/action) برای شناسایی کامپوننت‌های منطقی اولیه استفاده کنی—با شناسایی بازیگران سیستم و تخصیص اعمال اون‌ها به کامپوننت‌ها.
  • «دام موجودیت» (entity trap) رویکردیه که کامپوننت‌ها رو بر اساس موجودیت‌های اصلی سیستم مدل می‌کنه. از این رویکرد اجتناب کن، چون منجر به کامپوننت‌های مبهم، بزرگ و با مسئولیت بیش از حد می‌شه.
  • هنگام تخصیص نیازمندی‌ها به کامپوننت‌ها، نقش و مسئولیت هر کامپوننت رو مرور کن تا مطمئن بشی اون وظیفه باید توسط اون کامپوننت انجام بشه.
  • اتصال زمانی اتفاق می‌افته که کامپوننت‌ها برای انجام یک عملکرد تجاری به هم وابسته باشن.
  • اتصال ورودی (Afferent Coupling)، که با نام اتصال ورودی یا fan-in هم شناخته می‌شه، زمانی رخ می‌ده که سایر کامپوننت‌ها به یک کامپوننت هدف وابسته باشن.
  • اتصال خروجی (Efferent Coupling)، که با نام اتصال خروجی یا fan-out هم شناخته می‌شه، زمانی رخ می‌ده که یک کامپوننت هدف به سایر کامپوننت‌ها وابسته باشه.
  • داشتن دانش زیاد درباره‌ی اتفاقات سیستم باعث افزایش اتصال کامپوننت‌ها می‌شه.
  • قانون دمتر (Law of Demeter) بیان می‌کنه که سرویس‌ها یا کامپوننت‌ها باید دانش محدودی نسبت به سایر سرویس‌ها یا کامپوننت‌ها داشته باشن. این قانون برای ساخت سیستم‌های با اتصال ضعیف مفیده.
  • اگرچه اتصال ضعیف وابستگی بین کامپوننت‌ها رو کاهش می‌ده، اما دانش مربوط به جریان کاری رو پخش می‌کنه و مدیریت اون دانش رو سخت‌تر می‌کنه.
  • برای تعیین اتصال کل (CT) در معماری منطقی، سطح اتصال ورودی (CA) و خروجی (CE) هر کامپوننت رو با هم جمع می‌کنیم: CT = CA + CE.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *