زمانیکه کلاسی، دو یا چند کار را انجام میدهد، بهتر است این امور در کلاسهای مجزایی انجام شوند. راه اصلی این کار، بازسازی کد استخراج کلاس است. ایده اصلی این بازسازی کد با ساختن کلاسی جدید و انتقال خصوصیتها، فیلدها و متدهای مورد نظر به آن انجام میشود.
کلاسها معمولا از ابتدا به صورت چند وظیفهای و پیچیده طراحی و پیاده سازی نمیشوند. اما با گذشت زمان معمولا کلاسها پیچیدهتر میشوند. این پیچیدگی تاثیر مستقیمی را بر روی قابلیت نگهداری نرم افزار خواهد داشت.
تشخیص کلاسی که نیاز به جداسازی دارد امر مهمی است. روشهای مختلفی نیز برای این کار وجود دارد که در زیر اشاره شدهاست. با دیدن نشانههایی مانند نشانههای زیر میتوانید اطمینان داشته باشید که کلاس مورد نظر شما نیاز به جداسازی دارد.
- تعدادی خصوصیت و فیلد و متد در کلاس وجود دارند که به نظر میرسد فقط باهم کار میکنند.
- زیر مجموعهای از دادههای کلاس، معمولا همراه هم تغییر میکنند یا به یکدیگر وابسته هستند.
- با تغییر بدنه متدی، نیاز شود متدهای دیگری در همان راستا تغییر کنند.
- ارث بریها تنها به صورت دستهای بر اعضای کلاس اثر میگذارند.
مراحل انجام این بازسازی کد
- تصمیم بگیرید که به چه صورت کلاسی را تقسیم خواهید کرد.
- کلاس جدیدی بسازید که نشان دهنده مسئولیتهای جدید تقسیم شده باشد. اگر نام کلاس قدیمی با مسئولیتهای باقی مانده برای آن همخوانی نداشت، نام آن را تغییر دهید
- فیلدها و خصوصیات کلاس قدیمی را با استفاده از بازسازی Move field به کلاس جدید منتقل نمایید.
- کد را برای هر انتقال کامپایل و تست نمایید.
- متدهای کلاس قدیمی را با استفاده از بازسازی جابجایی متد به کلاس جدید منتقل نمایید.
- کد را برای هر انتقال، کامپایل و تست نمایید.
مثال (برای سادگی توضیح، موضوع مثالها بسیار ساده در نظر گرفته شدهاند)
فرض کنید بخشی از نرم افزار تولید شده، مسئولیت مدیریت صورت حسابها و پرداختها را بر عهده دارد. در این زیر سیستم کلاس مدیریت کننده صورت حسابها و پرداختها به اسم InvoiceService ساخته شدهاست. بدنه این کلاس به صورت زیر است:
public class InvoiceService
{
public void AddInvoice()
{
}
public void DeleteInvoice()
{
}
public void AddOfflinePayment(int invoiceId)
{
}
public void AddOnlinePayment(int invoiceId)
{
}
}
متدهای AddOfflinePayment و AddOnlinePayment مسئولیت ذخیره یک پرداخت را برای صورت حساب مشخص شده، دارند.
متدهای دیگری نیز برای ذخیره و حذف صورت حسابها در این کلاس مشاهده میشود.
اگر کلاس InvoiceService با این تعداد عضو باقی بماند احتمالا مشکل خاصی پیش نخواهد آمد. مشکل زمانی بوجود میآید که تعداد اعضای بیشتری برای مدیریت پرداختها و صورت حسابها نیاز باشد. طراحی فعلی این کلاس موارد مربوط به پرداخت و صورت حساب را تلفیق کردهاست. طراحی بهتر این کلاس، بازسازی استخراج کلاس است. به این صورت که کلاسی مجزا برای مدیریت امور پرداخت ایجاد شود. به این ترتیب کلاسهایی با مسئولیتهای مشخص خواهیم داشت. تکه کد زیر تغییرات کلاس InvoiceService را نشان میدهد:
public class InvoiceService
{
public void AddInvoice()
{
}
public void DeleteInvoice()
{
}
}
public class PaymentService
{
public void AddOfflinePayment(int invoiceId)
{
}
public void AddOnlinePayment(int invoiceId)
{
}
}
تغییرات اعمال شده ساده هستند، اما با در نظر گرفتن تاثیر مثبت فراوان کلاسهای تک وظیفهای، این تغییر میتواند اثر بسیار خوبی بر روی قابلیت نگهداری نرم افزار نیز داشته باشد.
نمونههای دیگری از بازسازی کد استخراج متد که بیشتر مشاهده کردهام، به صورت زیر هستند:
- تقسیم کلاسهای controller بر اساس قوانین طراحی REST
- تقسیم کلاسهای داده (data model) بر اساس قوانین شیءگرایی و تک وظیفگی
نظر شما چیست؟ بیشترین موارد نیاز به استخراج کلاس را در چه بخشهایی از کد مشاهده کردهاید؟