JavaRush /مدونة جافا /Random-AR /خمسة مبادئ أساسية لتصميم الفصل (SOLID) في Java
Ve4niY
مستوى

خمسة مبادئ أساسية لتصميم الفصل (SOLID) في Java

نشرت في المجموعة
الفئات هي الكتل التي يتم إنشاء التطبيق منها. تماما مثل الطوب في المبنى. الفصول المكتوبة بشكل سيء يمكن أن تسبب مشاكل في يوم من الأيام. خمسة مبادئ أساسية لتصميم الفصل (SOLID) في Java - 1لفهم ما إذا كان الفصل مكتوبًا بشكل صحيح، يمكنك التحقق من "معايير الجودة". في Java، هذه هي ما يسمى بالمبادئ الصلبة. دعونا نتحدث عنهم.

المبادئ الصلبة في جافا

SOLID هو اختصار يتكون من الحروف الكبيرة للمبادئ الخمسة الأولى لـ OOP والتصميم. اخترع روبرت مارتن هذه المبادئ في أوائل العقد الأول من القرن الحادي والعشرين، وصاغ مايكل فيذرز الاختصار لاحقًا. إليك ما تتضمنه مبادئ 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تُرجع صوابًا أو خطأً :
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، لأنه بدلاً من إرجاع خطأ إذا لم ينجح الأمر في التحقق من الصحة، فإن طريقتنا تطرح استثناءً IllegalStateException. لا يتوقع عملاء هذا الرمز هذا: فهم يتوقعون إرجاع صحيح أو خطأ . وهذا قد يؤدي إلى أخطاء في البرنامج.

مبدأ تقسيم الواجهة (ISP)

تتميز بالبيان التالي: لا ينبغي إجبار العملاء على تنفيذ أساليب لن يستخدموها . يشير مبدأ فصل الواجهة إلى أن الواجهات "السميكة" للغاية تحتاج إلى تقسيمها إلى واجهات أصغر وأكثر تحديدًا، بحيث يعرف عملاء الواجهات الصغيرة فقط الطرق اللازمة لعملهم. ونتيجة لذلك، عند تغيير أسلوب واجهة، يجب ألا يتغير العملاء الذين لا يستخدمون هذا الأسلوب. لنلقي نظرة على مثال. أنشأ المطور Alex واجهة "التقرير" وأضاف طريقتين: generateExcel()و generatedPdf(). الآن يريد العميل "أ" استخدام هذه الواجهة، لكنه ينوي فقط استخدام تقارير PDF وليس Excel. فهل سيكون راضيا عن هذه الوظيفة؟ لا. سيتعين عليه تنفيذ طريقتين، إحداهما غير ضرورية إلى حد كبير ولا توجد إلا بفضل مصمم البرمجيات Alex. سيقوم العميل إما باستخدام واجهة مختلفة أو ترك حقل Excel فارغًا. إذن ما هو الحل؟ وهو يتألف من تقسيم الواجهة الحالية إلى واجهتين أصغر. الأول عبارة عن تقرير بتنسيق PDF، والثاني تقرير بتنسيق Excel. سيتيح هذا للمستخدم الفرصة لاستخدام الوظيفة الضرورية له فقط.

مبدأ انعكاس التبعية (DIP)

يتم وصف مبدأ SOLID هذا في Java على النحو التالي: يتم بناء التبعيات داخل النظام على أساس التجريدات . وحدات المستوى الأعلى مستقلة عن وحدات المستوى الأدنى. لا ينبغي أن تعتمد التجريدات على التفاصيل. التفاصيل يجب أن تعتمد على التجريدات. يجب تصميم البرنامج بحيث تكون الوحدات المختلفة مستقلة وتتصل ببعضها البعض باستخدام التجريد. التطبيق الكلاسيكي لهذا المبدأ هو إطار الربيع. في إطار عمل 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 - مبادئ التصميم في Java. المزيد عن OOP بشكل عام، أساسيات لغة البرمجة هذه - ليست مملة ومع مئات الساعات من التدريب - في دورة JavaRush. حان الوقت لحل بعض المشاكل :) خمسة مبادئ أساسية لتصميم الفصل (SOLID) في Java - 2
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION