۲۷ مهر ۱۴۰۴

Techboy

اخبار و اطلاعات روز تکنولوژی

پنج روشی که کد شما به‌هم متصل است، چه بهتر و چه بد

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

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

هفته گذشته من دربارهٔ کنسنس صحبت کردم، معیاری برای اتصال کد. ما همه توافق داریم که کدهای کم‌اتصال خوب هستند، اما معنای دقیق آن همیشه واضح نیست. کنسنس به ما کمک می‌کند تا دربارهٔ نحوهٔ اتصال کد استدلال کنیم. اگر نحوهٔ اتصال کدها را درک نکنید، نمی‌توانید کد خود را از هم جدا کنید، درست است؟

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

دو نوع کنسنس وجود دارد—ثابت و پویا. کنسنس ثابت می‌تواند با بررسی بصری کد شما کشف شود، در حالی که کنسنس پویا فقط با اجرای کد شما کشف می‌شود. این هفته من به کنسنس ثابت می‌پردازم و هفتهٔ آینده به کنسنس پویا می‌پردازم.

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

کنسنس نام

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

function DoSomething(): void {     // Code that does something }

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

کنسنس نوع

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

class SomeClass { processWidget(aWidget: Widget, aAction: WidgetActionType): boolean { // Function logic goes here return false; // Example return value } }

در این صورت هر کد فراخوانی‌کننده باید یک Widget و یک WidgetActionType به عنوان پارامترهای تابع processWidget ارسال کند و باید یک boolean را به عنوان نوع نتیجه بپذیرد.

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

کنسنس معنی

کنسنس معنی زمانی رخ می‌دهد که مؤلفه‌ها باید دربارهٔ معنای مقادیر خاصی توافق داشته باشند. کنسنس معنی بیشتر زمانی رخ می‌دهد که از «اعداد جادویی» استفاده می‌کنیم، یعنی زمانی که مقادیر خاصی را در مکان‌های متعدد به کار می‌بریم.

کد زیر را در نظر بگیرید:

function getWidgetType(aWidget: Widget): number { if (aWidget.status === 'Working') { return 1; } else if (aWidget.status === 'Broken') { return 2; } else if (aWidget.state === 'Missing') { return 3; } else { return 0; } }

اگر می‌خواهید از کد بالا استفاده کنید، باید معنای کد نتیجه برای تابع GetWidgetType را بدانید. اگر یکی از کدهای نتیجه (۱، ۲، ۳ یا ۰) را تغییر دهید یا کد جدیدی اضافه کنید، باید کدی را که از این تابع استفاده می‌کند در همه‌جا به‌روز کنید. و برای انجام این تغییر باید معنای هر کد نتیجه را بدانید. مطمئناً می‌توانید به این تابع نگاه کنید و متوجه شوید که 'Working' برابر با ۱ است، اما این برای فراخوانندهٔ تابع به‌هیچ‌وجه واضح نیست، نه؟

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

کنسنس موقعیت

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

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

در اینجا یک مثال آورده شده است. این روال را در نظر بگیرید:

class UserManager { addUser(aFirstName: string, aLastName: string, aAge: number, aBirthdate: Date, aAddress: Address, aPrivileges: Privileges): void { // add the user here } }

ما می‌توانیم کنسنس یا اتصال را با بازسازی این کنسنس موقعیت به استفاده از کنسنس نوع کاهش دهیم. برای مثال:

type UserRecord = { firstName: string; lastName: string; age: number; birthday: Date; address: Address; privileges: Privileges; }; class UserManager { addUser(aUser: UserRecord): void { // add user here } }

در اینجا با ایجاد یک نوع واحد و اینکه رویهٔ addUser به نوع یک پارامتر وابسته باشد به‌جای موقعیت‌های فهرست طولانی پارامترها، اتصال را کاهش دادیم. می‌توانید داده‌های UserRecord را به هر ترتیب دلخواه پر کنید، که بار ذهنی فراخوانی تابع addUser را کاهش می‌دهد.

با تضعیف کنسنس یا کاهش قدرت اتصال، کد را بهبود دادیم.

کنسنس الگوریتم

کنسنس الگوریتم زمانی رخ می‌دهد که دو ماژول باید دربارهٔ یک الگوریتم خاص برای عملکرد مشترک توافق داشته باشند.

تصور کنید یک سرور با API C# می‌سازید که توسط یک کلاینت TypeScript مصرف می‌شود. اطلاعاتی که بین این دو ماژول ارسال می‌شود حساس است و باید رمزنگاری شود. بنابراین، این دو ماژول توسط کنسنس الگوریتم به هم متصل می‌شوند زیرا هر دو باید بر روی الگوریتم رمزنگاری مورد استفاده توافق کنند. اگر الگوریتم رمزنگاری برای سرور تغییر کند، باید برای کلاینت نیز تغییر یابد.

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

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