یکی از سوالات بنیادی در طراحی نرم افزار این است:
اگر بخواهیم دو عملکرد را در نرم افزار پیاده سازی کنیم، بهتر است آن ها را یک جا قرار دهیم، یا در دو جای متفاوت؟
این سوال در همه ی سطوح سیستم ممکن است مطرح شود، در متدها، کلاس ها و ...
برای مثال، اگر یک کلاس داریم که مسئولیت مدیریت فایل را بر عهده دارد، آیا buffering را در همان کلاس بگذاریم یا در یک کلاس مجزا؟
آیا همه ی عملکردِ مربوط به پردازش کردن یک HTTP request، در یک متد پیاده سازی شود؟ یا آن را بین چند متد مختلف پخش کنیم؟
هدف از این تصمیم گیری، کاهش پیچیدگی سیستم و افزایش modularity است. شاید به نظر برسد بهترین کار این است که تا حد ممکن سیستم را به بخش های کوچکتر بشکنیم ولی این کار می تواند بر پیچیدگی سیستم اضافه کند. چرا؟
به عنوان یک قانون کلی، اگر دو قطعه کد به هم مرتبط هستند، بهتر است در کنار هم باشند، در غیر این صورت آن ها را جدا کنید.
از کجا بفهمیم دو قطعه کد به هم مرتبط هستند؟
مثلا فرض کنید بخواهیم یک Http Request را پردازش و بخش های مختلف آن را از هم جدا کنیم. اگر این بیزنس را در چند متد پخش کنیم، احتمالا همه ی آن ها باید فرمت Http Request را بشناسند و بتوانند بخش های مختلف آن را از هم جدا کنند. بنابراین دانشی که اینجا مشترک است، نحوه ی کار با Http Request است و بهتر است آن را فقط در یک ماژول قرار دهیم.
منظور این است که، هر کسی که می خواهد از یکی از این قطعات کد استفاده کند، به احتمال زیاد دیگری را هم لازم دارد. البته این نوع از ارتباط باید حتما دو طرفه باشد تا شامل این قانون شود. برای مثال، ممکن است یک کلاس که مسئول cache کردن دیتا است، از Hash table استفاده کند، ولی Hash table ها در خیلی جاهای دیگر هم استفاده می شوند، پس این ارتباط دو طرفه نیست و باید این دو ماژول جدا از هم باشند.
یعنی می توانیم یک دسته بندی سطح بالاتر در نظر بگیریم، که این دو قطعه کد هر دو به آن تعلق دارند. مثلا اگر دو قطعه کد داریم، یکی برای جستجوی یک string و دیگری برای تبدیل حروف کوچک و بزرگ به هم، هر دوی آن ها در دسته بندی ویرایش string قرار می گیرند.
متدی را در نظر بگیرید که در پیاده سازی خودش از متد دیگری استفاده می کند. اگر هنگام خواندن کد این متد، مجبور شویم حتما کد متد داخلی را هم نگاه کنیم، یعنی این دو به درستی جداسازی نشده اند و با هم ارتباط دارند.
بعضی از برنامه نویس ها اعتقاد دارند، متدی که بیشتر از 20 خط کد دارد، باید حتما به چند متد دیگر تقسیم شود!
این جمله همیشه درست نیست. طولانی بودن یک متد، اصلا دلیل خوبی برای شکستن آن نیست. اگر بدون دلیل کافی، یک متد را بشکنیم، تمامی آن متدها واسط خود را به سیستم تحمیل می کنند و این یعنی افزودن پیچیدگی. تنها زمانی مجاز به شکستن یک متد هستیم، که پیچیدگی سیستم را کمتر کند.
متدهای طولانی همیشه بد نیستند. فرض کنید یک متد داریم که 4 قطعه کد دارد، هر قطعه کد هم 10 خط است. کسی که این متد را می خواند، می تواند از قطعه ی بالایی شروع کند و هر قطعه را بفهمد و به آخر متد برسد. اگر این قطعات به هم وابستگی زیاد داشته باشند و هر کدام از آن ها را تبدیل به یک متد دیگر کنیم، برای فهمیدن متد parent، باید مدام بین آن متد و متدهای child رفت و آمد داشته باشیم.
در طراحی متدها، مساله ی مهم این است که متد، فقط و فقط یک کار را، به صورت کامل انجام دهد. همچنین باید یک انتزاع تمیز و ساده فراهم کند و شامل یک واسط ساده باشد که بخش های دیگر کد به راحتی از آن استفاده کند. اگر متد این ویژگی ها را داشته باشد، تعداد خطوط آن، اصلا مهم نیست.
شکستن متد به دو روش قابل انجام است:
1. در کد آن متد، یک بخش از کد را به عنوان یک sub-task تشخصی بدهیم و آن را به یک متد child انتقال دهیم که توسط متد parent استفاده می شود.
اگر از این روش استفاده می کنیم، باید این دو شرط رعایت شود:
2. یک متد را به دو متد کاملا مجزا بشکنیم که هیچ کدام از دیگری استفاده نمی کنند.
اگر از این روش استفاده می کنیم، باید این شرط رعایت شود:
تصمیم گیری در مورد جدا کردن ماژول ها، یا یکی کردن آن ها، باید بر اساس پیچیدگی سیستم باشد. ساختاری را انتخاب کنید که نتیجه ی آن پنهان سازیِ بهترِ اطلاعات، کمترین وابستگی و عمیق ترین ماژول ها باشد.