CodeGym /Java Blog /এলোমেলো /সলিড: জাভাতে ক্লাস ডিজাইনের পাঁচটি মৌলিক নীতি
John Squirrels
লেভেল 41
San Francisco

সলিড: জাভাতে ক্লাস ডিজাইনের পাঁচটি মৌলিক নীতি

এলোমেলো দলে প্রকাশিত
ক্লাস হল অ্যাপ্লিকেশনের বিল্ডিং ব্লক। ঠিক যেন বিল্ডিংয়ে ইট। খারাপভাবে লিখিত ক্লাস অবশেষে সমস্যার কারণ হতে পারে। সলিড: জাভাতে ক্লাস ডিজাইনের পাঁচটি মৌলিক নীতি - 1একটি ক্লাস সঠিকভাবে লেখা হয়েছে কিনা তা বোঝার জন্য, আপনি পরীক্ষা করতে পারেন কিভাবে এটি "গুণমান মান" পর্যন্ত পরিমাপ করে। জাভাতে, এগুলি তথাকথিত সলিড নীতি, এবং আমরা সেগুলি সম্পর্কে কথা বলতে যাচ্ছি।

জাভাতে সলিড নীতি

SOLID হল OOP এবং ক্লাস ডিজাইনের প্রথম পাঁচটি নীতির ক্যাপিটাল অক্ষর থেকে গঠিত একটি সংক্ষিপ্ত রূপ। নীতিগুলি 2000-এর দশকের গোড়ার দিকে রবার্ট মার্টিন দ্বারা প্রকাশ করা হয়েছিল, এবং তারপরে সংক্ষেপণটি মাইকেল ফেদারস দ্বারা প্রবর্তিত হয়েছিল। এখানে সলিড নীতিগুলি রয়েছে:
  1. একক দায়িত্ব নীতি
  2. খোলা বন্ধ নীতি
  3. লিসকভ প্রতিস্থাপন নীতি
  4. ইন্টারফেস বিভাজন নীতি
  5. নির্ভরতা বিপরীত নীতি

একক দায়িত্ব নীতি (এসআরপি)

এই নীতিটি বলে যে একটি ক্লাস পরিবর্তন করার জন্য একাধিক কারণ থাকা উচিত নয়। প্রতিটি বস্তুর একটি দায়িত্ব আছে, যা ক্লাসে সম্পূর্ণরূপে আবদ্ধ। একটি শ্রেণীর সমস্ত পরিষেবা এই দায়িত্বকে সমর্থন করার লক্ষ্যে। এই ধরনের ক্লাস সবসময় প্রয়োজন হলে পরিবর্তন করা সহজ হবে, কারণ এটা পরিষ্কার যে ক্লাস কি এবং এর জন্য দায়ী নয়। অন্য কথায়, আমরা পরিবর্তন করতে সক্ষম হব এবং পরিণতি সম্পর্কে ভয় পাব না, অর্থাৎ অন্যান্য বস্তুর উপর প্রভাব। উপরন্তু, এই জাতীয় কোড পরীক্ষা করা অনেক সহজ, কারণ আপনার পরীক্ষাগুলি অন্য সমস্ত থেকে বিচ্ছিন্নভাবে কার্যকারিতার একটি অংশকে কভার করছে। একটি মডিউল কল্পনা করুন যা অর্ডার প্রক্রিয়া করে। যদি একটি অর্ডার সঠিকভাবে গঠিত হয়, এই মডিউল এটি একটি ডাটাবেসে সংরক্ষণ করে এবং অর্ডার নিশ্চিত করতে একটি ইমেল পাঠায়:

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 in the database

        return true;
    }

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

        // Send an email to the customer
    }
}
এই মডিউলটি তিনটি কারণে পরিবর্তন হতে পারে। প্রথমত, অর্ডার প্রক্রিয়াকরণের যুক্তি পরিবর্তিত হতে পারে। দ্বিতীয়ত, অর্ডার যেভাবে সংরক্ষণ করা হয় (ডাটাবেস প্রকার) পরিবর্তন হতে পারে। তৃতীয়ত, নিশ্চিতকরণ পাঠানোর উপায় পরিবর্তিত হতে পারে (উদাহরণস্বরূপ, ধরুন আমাদের একটি ইমেলের পরিবর্তে একটি পাঠ্য বার্তা পাঠাতে হবে)। একক দায়িত্ব নীতিটি বোঝায় যে এই সমস্যার তিনটি দিক আসলে তিনটি ভিন্ন দায়িত্ব। এর মানে তাদের বিভিন্ন ক্লাস বা মডিউলে থাকা উচিত। বিভিন্ন সময়ে এবং বিভিন্ন কারণে পরিবর্তিত হতে পারে এমন বেশ কয়েকটি সত্ত্বাকে একত্রিত করা একটি দুর্বল ডিজাইনের সিদ্ধান্ত হিসাবে বিবেচিত হয়। একটি মডিউলকে তিনটি পৃথক মডিউলে বিভক্ত করা অনেক ভালো, যার প্রতিটি একটি একক ফাংশন সম্পাদন করে:

public class MySQLOrderRepository {
    public boolean save(Order order) {
        MySqlConnection connection = new MySqlConnection("database.url");
        // Save the order in the database

        return true;
    }
}

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

        // Send an email to the customer
    }
}

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শ্রেণী নিজেই, আমরা খোলা বন্ধ নীতি লঙ্ঘন না করে আমাদের উদ্দেশ্য অর্জনের জন্য এটিকে প্রসারিত করব:

public class OrderProcessorWithPreAndPostProcessing extends OrderProcessor {

    @Override
    public void process(Order order) {
        beforeProcessing();
        super.process(order);
        afterProcessing();
    }
    
    private void beforeProcessing() {
        // Take some action before processing the order
    }
    
    private void afterProcessing() {
        // Take some action after processing the order
    }
}

লিসকভ প্রতিস্থাপন নীতি (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;
    }
}
কিন্তু এখানে আমরা লিসকভ প্রতিস্থাপন নীতি লঙ্ঘন করেছি, কারণ অর্ডারটি বৈধতা ব্যর্থ হলে মিথ্যাIllegalStateException ফেরত দেওয়ার পরিবর্তে, আমাদের পদ্ধতিটি একটি নিক্ষেপ করে । এই কোড ব্যবহারকারী ক্লায়েন্টরা এটি আশা করেন না: তারা সত্য বা মিথ্যার একটি রিটার্ন মান আশা করে । এটি রানটাইম ত্রুটি হতে পারে.

ইন্টারফেস সেগ্রিগেশন প্রিন্সিপল (ISP)

এই নীতিটি নিম্নলিখিত বিবৃতি দ্বারা চিহ্নিত করা হয়: ক্লায়েন্টকে এমন পদ্ধতি প্রয়োগ করতে বাধ্য করা উচিত নয় যা তারা ব্যবহার করবে না । ইন্টারফেস বিভাজন নীতির অর্থ হল যে ইন্টারফেসগুলি যেগুলি খুব "পুরু" সেগুলিকে অবশ্যই ছোট, আরও নির্দিষ্টগুলিতে বিভক্ত করতে হবে, যাতে ছোট ইন্টারফেস ব্যবহারকারী ক্লায়েন্টরা তাদের কাজের জন্য প্রয়োজনীয় পদ্ধতিগুলি সম্পর্কেই জানেন। ফলস্বরূপ, যখন একটি ইন্টারফেস পদ্ধতি পরিবর্তন হয়, যে কোনো ক্লায়েন্ট যে পদ্ধতিটি ব্যবহার করে না তাদের পরিবর্তন করা উচিত নয়। এই উদাহরণটি বিবেচনা করুন: অ্যালেক্স, একজন বিকাশকারী, একটি "রিপোর্ট" ইন্টারফেস তৈরি করেছেন এবং দুটি পদ্ধতি যোগ করেছেন: generateExcel()এবংgeneratedPdf(). এখন একজন ক্লায়েন্ট এই ইন্টারফেসটি ব্যবহার করতে চায়, কিন্তু শুধুমাত্র পিডিএফ ফরম্যাটে রিপোর্ট ব্যবহার করতে চায়, এক্সেলে নয়। এই কার্যকারিতা কি এই ক্লায়েন্টকে সন্তুষ্ট করবে? না। ক্লায়েন্টকে দুটি পদ্ধতি প্রয়োগ করতে হবে, যার মধ্যে একটির অনেকাংশে প্রয়োজন নেই এবং বিদ্যমান শুধুমাত্র অ্যালেক্সকে ধন্যবাদ, যিনি সফ্টওয়্যারটি ডিজাইন করেছেন। ক্লায়েন্ট হয় একটি ভিন্ন ইন্টারফেস ব্যবহার করবে বা এক্সেল রিপোর্টের পদ্ধতির সাথে কিছুই করবে না। তাহলে সমাধান কি? এটি বিদ্যমান ইন্টারফেসটিকে দুটি ছোটে বিভক্ত করা। একটি পিডিএফ রিপোর্টের জন্য, অন্যটি এক্সেল রিপোর্টের জন্য। এটি ক্লায়েন্টদের শুধুমাত্র তাদের প্রয়োজনীয় কার্যকারিতা ব্যবহার করতে দেয়।

নির্ভরতা বিপরীত নীতি (DIP)

জাভাতে, এই সলিড নীতিটি নিম্নরূপ বর্ণনা করা হয়েছে: সিস্টেমের মধ্যে নির্ভরতাগুলি বিমূর্ততার উপর ভিত্তি করে তৈরি করা হয়. উচ্চ-স্তরের মডিউলগুলি নিম্ন-স্তরের মডিউলগুলির উপর নির্ভর করে না। বিমূর্ত বিবরণের উপর নির্ভর করা উচিত নয়। বিবরণ বিমূর্ততা উপর নির্ভর করা উচিত. সফ্টওয়্যার এমনভাবে ডিজাইন করা দরকার যাতে বিভিন্ন মডিউল স্বয়ংসম্পূর্ণ থাকে এবং বিমূর্ততার মাধ্যমে একে অপরের সাথে সংযুক্ত থাকে। এই নীতির একটি ক্লাসিক প্রয়োগ হল স্প্রিং ফ্রেমওয়ার্ক। স্প্রিং ফ্রেমওয়ার্কে, সমস্ত মডিউল পৃথক উপাদান হিসাবে প্রয়োগ করা হয় যা একসাথে কাজ করতে পারে। এগুলি এতটাই স্বায়ত্তশাসিত যে তারা স্প্রিং ফ্রেমওয়ার্ক ছাড়া অন্য প্রোগ্রাম মডিউলগুলিতে খুব সহজে ব্যবহার করা যেতে পারে। এটি বন্ধ এবং খোলা নীতির নির্ভরতার জন্য ধন্যবাদ অর্জন করা হয়। সমস্ত মডিউলগুলি শুধুমাত্র বিমূর্ততায় অ্যাক্সেস প্রদান করে, যা অন্য মডিউলে ব্যবহার করা যেতে পারে। আসুন একটি উদাহরণ ব্যবহার করে এটি ব্যাখ্যা করার চেষ্টা করি। একক দায়িত্ব নীতি বলতে, আমরা বিবেচনা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 in the database

        return true;
    }
}

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

        // Send an email to the customer
    }
}
এই ক্লাসগুলি আমরা যাকে বিমূর্ততা বলব তা থেকে অনেক দূরে। এবং নির্ভরতা বিপরীত নীতির দৃষ্টিকোণ থেকে, নির্দিষ্ট বাস্তবায়নের পরিবর্তে কিছু বিমূর্ততা তৈরি করে শুরু করা ভাল হবে যা আমরা ভবিষ্যতে কাজ করতে পারি। আসুন দুটি ইন্টারফেস তৈরি করি: 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();

        // Send an email to the customer
    }

}

public class MySQLOrderRepository implements OrderRepository {

    @Override
    public boolean save(Order order) {
        MySqlConnection connection = new MySqlConnection("database.url");
        // Save the order in 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কোনো বস্তু তৈরি হওয়ার সময় কাঙ্খিত নির্ভরতা যোগ করে আমরা সহজেই এর আচরণ পরিবর্তন করতে পারি । আমরা জাভাতে সলিড ডিজাইনের নীতিগুলি পরীক্ষা করেছি। আপনি CodeGym কোর্সে সাধারণভাবে OOP এবং জাভা প্রোগ্রামিং-এর মূল বিষয়গুলি — বিরক্তিকর কিছু নয় এবং কয়েকশো ঘণ্টার অনুশীলন — সম্পর্কে আরও শিখবেন। কিছু কাজ সমাধান করার সময় :)
মন্তব্য
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION