TG Telegram Group & Channel
کانال مکتب‌خانه DDD | United States America (US)
Create: Update:

💡 Mixed Responsibility Smell

فرض کنید متدی دارید که پول را به حساب کاربر برمی‌گرداند و در همان لحظه مقدار جدید موجودی را هم برمی‌گرداند:

public Money Refund(Money amount)
{
Balance += amount;
return Balance;
}


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

اما سوال اینجاست:
آیا درسته که یک متد هم وضعیت را تغییر دهد و هم مقدار به‌روز شده رو برگرداند؟ در واقع سوال مهم در باره نحوه‌ی مدل کردن و پیاده‌سازی سناریوهایی که در بالا اشاره شد، است.

در جواب باید بگم:
این دقیقاً همون جاییه که با یک Code Smell به نام Mixed Responsibility Smell روبرو هستیم.

قطعاً ما نیاز به متد دیگری داریم که صرفاً موجودی را به ما بدهد. در واقع، نمی‌توانیم برای دریافت موجودی تنها به متد Refund تکیه کنیم؛ چرا که این امر ما را مجبور می‌کند برای هر بار خواندن موجودی، یک عملیات Refund انجام دهیم که نه منطقی است و نه قابل قبول. این یعنی عملاً ما هیچ‌گاه به یک موجودی ثابت و قابل اتکا دسترسی نداریم. علاوه بر بحث عدم قطعیت (indeterministic behavior) و موارد مشابه، این وضعیت یک Code Smell جدی محسوب می‌شود. حداقل پیامد آن این است که ما یک منطق یکسان (یعنی محاسبه موجودی) را در دو نقطه مختلف تکرار می‌کنیم: هم در متد Refund و هم در متدی مانند GetBalance، حتی اگر GetBalance تنها شامل return Balance; باشد.


سوال بعدی اینه که: چرا این مشکل‌ساز است
این «بوی بد» زمانی اتفاق می‌افته که یک متد یا کامپوننت بیش از یک مسئولیت یا وظیفه را بر عهده بگیره و باعث شود:

ابهام در نقش و وظیفه اصلی کد
پیچیدگی در فهم، تست و نگهداری کد
تکرار منطق مشابه در چند نقطه
مثل منطق محاسبه یا به‌روزرسانی موجودی که هم در متد Refund و هم در متد GetBalance تکرار می‌شود (حتی اگر در GetBalance صرفاً return Balance; باشد)
کاهش انعطاف‌پذیری و سختی توسعه کد
رفتار نامعین (Indeterministic Behavior): اگر برای گرفتن موجودی مجبور باشیم همیشه Refund را صدا بزنیم، هیچ‌وقت موجودی واقعی و پایدار در اختیار نخواهیم داشت، چون هر بار با گرفتن موجودی، وضعیت تغییر می‌کند!



مثال بهتر:
// command
public void Refund(Money amount)
{
Balance += amount;
}

// query
public Money GetBalance()
{
return Balance;
}


نتیجه اینکه:
هشدار مهمی که Mixed Responsibility Smell به ما میده اینه که:

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

💡 Mixed Responsibility Smell

فرض کنید متدی دارید که پول را به حساب کاربر برمی‌گرداند و در همان لحظه مقدار جدید موجودی را هم برمی‌گرداند:
public Money Refund(Money amount)
{
Balance += amount;
return Balance;
}


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

اما سوال اینجاست:
آیا درسته که یک متد هم وضعیت را تغییر دهد و هم مقدار به‌روز شده رو برگرداند؟ در واقع سوال مهم در باره نحوه‌ی مدل کردن و پیاده‌سازی سناریوهایی که در بالا اشاره شد، است.

در جواب باید بگم:
این دقیقاً همون جاییه که با یک Code Smell به نام Mixed Responsibility Smell روبرو هستیم.

قطعاً ما نیاز به متد دیگری داریم که صرفاً موجودی را به ما بدهد. در واقع، نمی‌توانیم برای دریافت موجودی تنها به متد Refund تکیه کنیم؛ چرا که این امر ما را مجبور می‌کند برای هر بار خواندن موجودی، یک عملیات Refund انجام دهیم که نه منطقی است و نه قابل قبول. این یعنی عملاً ما هیچ‌گاه به یک موجودی ثابت و قابل اتکا دسترسی نداریم. علاوه بر بحث عدم قطعیت (indeterministic behavior) و موارد مشابه، این وضعیت یک Code Smell جدی محسوب می‌شود. حداقل پیامد آن این است که ما یک منطق یکسان (یعنی محاسبه موجودی) را در دو نقطه مختلف تکرار می‌کنیم: هم در متد Refund و هم در متدی مانند GetBalance، حتی اگر GetBalance تنها شامل return Balance; باشد.


سوال بعدی اینه که: چرا این مشکل‌ساز است
این «بوی بد» زمانی اتفاق می‌افته که یک متد یا کامپوننت بیش از یک مسئولیت یا وظیفه را بر عهده بگیره و باعث شود:

ابهام در نقش و وظیفه اصلی کد
پیچیدگی در فهم، تست و نگهداری کد
تکرار منطق مشابه در چند نقطه
مثل منطق محاسبه یا به‌روزرسانی موجودی که هم در متد Refund و هم در متد GetBalance تکرار می‌شود (حتی اگر در GetBalance صرفاً return Balance; باشد)
کاهش انعطاف‌پذیری و سختی توسعه کد
رفتار نامعین (Indeterministic Behavior): اگر برای گرفتن موجودی مجبور باشیم همیشه Refund را صدا بزنیم، هیچ‌وقت موجودی واقعی و پایدار در اختیار نخواهیم داشت، چون هر بار با گرفتن موجودی، وضعیت تغییر می‌کند!



مثال بهتر:
// command
public void Refund(Money amount)
{
Balance += amount;
}

// query
public Money GetBalance()
{
return Balance;
}


نتیجه اینکه:
هشدار مهمی که Mixed Responsibility Smell به ما میده اینه که:

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


>>Click here to continue<<

کانال مکتب‌خانه DDD




Share with your best friend
VIEW MORE

United States America Popular Telegram Group (US)