اصل تفکیک رابطها در اصول SOLID
اصل Interface Segregation Principle در اصول SOLID چیست؟
Interface Segregation Principle یا به اختصار ISP که در فارسی به آن «اصل تفکیک رابطها» یا «اصل تفکیک اینترفیسها» یا «اصل جداسازی واسطها» هم گفته میشود، یک اصل مهندسی نرمافزار و اصل چهارم از اصول طراحی SOLID است.
این اصل بیان میکند که یک کلاس نباید مجبور به پیادهسازی رابطهایی شود که از آنها استفاده نمیکند. به عبارت دیگر، از رابطهای کوچکتر و تخصصیتر حمایت میکند که متناسب با نیازهای کلاسهای خاص باشد.
اصل ISP ایجاد چندین رابط کوچکتر و خاص را به جای داشتن یک رابط همه منظوره تشویق میکند. زیرا یک رابط همه منظوره میتواند منجر به پیچیدگیها و وابستگیهای ناخواسته شود.
ایده کلیدی پشت ISP این است که یک کلاس فقط باید متدهایی را داشته باشد که مربوط به آن کلاس است و به آنها نیاز داشته باشد. پیاده سازی متدهایی که به آن کلاس مربوط نیست میتواند موجب پیچیدگی و کاهش خوانایی کدها شود.
به طور خلاصه، ISP بر اهمیت طراحی رابطهای انعطافپذیر و ماژولار تأکید میکند که به شدت بر نیازهای خاص کلاسها متمرکز شده و وابستگیها را کمتر میکند.
یک مثال از اصل تفکیک رابطها در دنیای واقعی
اگر به دستفروشهایی که معمولا در مترو یا بازارها لوازم موبایل و لپتاپ میفروشند مراجعه کنید، به احتمال زیاد یک کابل همه کاره دارند که شبیه تصویر زیر است و تلاش میکند شما را قانع کند که آن را بخرید چون با یک کابل میتوانید به چندین دستگاه متصل شوید:

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

در اینجا یک رابط Animal برای حیوانات داریم که دو متد دارد: متد makeSound() برای صدای حیوانات و متد move() برای حرکت حیوانات است.
interface Animal {
public function makeSound();
public function move();
}
در مرحله بعد، دو رابط اضافی SwimmableAnimal برای حیواناتی که شنا میکنند و FlyableAnimal برای حیواناتی که پرواز میکنند، تعریف میکنیم که رابط Animal را گسترش میدهند.
interface SwimmableAnimal extends Animal {
public function swim();
}
interface FlyableAnimal extends Animal {
public function fly();
}
رابط SwimmableAnimal یک متد swim() را تعریف میکند که نشان دهنده توانایی شنا کردن است. به طور مشابه، رابط FlyableAnimal یک متد fly() را تعریف میکند که نشان دهنده توانایی پرواز است.
سپس سه کلاس Dog برای «سگ»، Fish برای «ماهی» و Bird برای «پرنده» ایجاد میکنیم که نشان دهنده گونههای مختلف حیوانات است.
class Dog implements Animal {
public function makeSound() {
echo "Bark";
}
public function move() {
echo "Walking";
}
}
کلاس Dog رابط Animal را پیاده سازی میکند و تمام متدهای تعریف شده در رابط Animal را پیادهسازی میکند. یعنی کلاس Dog «توانایی ایجاد صدا» و «توانایی حرکت کردن»» را دارد.
class Fish implements SwimmableAnimal {
public function makeSound() {
echo "Blub";
}
public function move() {
echo "Swimming";
}
public function swim() {
echo "Swimming";
}
}
کلاس Fish رابط SwimmableAnimal را پیادهسازی میکند که رابط Animal را گسترش می دهد. این به این معنی است که کلاس ماهی «توانایی ایجاد صدا» و «توانایی حرکت کردن» را دارد. اما علاوه بر این دو متد، یک متد هم برای «توانایی شنا کردن» دارد.
class Bird implements FlyableAnimal {
public function makeSound() {
echo "Chirp";
}
public function move() {
echo "Flying";
}
public function fly() {
echo "Flying";
}
}
کلاس Bird رابط FlyableAnimal را پیادهسازی میکند که رابط Animal را گسترش میدهد. این بدان معناست که کلاس Bird «توانایی ایجاد صدا» و «توانایی حرکت کردن» را دارد. اما علاوه بر این دو متد، یک متد هم برای «توانایی پرواز کردن» دارد.
با پیروی از اصل جداسازی رابط (ISP)، ما در اینجا به جای داشتن یک رابط واحد که همه ویژگیها را برای همه حیوانات داشته باشد، چندین رابط ایجاد کردهایم که ویژگیهای خاص انواع مختلف حیوانات را تعریف میکند.
به این ترتیب، کلاسی که فقط رابط Animal را پیادهسازی میکند، تنها نیاز به پیادهسازی ویژگیهای اساسی دارد که همه حیوانات دارند، در حالی که کلاسی که SwimmableAnimal یا FlyableAnimal را پیادهسازی میکند، میتواند ویژگیهای اضافی مخصوص آن حیوانات را پیادهسازی کند.
در نهایت کد ما به صورت کد زیر میشود:
interface Animal {
public function makeSound();
public function move();
}
interface SwimmableAnimal extends Animal {
public function swim();
}
interface FlyableAnimal extends Animal {
public function fly();
}
class Dog implements Animal {
public function makeSound() {
echo "Bark";
}
public function move() {
echo "Walking";
}
}
class Fish implements SwimmableAnimal {
public function makeSound() {
echo "Blub";
}
public function move() {
echo "Swimming";
}
public function swim() {
echo "Swimming";
}
}
class Bird implements FlyableAnimal {
public function makeSound() {
echo "Chirp";
}
public function move() {
echo "Flying";
}
public function fly() {
echo "Flying";
}
}
این اصل موجب میشود که نیازی به نوشتن کدهای اضافه نداشته باشیم و میتواند پیاده سازی کدها را سادهتر و قابل درکتر کند که باعث ایجاد اتصال بین اجزا میشود.