JavaRush /وبلاگ جاوا /Random-FA /پنج اصل اساسی طراحی کلاس (SOLID) در جاوا
Ve4niY
مرحله

پنج اصل اساسی طراحی کلاس (SOLID) در جاوا

در گروه منتشر شد
کلاس ها بلوک هایی هستند که یک برنامه از آنها ساخته می شود. درست مثل آجرهای یک ساختمان. کلاس های بد نوشته شده می تواند روزی مشکلاتی ایجاد کند. پنج اصل اساسی طراحی کلاس (SOLID) در جاوا - 1برای درک اینکه آیا یک کلاس به درستی نوشته شده است، می توانید "استانداردهای کیفیت" را بررسی کنید. در جاوا، اینها به اصطلاح اصول SOLID هستند. بیایید در مورد آنها صحبت کنیم.

اصول SOLID در جاوا

SOLID مخفف است که از حروف بزرگ پنج اصل اول OOP و طراحی تشکیل شده است. این اصول توسط رابرت مارتین در اوایل دهه 2000 ابداع شد و نام اختصاری بعداً توسط مایکل فیرز ابداع شد. در اینجا اصول SOLID شامل موارد زیر است:
  1. اصل مسئولیت واحد
  2. اصل بسته را باز کنید.
  3. اصل جایگزینی لیسکوف.
  4. اصل جداسازی رابط.
  5. اصل وارونگی وابستگی

اصل مسئولیت واحد (SRP)

این اصل بیان می کند که هرگز نباید بیش از یک دلیل برای تغییر یک کلاس وجود داشته باشد. هر شی یک مسئولیت دارد که به طور کامل در یک کلاس محصور شده است. تمام خدمات کلاس با هدف اطمینان از این مسئولیت است. چنین کلاس هایی در صورت لزوم همیشه به راحتی قابل تغییر خواهند بود، زیرا مشخص است که کلاس مسئول چه چیزی است و چه چیزی نیست. یعنی می توان تغییراتی ایجاد کرد و از عواقب آن نترسید - تأثیر بر اشیاء دیگر. و آزمایش چنین کدی بسیار آسان‌تر است، زیرا شما یک عملکرد را با تست‌هایی جدا از بقیه پوشش می‌دهید. ماژولی را تصور کنید که سفارشات را پردازش می کند. اگر سفارش به درستی شکل گرفته باشد، آن را در پایگاه داده ذخیره می کند و یک ایمیل برای تایید سفارش ارسال می کند:
public class OrderProcessor {

    public void process(Order order){
        if (order.isValid() && save(order)) {
            sendConfirmationEmail(order);
        }
    }

    private boolean save(Order order) {
        MySqlConnection connection = new MySqlConnection("database.url");
        // save the order to the database

        return true;
    }

    private void sendConfirmationEmail(Order order) {
        String name = order.getCustomerName();
        String email = order.getCustomerEmail();

        // Sending a letter to the client
    }
}
چنین ماژولی ممکن است به سه دلیل تغییر کند. اولا، منطق پردازش سفارش ممکن است متفاوت باشد، ثانیا، روش ذخیره آن (نوع پایگاه داده)، ثالثا، روش ارسال نامه تایید (به عنوان مثال، به جای ایمیل باید پیامک ارسال کنید). اصل مسئولیت واحد بیانگر این است که سه جنبه این مشکل در واقع سه مسئولیت متفاوت هستند. این بدان معناست که آنها باید در کلاس ها یا ماژول های مختلف باشند. ترکیب چندین موجودیت که ممکن است در زمان‌های مختلف و به دلایل مختلف تغییر کنند، یک تصمیم طراحی بد در نظر گرفته می‌شود. بسیار بهتر است که ماژول را به سه ماژول جداگانه تقسیم کنید که هر کدام یک عملکرد واحد را انجام می دهند:
public class MySQLOrderRepository {
    public boolean save(Order order) {
        MySqlConnection connection = new MySqlConnection("database.url");
        // save the order to the database

        return true;
    }
}

public class ConfirmationEmailSender {
    public void sendConfirmationEmail(Order order) {
        String name = order.getCustomerName();
        String email = order.getCustomerEmail();

        // Sending a letter to the client
    }
}

public class OrderProcessor {
    public void process(Order order){

        MySQLOrderRepository repository = new MySQLOrderRepository();
        ConfirmationEmailSender mailSender = new ConfirmationEmailSender();

        if (order.isValid() && repository.save(order)) {
            mailSender.sendConfirmationEmail(order);
        }
    }

}

اصل باز/بسته (OCP)

این اصل به طور خلاصه به شرح زیر است: موجودیت های نرم افزار (کلاس ها، ماژول ها، توابع و غیره) باید برای گسترش باز باشند، اما برای تغییر بسته باشند . این بدان معناست که باید بتوان رفتار خارجی یک کلاس را بدون ایجاد تغییرات فیزیکی در خود کلاس تغییر داد. با پیروی از این اصل، کلاس ها به گونه ای توسعه می یابند که برای تنظیم کلاس با شرایط خاص برنامه، کافی است آن را گسترش داده و برخی از توابع را دوباره تعریف کنیم. بنابراین، سیستم باید انعطاف پذیر باشد و بتواند تحت شرایط متغیر بدون تغییر کد منبع کار کند. در ادامه مثال سفارش، فرض کنید باید قبل از پردازش سفارش و پس از ارسال ایمیل تأیید، اقداماتی را انجام دهیم. به جای تغییر خود کلاس OrderProcessor، آن را گسترش می دهیم و بدون نقض اصل OCP به راه حلی برای مشکل در دست خواهیم رسید:
public class OrderProcessorWithPreAndPostProcessing extends OrderProcessor {

    @Override
    public void process(Order order) {
        beforeProcessing();
        super.process(order);
        afterProcessing();
    }

    private void beforeProcessing() {
        // Perform some actions before processing the order
    }

    private void afterProcessing() {
        // Perform some actions after order processing
    }
}

اصل تعویض باربارا لیسکوف (LSP)

این یک تغییر از اصل باز/بسته است که قبلاً بحث شد. می توان آن را به صورت زیر توصیف کرد: اشیاء در یک برنامه را می توان بدون تغییر ویژگی های برنامه توسط وارثان خود جایگزین کرد. این به این معنی است که کلاسی که با گسترش یک کلاس پایه توسعه می‌یابد، باید روش‌های خود را به‌گونه‌ای لغو کند که عملکرد را از نظر مشتری خراب نکند. یعنی اگر یک توسعه دهنده کلاس شما را گسترش دهد و از آن در یک برنامه کاربردی استفاده کند، نباید رفتار مورد انتظار متدهای نادیده گرفته شده را تغییر دهد. کلاس‌های فرعی باید روش‌های کلاس پایه را به گونه‌ای نادیده بگیرند که عملکرد را از نظر مشتری خراب نکنند. با استفاده از مثال زیر می توان این موضوع را به تفصیل بررسی کرد. بیایید فرض کنیم کلاسی داریم که مسئول اعتبار سنجی سفارش است و بررسی می کند که آیا همه اقلام سفارش موجود هستند یا خیر. این کلاس متدی دارد isValidکه true یا false را برمی گرداند :
public class OrderStockValidator {

    public boolean isValid(Order order) {
        for (Item item : order.getItems()) {
            if (! item.isInStock()) {
                return false;
            }
        }

        return true;
    }
}
بیایید همچنین فرض کنیم که برخی از سفارش‌ها باید به گونه‌ای دیگر اعتبارسنجی شوند: بررسی کنید که آیا همه کالاهای سفارش موجود هستند و آیا همه کالاها بسته‌بندی شده‌اند. برای انجام این کار، کلاس را OrderStockValidatorبا کلاس گسترش دادیم OrderStockAndPackValidator:
public class OrderStockAndPackValidator extends OrderStockValidator {

    @Override
    public boolean isValid(Order order) {
        for (Item item : order.getItems()) {
            if ( !item.isInStock() || !item.isPacked() ){
                throw new IllegalStateException(
                     String.format("Order %d is not valid!", order.getId())
                );
            }
        }

        return true;
    }
}
با این حال، در این کلاس، ما اصل LSP را نقض کردیم، زیرا به جای اینکه اگر دستور اعتبارسنجی را تأیید نکرد، به جای اینکه false راIllegalStateException برگردانیم، روش ما یک استثنا ایجاد می کند . مشتریان این کد انتظار این را ندارند: آنها انتظار دارند درست یا نادرست برگردانده شود . این ممکن است منجر به خطا در برنامه شود.

اصل تقسیم رابط (ISP)

با عبارت زیر مشخص می شود: مشتریان نباید مجبور به پیاده سازی روش هایی شوند که از آنها استفاده نمی کنند . اصل جداسازی رابط نشان می دهد که رابط هایی که بیش از حد "ضخیم" هستند باید به موارد کوچکتر و خاص تر تقسیم شوند، به طوری که مشتریان رابط های کوچک فقط در مورد روش های لازم برای کار خود بدانند. در نتیجه، هنگام تغییر یک روش رابط، کلاینت هایی که از این روش استفاده نمی کنند، نباید تغییر کنند. بیایید به یک مثال نگاه کنیم. توسعه دهنده Alex رابط "گزارش" را ایجاد کرد و دو روش اضافه کرد: generateExcel()و generatedPdf(). اکنون Client A می خواهد از این رابط استفاده کند، اما او فقط قصد دارد از گزارش های PDF استفاده کند و نه Excel. آیا او از این عملکرد راضی خواهد بود؟ خیر او باید دو روش را اجرا کند که یکی از آنها تا حد زیادی غیر ضروری است و تنها به لطف الکس، طراح نرم افزار وجود دارد. کلاینت یا از رابط دیگری استفاده می کند یا فیلد اکسل را خالی می گذارد. پس راه حل چیست؟ این شامل تقسیم رابط موجود به دو رابط کوچکتر است. یکی گزارشی با فرمت PDF، دومی گزارشی با فرمت اکسل. این به کاربر این فرصت را می دهد که فقط از قابلیت های لازم برای خود استفاده کند.

اصل وارونگی وابستگی (DIP)

این اصل SOLID در جاوا به شرح زیر است: وابستگی های درون سیستم بر اساس انتزاعات ساخته می شوند . ماژول های سطح بالا مستقل از ماژول های سطح پایین هستند. انتزاع ها نباید به جزئیات بستگی داشته باشند. جزئیات باید به انتزاعات بستگی داشته باشد. نرم افزار باید طوری طراحی شود که ماژول های مختلف مستقل باشند و با استفاده از انتزاع به یکدیگر متصل شوند. یک کاربرد کلاسیک این اصل چارچوب Spring است. در چارچوب Spring، همه ماژول ها به عنوان اجزای مجزا اجرا می شوند که می توانند با هم کار کنند. آنها به قدری مستقل هستند که می توانند به راحتی در ماژول های نرم افزاری دیگر علاوه بر چارچوب Spring نیز استفاده شوند. این امر از طریق وابستگی به اصول بسته و باز به دست می آید. همه ماژول ها فقط به یک انتزاع دسترسی دارند که می تواند در ماژول دیگری استفاده شود. بیایید سعی کنیم این را با یک مثال نشان دهیم. در مورد اصل مسئولیت انحصاری صحبت کردیم، برخی از آنها را در نظر گرفتیم OrderProcessor. بیایید نگاهی دیگر به کد این کلاس بیندازیم:
public class OrderProcessor {
    public void process(Order order){

        MySQLOrderRepository repository = new MySQLOrderRepository();
        ConfirmationEmailSender mailSender = new ConfirmationEmailSender();

        if (order.isValid() && repository.save(order)) {
            mailSender.sendConfirmationEmail(order);
        }
    }

}
در این مثال، کلاس ما OrderProcessorبه دو کلاس خاص MySQLOrderRepositoryو ConfirmationEmailSender. ما همچنین کد این کلاس ها را ارائه می دهیم:
public class MySQLOrderRepository {
    public boolean save(Order order) {
        MySqlConnection connection = new MySqlConnection("database.url");
        // save the order to the database

        return true;
    }
}

public class ConfirmationEmailSender {
    public void sendConfirmationEmail(Order order) {
        String name = order.getCustomerName();
        String email = order.getCustomerEmail();

        // Sending a letter to the client
    }
}
این طبقات به دور از انتزاع نامیده می شوند. و از نقطه نظر اصل DIP، درست تر است که با ایجاد برخی انتزاعات شروع کنیم که به ما امکان می دهد در آینده با آنها کار کنیم، نه با پیاده سازی های خاص. بیایید دو واسط MailSenderو ایجاد کنیم OrderRepositoryکه به انتزاعات ما تبدیل می شوند:
public interface MailSender {
    void sendConfirmationEmail(Order order);
}

public interface OrderRepository {
    boolean save(Order order);
}
حالا بیایید این رابط ها را در کلاس هایی که از قبل برای این کار آماده هستند پیاده سازی کنیم:
public class ConfirmationEmailSender implements MailSender {

    @Override
    public void sendConfirmationEmail(Order order) {
        String name = order.getCustomerName();
        String email = order.getCustomerEmail();

        // Sending a letter to the client
    }

}

public class MySQLOrderRepository implements OrderRepository {

    @Override
    public boolean save(Order order) {
        MySqlConnection connection = new MySqlConnection("database.url");
        // save the order to the database

        return true;
    }
}
ما کارهای مقدماتی را انجام داده‌ایم تا کلاس ما OrderProcessorنه به جزئیات عینی، بلکه به انتزاعات وابسته باشد. بیایید با معرفی وابستگی های خود در سازنده کلاس، تغییراتی در آن ایجاد کنیم:
public class OrderProcessor {

    private MailSender mailSender;
    private OrderRepository repository;

    public OrderProcessor(MailSender mailSender, OrderRepository repository) {
        this.mailSender = mailSender;
        this.repository = repository;
    }

    public void process(Order order){
        if (order.isValid() && repository.save(order)) {
            mailSender.sendConfirmationEmail(order);
        }
    }
}
کلاس ما اکنون به انتزاعات بستگی دارد تا پیاده سازی های عینی. شما به راحتی می توانید رفتار آن را با تزریق وابستگی مورد نظر در زمان ایجاد نمونه تغییر دهید OrderProcessor. ما به اصول طراحی SOLID در جاوا نگاه کردیم. بیشتر در مورد OOP به طور کلی، اصول اولیه این زبان برنامه نویسی - خسته کننده نیست و با صدها ساعت تمرین - در دوره JavaRush. زمان حل برخی مشکلات است :) پنج اصل اساسی طراحی کلاس (SOLID) در جاوا - 2
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION