اصول جامد – انجمن DEV

Summarize this content to 400 words in Persian Lang
جامد مجموعه ای از اصول اساسی است که برای افزایش مدیریت و مقیاس پذیری کد در برنامه نویسی شی گرا (OOP) طراحی شده است. از پنج اصل کلیدی تشکیل شده است:
اس اصل مسئولیت یکپارچه – SRP
O اصل بسته قلم – OCP
L اصل جایگزینی iskov – LSP
من اصل جداسازی رابط – ISP
دی اصل وارونگی وابستگی – DIP
این اصول توسط رابرت سی مارتین (همچنین به عنوان عمو باب شناخته می شود) در اوایل دهه 2000 و از آن زمان به طور گسترده در جامعه توسعه نرم افزار پذیرفته شده است. با پیروی از اصول SOLID، توسعهدهندگان میتوانند کدی ایجاد کنند که درک، اصلاح و گسترش آن آسانتر باشد، که منجر به سیستمهای نرمافزاری قویتر و قابل نگهداریتر میشود.
اصل مسئولیت واحد (SRP)
Single Responsibility Principle اولین و اساسی ترین اصل در OOP و SOLID است. همانطور که از نام به نظر می رسد، این اصل به معنای “یک کلاس باید فقط یک مسئولیت خاص برای مراقبت داشته باشد”.
فرض کنید کلاسی به نام Invoice داریم که شامل 2 متد ()genereInvoice و saveToFiles() است.
public class Invoice {
private Long InvoiceNo;
public void generateInvoice() {
// code to generate Invoice.
}
public void saveToFiles() {
// code to save invoice as a file.
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
این روش خوبی نیست زیرا کلاس Invoice دو مسئولیت دارد. یک رویکرد بهتر این است که این قابلیت ها را به کلاس های اختصاصی جدا کنیم.
public class Invoice {
private Long InvoiceNo;
public void generateInvoice() {
// code to generate Invoice.
}
}
public class FileManager {
public void saveToFiles(Invoice invoice) {
// code to save invoice as a file.
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
در اینجا، می توانیم ببینیم که 2 کلاس برای موارد استفاده داریم:
تولید فاکتور
آن را در فایل ها ذخیره کنید
مزایای پیروی از SRP
سازمان کد بهبود یافته : با تفکیک نگرانیها به کلاسهای مختلف، پایگاه کد سازمانیافتهتر و پیمایش آسانتر میشود.
قابلیت نگهداری بهتر : وقتی یک کلاس یک مسئولیت دارد، درک هدف آن و ایجاد تغییرات بدون عوارض ناخواسته آسان تر است.
افزایش قابلیت استفاده مجدد : کلاس هایی با یک مسئولیت واحد به احتمال زیاد در قسمت های مختلف اپلیکیشن یا حتی در پروژه های دیگر قابل استفاده مجدد هستند.
تست آسانتر : کلاس هایی با مسئولیت واحد معمولا کوچکتر و متمرکزتر هستند و آزمایش آنها را به صورت مجزا آسان تر می کند.
اصل باز-بسته (OCP)
اصل باز-بسته یکی دیگر از اصول اصلی در SOLID است. این اصل توسط برتراند مایر در سال 1997. ایده پشت این اصل این است که “مصنوعات نرم افزاری (کلاس ها، ماژول ها و توابع) باید برای برنامه های افزودنی باز شوند، اما برای تغییرات بسته شوند.”
به عنوان مثال؛
فرض کنید یک کلاس به نام Shape داریم، می توانیم از این کلاس برای محاسبه مساحت شکل استفاده کنیم.
public class Shape {
private String shapeType;
private double radius;
private double length;
private double width;
public Shape(String shapeType, double radius, double length, double width) {
this.shapeType = shapeType;
this.radius = radius;
this.length = length;
this.width = width;
}
public double area() {
if (shapeType.equals(“circle”)) {
return Math.PI * (radius * radius);
} else if (shapeType.equals(“rectangle”)) {
return length * width;
} else {
throw new IllegalArgumentException(“Unknown shape type”);
}
}
}
// Usage
public class Main {
public static void main(String[] args) {
Shape circle = new Shape(“circle”, 5, 0, 0);
Shape rectangle = new Shape(“rectangle”, 0, 4, 6);
System.out.println(circle.area());
System.out.println(rectangle.area());
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
در کد بالا، افزودن یک شکل جدید مستلزم اصلاح کلاس Shape موجود است که عمل خوبی در نظر گرفته نمی شود.
در زیر یک نمونه کد وجود دارد که نحوه اعمال اصل بسته باز را در این سناریو نشان می دهد.
public interface Shape {
public double area();
}
class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * (radius * radius);
}
}
class Rectangle implements Shape {
private double length;
private double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
public double area() {
return length * width;
}
}
// Usage
public class Main {
public static void main(String[] args) {
Shape circle = new Circle(5);
Shape rectangle = new Rectangle(4, 6);
System.out.println(circle.area());
System.out.println(rectangle.area());
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
با استفاده از OCP، میتوانیم بدون تغییر پیادهسازی فعلی، شکلهای زیادی را به دلخواه خود اضافه کنیم.
توجه: استفاده از رابط ها تنها راه برای دستیابی به OCP نیست.
مزایای پیروی از OCP
کاهش خطر اشکالات : با اصلاح نکردن کد موجود، خطر ایجاد اشکالات جدید یا شکستن عملکرد موجود به حداقل می رسد.
قابلیت نگهداری بهبود یافته : کدی که از OCP پیروی می کند، نگهداری و گسترش آن آسان تر است، زیرا ویژگی های جدید را می توان بدون تغییر در پایگاه کد موجود اضافه کرد.
انعطاف پذیری پیشرفته : استفاده از انتزاعات و چند شکلی امکان طراحی های انعطاف پذیرتر و سازگارتر را فراهم می کند و تطبیق نیازهای متغیر را آسان تر می کند.
اصل جایگزینی لیسکوف (LSP)
اصل جایگزینی لیسکوف یکی دیگر از اصول مهم در OOP است. توسط معرفی شد باربارا لیسکوف در سال 1987 طی یک سخنرانی در کنفرانسی در مورد انتزاع داده ها.
این اصل می گوید: “اشیاء یک سوپرکلاس باید با اشیاء زیر کلاس های آن بدون تغییر در صحت برنامه قابل تعویض باشند”.
به عنوان مثال، اگر Circle و Rectangle زیر انواع Shape هستند، باید بتوانیم بدون هیچ مشکلی شی Shape را با یک شی Circle یا Rectangle جایگزین کنیم.
public interface Shape {
public double area();
}
class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * (radius * radius);
}
}
class Rectangle implements Shape {
private double length;
private double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
public double area() {
return length * width;
}
}
public class Main {
public static void main(String[] args) {
Shape circle = new Circle(5); // Replace Shape object with Circle
Shape rectangle = new Rectangle(4, 6); // Replace Shape object with Rectangle
System.out.println(circle.area());
System.out.println(rectangle.area());
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
همانطور که در این مثال نشان داده شد، پایبندی به اصل جایگزینی لیسکوف به این معنی است که باید بتوانیم یک نمونه سوپرکلاس را با یک نمونه زیر کلاس جایگزین کنیم.
مزایای پیروی از LSP
قابلیت استفاده مجدد کد بهبود یافته : با حصول اطمینان از اینکه میتوان انواع فرعی را جایگزین انواع پایه آنها کرد، کدی که از نوع پایه استفاده میکند نیز میتواند با هر یک از زیرگروههای آن کار کند و استفاده مجدد از کد را ترویج کند.
قابلیت نگهداری پیشرفته : حفظ کدی که از LSP پیروی می کند آسان تر است زیرا خطر ایجاد اشکال در هنگام اصلاح یا گسترش پایگاه کد را کاهش می دهد.
تست پذیری بهتر : LSP نوشتن تستهای واحد برای کلاسها و زیرگروههای آنها را آسانتر میکند، زیرا تستها را میتوان بر خلاف نوع پایه نوشت و باید برای همه زیرگروهها کار کند.
اصل جداسازی رابط (ISP)
اصل جداسازی رابط یکی از پنج اصل SOLID است که توسط رابرت سی مارتین. بیان میکند: «کلاینتها نباید مجبور شوند به رابطهایی که استفاده نمیکنند وابسته شوند».
به عبارت دیگر، استفاده از بسیاری از واسطهای خاص وظیفه بهتر از استفاده از یک رابط کاربری عمومی است.
مثال زیر استفاده از رابط کاربری عمومی را نشان می دهد.
public interface MultifunctionPrinter {
void printDocument(Document document);
void scanDocument(Document document);
void faxDocument(Document document);
}
public class BasicPrinter implements MultifunctionPrinter {
@Override
public void printDocument(Document document) {
System.out.println(“Printing: ” + document.name());
}
@Override
public void scanDocument(Document document) {
throw new UnsupportedOperationException(“Scanning not supported.”);
}
@Override
public void faxDocument(Document document) {
throw new UnsupportedOperationException(“Faxing not supported.”);
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
استفاده از یک رابط همه منظوره مانند MultifunctionPrinter ما را مجبور میکند تا روشهای غیر ضروری را پیادهسازی کنیم، که عمل بدی در نظر گرفته میشود. بیایید بررسی کنیم که چگونه می توانیم اصل جداسازی رابط را در این سناریو اعمال کنیم.
رابط ها
public interface Printer {
void printDocument(Document document);
}
public interface Scanner {
void scanDocument(Document document);
}
public interface Fax {
void faxDocument(Document document);
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
پیاده سازی ها
public class BasicPrinter implements Printer {
@Override
public void printDocument(Document document) {
System.out.println(“Printing: ” + document.name());
}
}
public class AdvancedPrinter implements Printer, Scanner {
@Override
public void printDocument(Document document) {
System.out.println(“Printing: ” + document.name());
}
@Override
public void scanDocument(Document document) {
System.out.println(“Scanning: ” + document.name());
}
}
public class FaxMachine implements Fax {
@Override
public void faxDocument(Document document) {
System.out.println(“Faxing: ” + document.name());
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
با استفاده از ISP ، ما آن را به رابط های کوچکتر و مخصوص نقش تقسیم کردیم – مانند چاپگر، اسکنر و فکس. این به هر کلاس (مثلاً BasicPrinter، AdvancedPrinter یا FaxMachine) اجازه میدهد تا فقط عملکردهای مربوطه را پیادهسازی کند، ماژولار بودن را ارتقا داده و وابستگیهای غیرضروری را کاهش دهد.
مزایای پیروی از ISP
کد ماژولار و قابل استفاده مجدد : با تقسیم کردن رابط های بزرگ به رابط های کوچکتر و خاص تر، کد ماژولارتر و قابل استفاده مجدد می شود. کلاس ها یا ماژول ها فقط می توانند رابط های مورد نیاز خود را پیاده سازی کنند و وابستگی های غیر ضروری را کاهش دهند و استفاده مجدد از کد را در بخش های مختلف سیستم آسان تر کنند.
کاهش پیچیدگی کد : وقتی کلاسها یا ماژولها فقط به رابطهایی که نیاز دارند وابسته باشند، کد پیچیدهتر و درک آن آسانتر میشود. این به این دلیل است که توسعه دهندگان مجبور نیستند با روش ها یا وابستگی های غیر ضروری سر و کار داشته باشند. اینها به مورد استفاده خاص آنها مربوط نیست.
قابلیت نگهداری بهبود یافته : با رابط های کوچکتر و متمرکز تر، حفظ کد آسان تر می شود. تغییرات در یک رابط احتمال کمتری دارد که سایر بخشهای سیستم را تحت تأثیر قرار دهد و خطر ایجاد باگ یا شکستن عملکرد موجود را کاهش دهد.
تست پذیری بهتر : رابط های کوچکتر و متمرکزتر نوشتن تست های واحد را برای اجزای جداگانه آسان تر می کند. این به این دلیل است که آزمونها میتوانند بر روی رفتارهای خاص تمرکز کنند بدون اینکه تحت تأثیر روشها یا وابستگیهای نامربوط قرار گیرند.
افزایش انعطاف پذیری : با پایبندی به ISP، سیستم انعطاف پذیرتر می شود و گسترش یا اصلاح آن آسان تر می شود. ویژگیها یا نیازمندیهای جدید را میتوان با ایجاد رابطهای جدید یا اصلاح رابطهای موجود بدون تأثیر بر کل سیستم اضافه کرد.
اصل وارونگی وابستگی (DIP)
Dependency Inversion Principle اصل نهایی SOLID است. که توسط آن نیز معرفی شد رابرت سی مارتین. این کدهای با اتصال آزاد را ترویج می کند.
DIP چند نکته را بیان می کند:
ماژول های سطح بالا نباید به ماژول های سطح پایین وابسته باشند.
هر دو باید به انتزاع بستگی داشته باشند.
انتزاع نباید به جزئیات بستگی داشته باشد.
جزئیات باید به انتزاع بستگی داشته باشد.
به عبارت ساده، به جای اینکه یک کلاس مستقیماً به کلاس های خاص دیگر (پیاده سازی های بتن) وابسته باشد، باید به آن بستگی داشته باشد رابط ها یا کلاس های انتزاعی. این باعث میشود کد انعطافپذیرتر و نگهداری آسانتر شود، زیرا میتوانید بدون تغییر کلاس وابسته، پیادهسازیها را تعویض کنید.
کد محکم همراه (بدون DIP)
public class Keyboard {
public void type() {
System.out.println(“Typing…”);
}
}
public class Computer {
private Keyboard keyboard;
public Computer() {
this.keyboard = new Keyboard(); // Direct dependency
}
public void use() {
keyboard.type();
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
همانطور که در مثال بالا نشان داده شده است، کلاس Computer مستقیماً به کلاس Keyboard بستگی دارد.
کد جفت شده ضعیف (با DIP)
public interface InputDevice {
void type();
}
pubic class Keyboard implements InputDevice {
@Override
public void type() {
System.out.println(“Typing…”);
}
}
public class Computer {
private InputDevice inputDevice;
public Computer(InputDevice inputDevice) {
this.inputDevice = inputDevice; // Depends on abstraction
}
public void use() {
inputDevice.type();
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
اکنون، رایانه به رابط InputDevice بستگی دارد، نه یک صفحه کلید خاص. این باعث میشود بدون تغییر کلاس کامپیوتر، به دستگاه ورودی دیگری مانند صفحهکلید بیسیم سوئیچ کنید.
مزایای دنبال کردن DIP
کوپلینگ شل : با وابستگی به انتزاعات به جای پیاده سازی های عینی، کدها با هم پیوند کمتری پیدا می کنند و تغییر یک قسمت از سیستم را بدون تأثیرگذاری بر سایرین آسان تر می کند.
قابلیت نگهداری بهبود یافته : تغییرات در ماژولهای سطح پایین بر ماژولهای سطح بالا تأثیر نمیگذارد و نگهداری و گسترش سیستم را آسانتر میکند.
قابلیت تست پیشرفته : ماژول های سطح بالا را می توان با استفاده از پیاده سازی های ساختگی ماژول های سطح پایین آزمایش کرد و آزمایش را سریع تر و قابل اطمینان تر کرد.
افزایش قابلیت استفاده مجدد : ماژول های سطح بالا را می توان در زمینه های مختلف بدون نیاز به تغییر ماژول های سطح پایین مورد استفاده مجدد قرار داد.
نتیجه گیری
در نتیجه، اصول SOLID: مسئولیت منفرد، باز-بسته، جایگزینی Liskov، جداسازی رابط، و وارونگی وابستگی دستورالعملهای ضروری را برای نوشتن کدهای تمیز، قابل نگهداری و مقیاسپذیر در برنامهنویسی شیگرا ارائه میکنند.
با رعایت این اصول، توسعهدهندگان میتوانند سیستمهایی ایجاد کنند که درک، اصلاح و گسترش آن آسانتر باشد و در نهایت منجر به نرمافزار با کیفیت بالاتر و فرآیندهای توسعه کارآمدتر شود.
خلاصه
با تشکر از شما برای خواندن این مقاله! امیدوارم اکنون درک کاملی از اصول SOLID داشته باشید و اینکه چگونه می توانید آنها را برای بهبود پروژه های خود به کار ببرید.
من را دنبال کنید:
– سادیشا نیم سرا
جامد مجموعه ای از اصول اساسی است که برای افزایش مدیریت و مقیاس پذیری کد در برنامه نویسی شی گرا (OOP) طراحی شده است. از پنج اصل کلیدی تشکیل شده است:
- اس اصل مسئولیت یکپارچه – SRP
- O اصل بسته قلم – OCP
- L اصل جایگزینی iskov – LSP
- من اصل جداسازی رابط – ISP
- دی اصل وارونگی وابستگی – DIP
این اصول توسط رابرت سی مارتین (همچنین به عنوان عمو باب شناخته می شود) در اوایل دهه 2000 و از آن زمان به طور گسترده در جامعه توسعه نرم افزار پذیرفته شده است. با پیروی از اصول SOLID، توسعهدهندگان میتوانند کدی ایجاد کنند که درک، اصلاح و گسترش آن آسانتر باشد، که منجر به سیستمهای نرمافزاری قویتر و قابل نگهداریتر میشود.
اصل مسئولیت واحد (SRP)
Single Responsibility Principle اولین و اساسی ترین اصل در OOP و SOLID است. همانطور که از نام به نظر می رسد، این اصل به معنای “یک کلاس باید فقط یک مسئولیت خاص برای مراقبت داشته باشد”.
فرض کنید کلاسی به نام Invoice داریم که شامل 2 متد ()genereInvoice و saveToFiles() است.
public class Invoice {
private Long InvoiceNo;
public void generateInvoice() {
// code to generate Invoice.
}
public void saveToFiles() {
// code to save invoice as a file.
}
}
این روش خوبی نیست زیرا کلاس Invoice دو مسئولیت دارد. یک رویکرد بهتر این است که این قابلیت ها را به کلاس های اختصاصی جدا کنیم.
public class Invoice {
private Long InvoiceNo;
public void generateInvoice() {
// code to generate Invoice.
}
}
public class FileManager {
public void saveToFiles(Invoice invoice) {
// code to save invoice as a file.
}
}
در اینجا، می توانیم ببینیم که 2 کلاس برای موارد استفاده داریم:
- تولید فاکتور
- آن را در فایل ها ذخیره کنید
مزایای پیروی از SRP
- سازمان کد بهبود یافته : با تفکیک نگرانیها به کلاسهای مختلف، پایگاه کد سازمانیافتهتر و پیمایش آسانتر میشود.
- قابلیت نگهداری بهتر : وقتی یک کلاس یک مسئولیت دارد، درک هدف آن و ایجاد تغییرات بدون عوارض ناخواسته آسان تر است.
- افزایش قابلیت استفاده مجدد : کلاس هایی با یک مسئولیت واحد به احتمال زیاد در قسمت های مختلف اپلیکیشن یا حتی در پروژه های دیگر قابل استفاده مجدد هستند.
- تست آسانتر : کلاس هایی با مسئولیت واحد معمولا کوچکتر و متمرکزتر هستند و آزمایش آنها را به صورت مجزا آسان تر می کند.
اصل باز-بسته (OCP)
اصل باز-بسته یکی دیگر از اصول اصلی در SOLID است. این اصل توسط برتراند مایر در سال 1997. ایده پشت این اصل این است که “مصنوعات نرم افزاری (کلاس ها، ماژول ها و توابع) باید برای برنامه های افزودنی باز شوند، اما برای تغییرات بسته شوند.”
به عنوان مثال؛
فرض کنید یک کلاس به نام Shape داریم، می توانیم از این کلاس برای محاسبه مساحت شکل استفاده کنیم.
public class Shape {
private String shapeType;
private double radius;
private double length;
private double width;
public Shape(String shapeType, double radius, double length, double width) {
this.shapeType = shapeType;
this.radius = radius;
this.length = length;
this.width = width;
}
public double area() {
if (shapeType.equals("circle")) {
return Math.PI * (radius * radius);
} else if (shapeType.equals("rectangle")) {
return length * width;
} else {
throw new IllegalArgumentException("Unknown shape type");
}
}
}
// Usage
public class Main {
public static void main(String[] args) {
Shape circle = new Shape("circle", 5, 0, 0);
Shape rectangle = new Shape("rectangle", 0, 4, 6);
System.out.println(circle.area());
System.out.println(rectangle.area());
}
}
در کد بالا، افزودن یک شکل جدید مستلزم اصلاح کلاس Shape موجود است که عمل خوبی در نظر گرفته نمی شود.
در زیر یک نمونه کد وجود دارد که نحوه اعمال اصل بسته باز را در این سناریو نشان می دهد.
public interface Shape {
public double area();
}
class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * (radius * radius);
}
}
class Rectangle implements Shape {
private double length;
private double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
public double area() {
return length * width;
}
}
// Usage
public class Main {
public static void main(String[] args) {
Shape circle = new Circle(5);
Shape rectangle = new Rectangle(4, 6);
System.out.println(circle.area());
System.out.println(rectangle.area());
}
}
با استفاده از OCP، میتوانیم بدون تغییر پیادهسازی فعلی، شکلهای زیادی را به دلخواه خود اضافه کنیم.
توجه: استفاده از رابط ها تنها راه برای دستیابی به OCP نیست.
مزایای پیروی از OCP
- کاهش خطر اشکالات : با اصلاح نکردن کد موجود، خطر ایجاد اشکالات جدید یا شکستن عملکرد موجود به حداقل می رسد.
- قابلیت نگهداری بهبود یافته : کدی که از OCP پیروی می کند، نگهداری و گسترش آن آسان تر است، زیرا ویژگی های جدید را می توان بدون تغییر در پایگاه کد موجود اضافه کرد.
- انعطاف پذیری پیشرفته : استفاده از انتزاعات و چند شکلی امکان طراحی های انعطاف پذیرتر و سازگارتر را فراهم می کند و تطبیق نیازهای متغیر را آسان تر می کند.
اصل جایگزینی لیسکوف (LSP)
اصل جایگزینی لیسکوف یکی دیگر از اصول مهم در OOP است. توسط معرفی شد باربارا لیسکوف در سال 1987 طی یک سخنرانی در کنفرانسی در مورد انتزاع داده ها.
این اصل می گوید: “اشیاء یک سوپرکلاس باید با اشیاء زیر کلاس های آن بدون تغییر در صحت برنامه قابل تعویض باشند”.
به عنوان مثال، اگر Circle و Rectangle زیر انواع Shape هستند، باید بتوانیم بدون هیچ مشکلی شی Shape را با یک شی Circle یا Rectangle جایگزین کنیم.
public interface Shape {
public double area();
}
class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * (radius * radius);
}
}
class Rectangle implements Shape {
private double length;
private double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
public double area() {
return length * width;
}
}
public class Main {
public static void main(String[] args) {
Shape circle = new Circle(5); // Replace Shape object with Circle
Shape rectangle = new Rectangle(4, 6); // Replace Shape object with Rectangle
System.out.println(circle.area());
System.out.println(rectangle.area());
}
}
همانطور که در این مثال نشان داده شد، پایبندی به اصل جایگزینی لیسکوف به این معنی است که باید بتوانیم یک نمونه سوپرکلاس را با یک نمونه زیر کلاس جایگزین کنیم.
مزایای پیروی از LSP
- قابلیت استفاده مجدد کد بهبود یافته : با حصول اطمینان از اینکه میتوان انواع فرعی را جایگزین انواع پایه آنها کرد، کدی که از نوع پایه استفاده میکند نیز میتواند با هر یک از زیرگروههای آن کار کند و استفاده مجدد از کد را ترویج کند.
- قابلیت نگهداری پیشرفته : حفظ کدی که از LSP پیروی می کند آسان تر است زیرا خطر ایجاد اشکال در هنگام اصلاح یا گسترش پایگاه کد را کاهش می دهد.
- تست پذیری بهتر : LSP نوشتن تستهای واحد برای کلاسها و زیرگروههای آنها را آسانتر میکند، زیرا تستها را میتوان بر خلاف نوع پایه نوشت و باید برای همه زیرگروهها کار کند.
اصل جداسازی رابط (ISP)
اصل جداسازی رابط یکی از پنج اصل SOLID است که توسط رابرت سی مارتین. بیان میکند: «کلاینتها نباید مجبور شوند به رابطهایی که استفاده نمیکنند وابسته شوند».
به عبارت دیگر، استفاده از بسیاری از واسطهای خاص وظیفه بهتر از استفاده از یک رابط کاربری عمومی است.
مثال زیر استفاده از رابط کاربری عمومی را نشان می دهد.
public interface MultifunctionPrinter {
void printDocument(Document document);
void scanDocument(Document document);
void faxDocument(Document document);
}
public class BasicPrinter implements MultifunctionPrinter {
@Override
public void printDocument(Document document) {
System.out.println("Printing: " + document.name());
}
@Override
public void scanDocument(Document document) {
throw new UnsupportedOperationException("Scanning not supported.");
}
@Override
public void faxDocument(Document document) {
throw new UnsupportedOperationException("Faxing not supported.");
}
}
استفاده از یک رابط همه منظوره مانند MultifunctionPrinter ما را مجبور میکند تا روشهای غیر ضروری را پیادهسازی کنیم، که عمل بدی در نظر گرفته میشود. بیایید بررسی کنیم که چگونه می توانیم اصل جداسازی رابط را در این سناریو اعمال کنیم.
رابط ها
public interface Printer {
void printDocument(Document document);
}
public interface Scanner {
void scanDocument(Document document);
}
public interface Fax {
void faxDocument(Document document);
}
پیاده سازی ها
public class BasicPrinter implements Printer {
@Override
public void printDocument(Document document) {
System.out.println("Printing: " + document.name());
}
}
public class AdvancedPrinter implements Printer, Scanner {
@Override
public void printDocument(Document document) {
System.out.println("Printing: " + document.name());
}
@Override
public void scanDocument(Document document) {
System.out.println("Scanning: " + document.name());
}
}
public class FaxMachine implements Fax {
@Override
public void faxDocument(Document document) {
System.out.println("Faxing: " + document.name());
}
}
با استفاده از ISP ، ما آن را به رابط های کوچکتر و مخصوص نقش تقسیم کردیم – مانند چاپگر، اسکنر و فکس. این به هر کلاس (مثلاً BasicPrinter، AdvancedPrinter یا FaxMachine) اجازه میدهد تا فقط عملکردهای مربوطه را پیادهسازی کند، ماژولار بودن را ارتقا داده و وابستگیهای غیرضروری را کاهش دهد.
مزایای پیروی از ISP
- کد ماژولار و قابل استفاده مجدد : با تقسیم کردن رابط های بزرگ به رابط های کوچکتر و خاص تر، کد ماژولارتر و قابل استفاده مجدد می شود. کلاس ها یا ماژول ها فقط می توانند رابط های مورد نیاز خود را پیاده سازی کنند و وابستگی های غیر ضروری را کاهش دهند و استفاده مجدد از کد را در بخش های مختلف سیستم آسان تر کنند.
- کاهش پیچیدگی کد : وقتی کلاسها یا ماژولها فقط به رابطهایی که نیاز دارند وابسته باشند، کد پیچیدهتر و درک آن آسانتر میشود. این به این دلیل است که توسعه دهندگان مجبور نیستند با روش ها یا وابستگی های غیر ضروری سر و کار داشته باشند. اینها به مورد استفاده خاص آنها مربوط نیست.
- قابلیت نگهداری بهبود یافته : با رابط های کوچکتر و متمرکز تر، حفظ کد آسان تر می شود. تغییرات در یک رابط احتمال کمتری دارد که سایر بخشهای سیستم را تحت تأثیر قرار دهد و خطر ایجاد باگ یا شکستن عملکرد موجود را کاهش دهد.
- تست پذیری بهتر : رابط های کوچکتر و متمرکزتر نوشتن تست های واحد را برای اجزای جداگانه آسان تر می کند. این به این دلیل است که آزمونها میتوانند بر روی رفتارهای خاص تمرکز کنند بدون اینکه تحت تأثیر روشها یا وابستگیهای نامربوط قرار گیرند.
- افزایش انعطاف پذیری : با پایبندی به ISP، سیستم انعطاف پذیرتر می شود و گسترش یا اصلاح آن آسان تر می شود. ویژگیها یا نیازمندیهای جدید را میتوان با ایجاد رابطهای جدید یا اصلاح رابطهای موجود بدون تأثیر بر کل سیستم اضافه کرد.
اصل وارونگی وابستگی (DIP)
Dependency Inversion Principle اصل نهایی SOLID است. که توسط آن نیز معرفی شد رابرت سی مارتین. این کدهای با اتصال آزاد را ترویج می کند.
DIP چند نکته را بیان می کند:
- ماژول های سطح بالا نباید به ماژول های سطح پایین وابسته باشند.
- هر دو باید به انتزاع بستگی داشته باشند.
- انتزاع نباید به جزئیات بستگی داشته باشد.
- جزئیات باید به انتزاع بستگی داشته باشد.
به عبارت ساده، به جای اینکه یک کلاس مستقیماً به کلاس های خاص دیگر (پیاده سازی های بتن) وابسته باشد، باید به آن بستگی داشته باشد رابط ها یا کلاس های انتزاعی. این باعث میشود کد انعطافپذیرتر و نگهداری آسانتر شود، زیرا میتوانید بدون تغییر کلاس وابسته، پیادهسازیها را تعویض کنید.
کد محکم همراه (بدون DIP)
public class Keyboard {
public void type() {
System.out.println("Typing...");
}
}
public class Computer {
private Keyboard keyboard;
public Computer() {
this.keyboard = new Keyboard(); // Direct dependency
}
public void use() {
keyboard.type();
}
}
همانطور که در مثال بالا نشان داده شده است، کلاس Computer مستقیماً به کلاس Keyboard بستگی دارد.
کد جفت شده ضعیف (با DIP)
public interface InputDevice {
void type();
}
pubic class Keyboard implements InputDevice {
@Override
public void type() {
System.out.println("Typing...");
}
}
public class Computer {
private InputDevice inputDevice;
public Computer(InputDevice inputDevice) {
this.inputDevice = inputDevice; // Depends on abstraction
}
public void use() {
inputDevice.type();
}
}
اکنون، رایانه به رابط InputDevice بستگی دارد، نه یک صفحه کلید خاص. این باعث میشود بدون تغییر کلاس کامپیوتر، به دستگاه ورودی دیگری مانند صفحهکلید بیسیم سوئیچ کنید.
مزایای دنبال کردن DIP
- کوپلینگ شل : با وابستگی به انتزاعات به جای پیاده سازی های عینی، کدها با هم پیوند کمتری پیدا می کنند و تغییر یک قسمت از سیستم را بدون تأثیرگذاری بر سایرین آسان تر می کند.
- قابلیت نگهداری بهبود یافته : تغییرات در ماژولهای سطح پایین بر ماژولهای سطح بالا تأثیر نمیگذارد و نگهداری و گسترش سیستم را آسانتر میکند.
- قابلیت تست پیشرفته : ماژول های سطح بالا را می توان با استفاده از پیاده سازی های ساختگی ماژول های سطح پایین آزمایش کرد و آزمایش را سریع تر و قابل اطمینان تر کرد.
- افزایش قابلیت استفاده مجدد : ماژول های سطح بالا را می توان در زمینه های مختلف بدون نیاز به تغییر ماژول های سطح پایین مورد استفاده مجدد قرار داد.
نتیجه گیری
در نتیجه، اصول SOLID: مسئولیت منفرد، باز-بسته، جایگزینی Liskov، جداسازی رابط، و وارونگی وابستگی دستورالعملهای ضروری را برای نوشتن کدهای تمیز، قابل نگهداری و مقیاسپذیر در برنامهنویسی شیگرا ارائه میکنند.
با رعایت این اصول، توسعهدهندگان میتوانند سیستمهایی ایجاد کنند که درک، اصلاح و گسترش آن آسانتر باشد و در نهایت منجر به نرمافزار با کیفیت بالاتر و فرآیندهای توسعه کارآمدتر شود.
خلاصه
با تشکر از شما برای خواندن این مقاله! امیدوارم اکنون درک کاملی از اصول SOLID داشته باشید و اینکه چگونه می توانید آنها را برای بهبود پروژه های خود به کار ببرید.
من را دنبال کنید:
– سادیشا نیم سرا