اصول طراحی SOLID در #C (قسمت آخر)

در این مطلب اصل آخر از SOLID یعنی Dependency Inversion Principle را بررسی می کنیم.
این پنجمین اصل از اصول SOLID است که به شرح زیر تعریف می شود.
کلاسهای سطح بالا نباید به کلاسهای سطح پایین وابسته باشند. هر دو باید وابسته به انتزاع (Abstractions) باشند. موارد انتزاعی نباید وابسته به جزییات باشند بلکه جزییات باید وابسته به انتزاع باشند.
مواردی مانند کلاس های سطح بالا ، سطح پایین و انتزاع احتمالا کمی باعث سردرگمی شما شده باشد ابتدا به صورت مختصر هریک از آنها را توضیح میدهم.
کلاس سطح پایین :
کلاس هایی هستند که عملیات پایه ای و زیرساختی مانند ارتباط با دیتابیس و یا ارسال ایمیل را انجام میدهند.
کلاس سطح بالا:
کلاس هایی هستند که عملیات خاصی را انجام میدهند و برای انجام عملیات از کلاس های سطح پایین بهره می برند. مثلا کلاسی که ورود به سیستم را پیاده سازی کرده است از کلاس سطح پایین ارتباط با دیتابیس و کلاس ارسال ایمیل استفاده می کند.
انتزاع (Abstractions) :
این نوع کلاس ها پیاده سازی نمی شوند بلکه به عنوان یک الگو برای کلاس های دیگر در نظر گرفته می شود.
در نظر بگیرید وقتی میگوییم پرنده ، پرنده نام گونه خاصی از پرندگان نیست بلکه یک انتزاع برای کبوتر ، عقاب ، گنجشک و سایر پرندگان می باشد.
این اصل نشان میدهد که باید بین کلاسهای سطح بالا و سطح پایین اتصال سست وجود داشته باشد. درواقع کلاسها باید به اینترفیس ها و کلاسهای انتزاعی وابستگی داشته باشند نه به نوع مشخصی از یک کلاس!
توضیح
اصل Dependency Inversion (DI) در SOLID به عنوان Inversion of control (IoC) نیز شناخته می شود. این اصل در ابتدا IoC نامیده می شد، اما مارتین فاولر نام DI یعنی تزریق وابستگی یا وارونگی وابستگی را ابداع کرد.
این اصل به سادگی می گوید که شما باید انتزاعی را بین کلاس های سطح بالا و سطح پایین معرفی کنید که به ما امکان می دهد کلاس های سطح بالا و سطح پایین را از یکدیگر جدا کنیم.
اگر کلاس ها به یکدیگر وابسته باشند، بین آنها اتصال محکم ایجاد می شوند که در این صورت، تغییر در هر یک از کلاس ها باعث ایجاد تغییرات در تمام کلاس های وابسته دیگر نیز می شود. در عوض، کلاسهای سطح پایین باید قراردادها را با استفاده از یک رابط یا کلاسهای انتزاعی پیادهسازی کنند و کلاسهای سطح بالا باید از این قراردادها استفاده کنند.
این اصل با اصول دیگر در Solid Principles مرتبط است، یعنی اگر شما از هر دو اصل Open/Closed و اصل جایگزینی Liskov در کد خود پیروی کنید، به طور غیرمستقیم از اصل وارونگی وابستگی پیروی کرده اید.
به کد زیر نگاه کنید که در آن کلاس Order برای عملیات پایگاه داده به OrderRepository وابسته است.
public class OrderRepository
{
public bool AddOrder(object orderDetails)
{
//Save Order to Database
return true;
}
public bool ModifyOrder(object orderDetails)
{
//Modify Order Details in Database
return true;
}
public object GetOrderDetails(string orderId)
{
object orderDetails = new object();
//Get Order Details from Database for given oderId
return orderDetails;
}
}
public class Order
{
private OrderRepository _orderRepository = null;
public Order()
{
_orderRepository = new OrderRepository();
}
public bool AddOrder(object orderDetails)
{
return _orderRepository.AddOrder(orderDetails);
}
public bool ModifyOrder(object orderDetails)
{
return _orderRepository.ModifyOrder(orderDetails);
}
public object GetOrderDetails(string orderId)
{
return _orderRepository.GetOrderDetails(orderId);
}
}
همانطور که در کد بالا می بینیم، یک شی از کلاس repository را در کلاس order نمونه سازی کرده ایم و وابستگی مستقیم 2 کلاس وجود دارد. این در نقض اصل وارونگی وابستگی در SOLID است که بیان میکند که کلاسها باید با استفاده از انتزاع به صورت سست با یکدیگر اتصال داشته باشند.
بیایید کد را مطابق با Dependency Inversion Principle تغییر دهیم و انتزاع را بین کلاس ها قرار دهیم.
public interface IOrderRespository
{
bool AddOrder(object orderDetails);
bool ModifyOrder(object orderDetails);
object GetOrderDetails(string orderId);
}
public class OrderRepository : IOrderRespository
{
public bool AddOrder(object orderDetails)
{
//Save Order to Database
return true;
}
public bool ModifyOrder(object orderDetails)
{
//Modify Order Details in Database
return true;
}
public object GetOrderDetails(string orderId)
{
object orderDetails = new object();
//Get Order Details from Database for given oderId
return orderDetails;
}
}
public class Order
{
private IOrderRespository _orderRepository = null;
public Order(IOrderRespository orderRepository)
{
_orderRepository = orderRepository;
}
public bool AddOrder(object orderDetails)
{
return _orderRepository.AddOrder(orderDetails);
}
public bool ModifyOrder(object orderDetails)
{
return _orderRepository.ModifyOrder(orderDetails);
}
public object GetOrderDetails(string orderId)
{
return _orderRepository.GetOrderDetails(orderId);
}
}
همانطور که از کد بالا می بینید آنچه ما اصلاح کرده ایم این است که یک اینترفیس برای OrderRepository اضافه کرده ایم و همان را در کلاس OrderRepository پیاده سازی کرده ایم که در کلاس order به جای نمونه سازی مستقیم از OrderRepository ، از انتزاع یعنی IOrderRepository استفاده کرده ایم. بنابراین اکنون در کلاس order، میتوانیم از هر کلاسی که اینترفیس را برای OrderRepository پیادهسازی میکند، استفاده کنیم.
اکنون می توانیم مطابق شکل زیر از کد بالا استفاده کنیم
Order order = new Order(new OrderRepository());
از کد بالا می بینیم که هنگام نمونه سازی اشیاء برای کلاس order، نمونه ای از OrderRepository را از سازنده ارسال می کنیم. بنابراین به جای اینکه کلاس order به کلاس order repository وابسته باشد، به IOrderRepository وابسته است.
به این ترتیب ما میتوانیم هر کلاسی را که IOrderRepository را پیادهسازی میکند به کلاس Order برای عملیات پایگاه داده منتقل کنیم. به عنوان مثال، اگر فردا قصد داریم جزئیات سفارش را در یک فایل ذخیره کنیم، میتوانیم یک OrderRepository جدید ایجاد کرده و آن را به کلاس Order منتقل کنیم. بنابراین بدون هیچ گونه تغییری در کلاس Order شروع به ذخیره جزئیات در پرونده می کند.
مزایا :
- کلاس ها به انتزاع بستگی دارند.
- کلاس های سطح بالا و سطح پایین به طور ضعیفی به یکدیگر متصل می شوند.
- تغییر در یک کلاس باعث تغییر در کلاس دیگر نمی شود.
- تغییر در یک کلاس باعث ایجاد خطا در کلاس دیگر نمی شود.
سخن پایانی :
این پنجمین و آخرین اصل در Solid Principles است که در عین حال یکی از اصول طراحی مهم در برنامهنویسی امروزی است. این اصل ، وابستگی بین موجودیت ها را با تعریف روشی که “دو کلاس باید به طور آزادانه با استفاده از انتزاع به یکدیگر متصل باشند” حذف می کند.
هنگامی که یک کلاس اطلاعات زیادی در مورد جزئیات یک کلاس دیگر داشته باشد، این خطر وجود دارد که تغییرات در یک کلاس ، کلاس دیگر را خراب کند، بنابراین کلاسهای سطح بالا و سطح پایین باید تا حد امکان به صورت سست به یکدیگر متصل باشند.
جمع بندی
اصول SOLID را با مثال هایی بررسی کردیم ولی برای تسلط و ماندگار شدن مطالب نیاز به تمرین و حل مثال های گوناگون دارید.
اصول SOLID را در کد خود اعمال کنید و خسته نشوید،خواهید دید که کد شما قوی، انعطاف پذیر شده و نگهداری آسان خواهد بود، یعنی به طور خلاصه طول عمر کد شما افزایش می یابد.
دیدگاهتان را بنویسید