علاوه بر اصول پنجگانه ی SOLID در برنامه نویسی شیء گرایی، برخی اصول توسعه ی نرمافزار به سبک شیء گرایی هستند که داشتن یک آشنایی نسبی با آنها خالی از لطف نیست. GRASP که مخفف واژگان General Responsibility Assignment Software Patterns به معنی «الگوهای نرم افزاری تفویض کلی مسئولیت»، یکی دیگر از مجموعه قوانینی است که تبعیت از آنها میتواند در هرچه حرفه تر شدن ما مفید واقع گردد.
به طور خلاصه، هدف از GRASP این است که مسئولیت ها در آبجکت هایی که از روی کلاسهای مختلف میسازیم مشخص گردد. به طول مثال، چه کسی این آبجکت را می سازد؟ چه کسی مسئول ارتباط مابین آبجکت های مختلف است؟ چه کسی مسئول دریافت درخواست های کاربران و تحویل آنها به آبجکت های مختلف است؟ و سؤالات دیگری از این دست.
توجه داشته باشیم که مفاهیم SOLID و GRASP هیچ گونه کانفلیکت یا «مغایرتی» با یکدیگر ندارند. برنامه نویسی که با یک زبان برنامه نویسی شیء گرا کدنویسی می کند، میتوان SOLID را در کدنویسی رعایت کند، میتوان GRASP را در پیش گیرد و یا هیچ کدام را! حال می بایست ببینیم که چه قوانینی بر GRASP حاکماند که در ادامه با برخی از مهمترین آنها آشنا خواهیم شد.
Expert
این اصل حاکی از آن است که اگر مسئولیتی در نرمافزار وجود دارد که یکی از کلاسهای نرمافزار می بایست آن را بر عهده گیرد، آن مسئولیت را به کلاسی واگذار کنید که نسبت به بقیه توانمندتر است. این کلاس توانمند می بایست آنقدر به سایر کلاسها احاطه داشته باشد که اگر درخواستی مبنی بر انجام کاری را دریافت کرد، بداند که کدام کلاس از عهده ی این کار بر میآید -شاید هم خودش از عهده ی آن برآید- و به چه شکل درخواست را می بایست در اختیار آن کلاس قرار دهد. برای روشنتر شدن این مسأله، یو ام ال زیر را مد نظر قرار می دهیم:
فرض کنیم که سه آبجکت داریم تحت عناوین Customer, ShoppingCart و Item. حال فرض کنیم که آبجکت مشتری میخواهد بداند که تعداد کل محصولات قرار گرفته در سبد خرید چند مورد است. چنین کاری را ما میتوانیم داخل آبجکت مشتری انجام دهیم چرا که به نوعی دربرگیرنده ی دو آبجکت دیگر است اما واقعیت امر این است که آبجکت سبد خرید است که مسئول واقعی این کار است.
Creator
در اینجا منظور این است که چه کسی مسئول ساخت یک آبجکت است. با توجه به اینکه تعاملات بسیاری مابین آبجکت های مختلف یک نرمافزار وجود دارد، درک این موضوع که کدام آبجکت توسط کدام بخش نرمافزار ساخته شده است کار نسبتاً دشواری است. در چنین شرایطی کشیدن نمودارهای سلسله مراتبی کلاسهای نرمافزار تاحدودی کمک می کند. آنچه در اینجا حائز اهمیت است این میباشد که آیا یک آبجکت دربرگیرنده ی آبجکت دیگری نیز هست؟
Low Coupling / High cohesion
به طور کلی منظور از Low Coupling این است که یک برنامه نویس می بایست تا حد ممکن میزان Dependency یا «وابستگی» مابین آبجکت های مختلف را به حداقل برساند. اگر یک آبجکت نیاز دارد تا به مثلاً پنج آبجکت دیگر وصل شود، برنامه ی ما دارای High Coupling است و این مسأله میتواند در آینده مشکل زا گردد. فرض کنیم در آینده یکی از آبجکت هایی که آبجکت ما به آن وابسته است را تغییر می دهیم، از آن لحظه به بعد عملکرد آبجکت ما هم آن طور که انتظار میرود درست پیش نخواهد رفت. پس به خاطر داشته باشیم که می بایست تا حد ممکن وابستگیها را به حداقل برسانیم.
علاوه بر این، توجه داشته باشیم که Low Coupling هرگز بدان معنا نیست که ما هیچ گونه وابستگی به سایر آبجکت های برنامه نخواهیم داشت. آبجکت ها نیاز دارند تا اطلاعاتی در مورد یکدیگر داشته باشند اما همواره می بایست تمام تلاش خود را به کار بندیم تا این میزان وابستگی در کمترین حد ممکن نگه داشته شود.
به طور کلی، منظور از cohesion در برنامه نویسی شیء گرا این است که یک کلاس تا چه اندازه مسئولیت های تخصصی و متمرکزی دارد. اگر یک کلاس بهقدری خوب نوشته شده باشد که صرفاً یک مسئولیت را برعهده گیرد -یا یکسری مسئولیت های مرتبط با هم را بر عهده گیرد- اینجا است که میگوییم کلاس دارای High cohesion است. مثلاً کلاسهای «همه فن حریف» که دارای طیف گسترده ای از وظایف هستند، اصطلاحاً دارای Low cohesion می باشند. در یک کلام، کلاسی که ما می نویسیم می بایست دارای Low Coupling و High cohesion باشد.
Controller
فرض کنیم که قصد کدنویسی یک وب اپلیکیشن را داریم. این وب اپلیکیشن در سادهترین حالت ممکن دارای یک UI یا «رابط کاربری» است که کاربران سایت آن را مشاهده میکنند و یک Logic که «منطق» وب اپلیکیشن که مشخص میکند در ارتباط با درخواست های مختلف، چه جوابی برای کاربر می بایست ارسال گردد.
در چنین شرایطی اگر ما آبجکت UI را به طور مستقیم درگیر با آبجکت Logic وب اپلیکیشن کنیم، اصطلاحاً High Coupling ایجاد کردهایم که اصلاً خوب نیست و بایستی تمام تلاش خود را به کار بندیم تا این وابستگی را به حداقل برسانیم و اینجا است که پای آبجکت کنترلر به میان میآید که به مثابه ی یک واسطه عمل می کند. به عبارت دیگر، درخواست ها را از آبجکت رابط کاربری گرفته و تحویل به آبجکت منطق وب اپلیکیشن می دهد. آبجکت منطق هم پس از تجزیه و تحلیل این درخواست، پاسخی برای آن آماده کرده و تحویل آبجکت کنترلر میدهد و کنترلر هم آنرا دو دستی تقدیم به رابط کاربری می کند! چنین چیزی اصطلاحاً MVC نامیده میشود که به منزله ی یکی از معروف ترین الگوی های مدرن برنامه نویسی که در فصل بعد به تفصیل در مورد آن صحبت خواهیم کرد.
Pure Fabrication
مواقعی برای ما پیش خواهد آمد که نیاز داریم چیزی در اپلیکیشن خود داشته باشیم lما آن چیز را نمیتوان به عنوان یک کلاس یا آبجکت تلقی کرد. به عبارت دیگر، مواقعی برای ما در کدنویسی پیش خواهد آمد که Behavior یی داریم که نمیتوان آن را در قالب یکی از کلاسهای موجود اپلیکیشن گنجاند. در چنین مواقعی، به جای به زور تحمیل کردن آن Behavior یا «عملکرد» به یک کلاس و بالتبع کاهش دادن cohesion، در عوض ما اقدام به ساخت یک کلاس جدید می کنیم.
درست است که چنین کلاسی در برنامهریزی اولیه ی ما هیچ جایی نداشته اما در شرایط فعلی به آن نیاز داریم، پس آن را خواهیم ساخت!
Indirection
در یک کلام، هدف از Indirection کاهش میزان وابستگی مابین آبجکت های مختلف است. فرض کنیم چهار آبجکت مختلف داریم که برای انجام وظایف شان، نیاز دارند تا با یکدیگر در ارتباط باشند.
آنچه در یو ام ال فوق مشاهده می شود، سطح بالایی از وابستگی مابین آبجکت ها را نشان میدهد که اصلاً خوب نیست.
در عوض، همان طور که در یو ال ام فوق مشاهده می شود ما میتوانیم ارتباط مستقیم مابین آبجکت های مختلف را حذف کرده و یک Indirection Object میان آنها قرار دهیم تا ارتباطات مابین آبجکت ها را مدیریت کند.
Polymorphism
پیش از این هم در مبحث مربوط به شیء گرایی در مورد Polymorphism صحبت کردیم. به طور خلاصه، این مفهوم عبارت است از تغییر رفتار یک کلاس بر اساس نوع دادهای که در اختیارش قرار می دهیم.
Protected Variations
این مفهوم مرتبط با شرایطی است که میخواهیم مشخص کنیم بروز یک تغییر در سیستم، تا چه حد بخشهای دیگر سیستم را تحت الشعاع خود قرار می دهد. به عبارت دیگر، می بایست کدنویسی ما بر اساسی صورت گیرد که ایجاد تغییر در سیستم، دارای حداقل کانفلیکت در سایر بخشها گردد. بر همین اساس، ما به عنوان یک برنامه نویس حرفهای می بایست نقاطی از اپلیکیشن که بسیار حساس هستند و همچنین نقاطی که مستعد تغییر هستند را مشخص نموده تا در صورت بروز هرگونه تغییر، بتوانیم سیستم را به راحتی مدیریت کنیم. برای مثال، Encapsulation یکی از راههایی است که از آن طریق میتوانیم این تضمین را ایجاد کنیم که در صورت بروز هرگونه تغییری در سیستم، حداقل کانفلیکت رخ خواهد داد. به عنوان راهکاری دیگر، میتوان به اینترفیس ها اشاره کرد که ما را ملزم به استفاده از چیزهایی میکنند که در آینده منجر به یکپارچکی بیشتر نرمافزار خواهند شد. علاوه بر این ها، قانون Open /Closed Principle در SOLID نیز میتواند متضمن ایجاد ثبات در نرم افزارهای ما گردد.