সময়ের সাথে সাথে সমস্ত সফ্টওয়্যার অ্যাপ্লিকেশন পরিবর্তিত হয়। সফ্টওয়্যারে করা পরিবর্তনগুলি ক্যাসকেডিং সমস্যা সৃষ্টি করতে পারে যা অপ্রত্যাশিত। যাইহোক, পরিবর্তন অনিবার্য, কারণ আমরা এমন সফ্টওয়্যার তৈরি করতে পারি না যা পরিবর্তন হয় না। সফ্টওয়্যার বৃদ্ধির সাথে সাথে সফ্টওয়্যার প্রয়োজনীয়তা পরিবর্তিত হতে থাকে। আমরা যা করতে পারি তা হল সফ্টওয়্যারকে এমনভাবে ডিজাইন করা যাতে এটি পরিবর্তনের জন্য স্থিতিস্থাপক হয়। সঠিকভাবে সফ্টওয়্যার ডিজাইন করা শুরুতে সময় এবং প্রচেষ্টা নিতে পারে, তবে দীর্ঘমেয়াদে, এটি সময় এবং শ্রম সাশ্রয় করে। শক্তভাবে সংযুক্ত সফ্টওয়্যারটি ভঙ্গুর, এবং পরিবর্তনের সাথে কী ঘটবে তা আমরা ভবিষ্যদ্বাণী করতে পারি না। এখানে খারাপভাবে ডিজাইন করা সফ্টওয়্যারের কিছু প্রভাব রয়েছে:
- এটি অচলতা সৃষ্টি করে।
- কোড পরিবর্তন করা ব্যয়বহুল।
- সফ্টওয়্যারটিকে সহজ করার চেয়ে আরও জটিলতা যোগ করা সহজ৷ ৷
- কোডটি নিয়ন্ত্রণের অযোগ্য।
- একজন ডেভেলপারের কার্যকারিতা কিভাবে কাজ করে তা বের করতে অনেক সময় লাগে।
- সফ্টওয়্যারের একটি অংশ পরিবর্তন করলে অন্য অংশটি ভেঙে যায় এবং আমরা ভবিষ্যদ্বাণী করতে পারি না যে একটি পরিবর্তন কী সমস্যা আনতে পারে।
কাগজের ডিজাইন প্রিন্সিপলস এবং ডিজাইন প্যাটার্নস, সফ্টওয়্যার পচনের নিম্নলিখিত লক্ষণগুলি তালিকাভুক্ত করে:
- কঠোরতা:সমস্যা সৃষ্টি না করে কোড পরিবর্তন করা খুবই কঠিন, কারণ একটি অংশে পরিবর্তন করলে কোডের অন্যান্য অংশে পরিবর্তন করার প্রয়োজন হয়।
- ভঙ্গুরতা:কোড পরিবর্তন করলে সাধারণত সফ্টওয়্যারের আচরণ ভেঙে যায়। এটি এমন অংশগুলিও ভেঙে ফেলতে পারে যা সরাসরি পরিবর্তনের সাথে সম্পর্কিত নয়৷
- অচলতা:যদিও একটি সফ্টওয়্যার অ্যাপ্লিকেশনের কিছু অংশে অনুরূপ আচরণ থাকতে পারে, আমরা কোডটি পুনরায় ব্যবহার করতে অক্ষম এবং সেগুলিকে অবশ্যই নকল করতে হবে৷
- সান্দ্রতা:যখন সফ্টওয়্যারটি পরিবর্তন করা কঠিন, তখন আমরা সফ্টওয়্যারটিকে আরও উন্নত করার পরিবর্তে জটিলতা যোগ করতে থাকি৷
এমনভাবে সফ্টওয়্যার ডিজাইন করা প্রয়োজন যাতে পরিবর্তনগুলি নিয়ন্ত্রণ করা যায় এবং অনুমান করা যায়৷
সলিড ডিজাইন নীতিগুলি সফ্টওয়্যার প্রোগ্রামগুলিকে ডিকপলিং করে এই সমস্যাগুলি সমাধান করতে সহায়তা করে। রবার্ট সি. মার্টিন তার ডিজাইন প্রিন্সিপলস অ্যান্ড ডিজাইন প্যাটার্নস শিরোনামের গবেষণাপত্রে এই ধারণাগুলি প্রবর্তন করেছিলেন এবং মাইকেল ফেদারস পরে সংক্ষিপ্ত রূপ নিয়ে আসেন।
সলিড ডিজাইন নীতিতে এই পাঁচটি নীতি অন্তর্ভুক্ত রয়েছে:
- S একক দায়িত্ব নীতি
- ও কলম/বন্ধ নীতি
- L ইসকভ প্রতিস্থাপন নীতি
- আমি ইন্টারফেস সেগ্রিগেশন নীতি
- D ependency inversion Principle
রুবিতে কীভাবে এই নীতিগুলি ভাল-ডিজাইন করা সফ্টওয়্যার তৈরি করতে সাহায্য করতে পারে তা বোঝার জন্য আমরা তাদের প্রত্যেককে অন্বেষণ করব৷
একক দায়িত্বের নীতি - SRP
আসুন আমরা বলি যে এইচআর ম্যানেজমেন্ট সফ্টওয়্যারের জন্য, আমাদের ব্যবহারকারী তৈরি করতে, একজন কর্মচারীর বেতন যোগ করতে এবং একজন কর্মচারীর পে-স্লিপ তৈরি করার কার্যকারিতা প্রয়োজন। এটি তৈরি করার সময়, আমরা এই কার্যকারিতাগুলিকে একটি একক শ্রেণিতে যুক্ত করতে পারি, তবে এই পদ্ধতির এই কার্যকারিতাগুলির মধ্যে অবাঞ্ছিত নির্ভরতা সৃষ্টি করে। আমরা যখন শুরু করি তখন এটি সহজ, কিন্তু যখন জিনিসগুলি পরিবর্তিত হয় এবং নতুন প্রয়োজনীয়তা দেখা দেয়, তখন আমরা ভবিষ্যদ্বাণী করতে পারি না যে পরিবর্তনটি কোন কার্যকারিতা ভেঙে ফেলবে৷
একটি ক্লাসে একটি থাকা উচিত এবং পরিবর্তনের একমাত্র কারণ - রবার্ট সি মার্টিন
এখানে একটি নমুনা কোড যেখানে সমস্ত কার্যকারিতা একটি একক শ্রেণিতে রয়েছে:
class User
def initialize(employee, month)
@employee = employee
@month = month
end
def generate_payslip
# Code to read from database,
# generate payslip
# and write it to a file
self.send_email
end
def send_email
# code to send email
employee.email
month
end
end
একটি পেস্লিপ তৈরি করতে এবং ব্যবহারকারীকে পাঠাতে, আমরা ক্লাস শুরু করতে পারি এবং জেনারেট পেস্লিপ পদ্ধতিতে কল করতে পারি:
month = 11
user = User.new(employee, month)
user.generate_payslip
এখন, একটি নতুন প্রয়োজন আছে. আমরা পেস্লিপ তৈরি করতে চাই কিন্তু ইমেল পাঠাতে চাই না। আমাদের বিদ্যমান কার্যকারিতা যেমন আছে তেমনি রাখতে হবে এবং একটি ইমেল না পাঠিয়ে অভ্যন্তরীণ প্রতিবেদনের জন্য একটি নতুন পেস্লিপ জেনারেটর যুক্ত করতে হবে, কারণ এটি অভ্যন্তরীণ প্রস্তাবের জন্য। এই পর্যায়ে, আমরা নিশ্চিত করতে চাই যে কর্মীদের পাঠানো বিদ্যমান পে-স্লিপ কার্যকর থাকবে।
এই প্রয়োজনের জন্য, আমরা বিদ্যমান কোডটি পুনরায় ব্যবহার করতে পারি না। আমাদের হয় generate_payslip পদ্ধতিতে একটি পতাকা যুক্ত করতে হবে যাতে বলা হয় যদি সত্য ইমেল পাঠান অন্যথায় না। এটি করা যেতে পারে, কিন্তু যেহেতু এটি বিদ্যমান কোড পরিবর্তন করে, এটি প্রস্থান কার্যকারিতা ভঙ্গ করতে পারে৷
আমরা জিনিসগুলিকে ভেঙে ফেলি না তা নিশ্চিত করার জন্য, আমাদের এই লজিকগুলিকে আলাদা শ্রেণীতে ভাগ করতে হবে:
class PayslipGenerator
def initialize(employee, month)
@employee = employee
@month = month
end
def generate_payslip
# Code to read from database,
# generate payslip
# and write it to a file
end
end
class PayslipMailer
def initialize(employee)
@employee = employee
end
def send_mail
# code to send email
employee.email
month
end
end
এর পরে, আমরা এই দুটি ক্লাস শুরু করতে পারি এবং তাদের পদ্ধতিগুলিকে কল করতে পারি:
month = 11
# General Payslip
generator = PayslipGenerator.new(employee, month)
generator.generate_payslip
# Send Email
mailer = PayslipMailer.new(employee, month)
mailer.send_mail
এই পদ্ধতিটি দায়িত্বগুলিকে দ্বিগুণ করতে সাহায্য করে এবং একটি অনুমানযোগ্য পরিবর্তন নিশ্চিত করে। যদি আমাদের শুধুমাত্র মেইলারের কার্যকারিতা পরিবর্তন করতে হয়, আমরা প্রতিবেদন তৈরির পরিবর্তন না করেই তা করতে পারি। এটি কার্যকারিতার কোনো পরিবর্তনের পূর্বাভাস দিতেও সাহায্য করে।
ধরুন আমাদের ইমেলে মাসের ফিল্ডের ফর্ম্যাট পরিবর্তন করে Nov
করতে হবে 11
এর পরিবর্তে . এই ক্ষেত্রে, আমরা PayslipMailer ক্লাসটি সংশোধন করব এবং এটি নিশ্চিত করবে যে PayslipGenerator কার্যকারিতার মধ্যে কিছুই পরিবর্তন বা ভাঙবে না।
প্রতিবার আপনি কোডের একটি অংশ লিখলে, পরে একটি প্রশ্ন জিজ্ঞাসা করুন। এই শ্রেণীর দায়িত্ব কি? যদি আপনার উত্তরে "এবং" থাকে, তাহলে ক্লাসটিকে একাধিক শ্রেণীতে ভাগ করুন। ছোট ক্লাস সবসময় বড়, জেনেরিক ক্লাসের চেয়ে ভালো।
খোলা/বন্ধ নীতি - OCP
বার্ট্রান্ড মেয়ার তার অবজেক্ট-ওরিয়েন্টেড সফ্টওয়্যার নির্মাণ শিরোনামের বইতে খোলা/বন্ধ নীতির উদ্ভব করেছিলেন।
নীতিটি বলে, "সফ্টওয়্যার সত্ত্বা (ক্লাস, মডিউল, ফাংশন, ইত্যাদি) এক্সটেনশনের জন্য খোলা থাকা উচিত কিন্তু পরিবর্তনের জন্য বন্ধ করা উচিত ". এর মানে হল যে সত্তা পরিবর্তন না করেই আমাদের আচরণ পরিবর্তন করতে সক্ষম হওয়া উচিত৷
৷উপরের উদাহরণে, আমাদের কাছে একজন কর্মচারীর জন্য পে-স্লিপ পাঠানোর কার্যকারিতা রয়েছে, তবে এটি সমস্ত কর্মচারীদের জন্য খুবই সাধারণ। যাইহোক, একটি নতুন প্রয়োজনীয়তা দেখা দিয়েছে:কর্মচারীর প্রকারের উপর ভিত্তি করে একটি পেস্লিপ তৈরি করুন৷ আমাদের ফুল-টাইম কর্মচারী এবং ঠিকাদারদের জন্য বিভিন্ন বেতন-প্রজন্মের যুক্তি প্রয়োজন। এই ক্ষেত্রে, আমরা বিদ্যমান PayrollGenerator সংশোধন করতে পারি এবং এই কার্যকারিতা যোগ করতে পারি:
class PayslipGenerator
def initialize(employee, month)
@employee = employee
@month = month
end
def generate_payslip
# Code to read from database,
# generate payslip
if employee.contractor?
# generate payslip for contractor
else
# generate a normal payslip
end
# and write it to a file
end
end
যাইহোক, এটি একটি খারাপ প্যাটার. এটি করার মাধ্যমে, আমরা বিদ্যমান ক্লাসটি সংশোধন করছি। যদি আমাদের কর্মচারী চুক্তির উপর ভিত্তি করে আরও প্রজন্মের যুক্তি যোগ করতে হয়, তাহলে আমাদের বিদ্যমান শ্রেণীটি সংশোধন করতে হবে, কিন্তু তা করা উন্মুক্ত/বন্ধ নীতি লঙ্ঘন করে। ক্লাস পরিবর্তন করে, আমরা অনিচ্ছাকৃত পরিবর্তন করার ঝুঁকি নিয়ে থাকি। কিছু পরিবর্তন বা যোগ করা হলে, এটি বিদ্যমান কোডে অজানা সমস্যা সৃষ্টি করতে পারে। এই if-else একই ক্লাসের মধ্যে আরও জায়গায় করতে পারে। সুতরাং, যখন আমরা একটি নতুন কর্মচারীর ধরন যোগ করি, তখন আমরা সেই জায়গাগুলি মিস করতে পারি যেখানে এই if-else উপস্থিত থাকে। সেগুলিকে খুঁজে বের করা এবং সংশোধন করা ঝুঁকিপূর্ণ হতে পারে এবং একটি সমস্যা তৈরি করতে পারে৷
আমরা এই কোডটিকে এমনভাবে রিফ্যাক্টর করতে পারি যাতে আমরা কার্যকারিতা প্রসারিত করে কার্যকারিতা যোগ করতে পারি কিন্তু সত্তা পরিবর্তন করা এড়াতে পারি। সুতরাং, আসুন প্রতিটির জন্য একটি আলাদা ক্লাস তৈরি করি এবং একই generate
করি তাদের প্রত্যেকের জন্য পদ্ধতি:
class ContractorPayslipGenerator
def initialize(employee, month)
@employee = employee
@month = month
end
def generate
# Code to read from the database,
# generate payslip
# and write it to a file
end
end
class FullTimePayslipGenerator
def initialize(employee, month)
@employee = employee
@month = month
end
def generate
# Code to read from the database,
# generate payslip
# and write it to a file
end
end
নিশ্চিত করুন যে এই একই পদ্ধতির নাম আছে। এখন, এই ক্লাসগুলি ব্যবহার করতে PayslipGenerator ক্লাস পরিবর্তন করুন:
GENERATORS = {
'full_time' => FullTimePayslipGenerator,
'contractor' => ContractorPayslipGenerator
}
class PayslipGenerator
def initialize(employee, month)
@employee = employee
@month = month
end
def generate_payslip
# Code to read from database,
# generate payslip
GENERATORS[employee.type].new(employee, month).generate()
# and write it to a file
end
end
এখানে, আমাদের একটি জেনারেটর ধ্রুবক রয়েছে যা কর্মচারীর প্রকারের উপর ভিত্তি করে কল করার জন্য ক্লাস ম্যাপ করে। কোন ক্লাসে কল করতে হবে তা নির্ধারণ করতে আমরা এটি ব্যবহার করতে পারি। এখন, যখন আমাদের নতুন কার্যকারিতা যোগ করতে হবে, তখন আমরা তার জন্য একটি নতুন ক্লাস তৈরি করতে পারি এবং এটি জেনারেটর ধ্রুবক যোগ করতে পারি। এটি কিছু ভাঙ্গা বা বিদ্যমান যুক্তি সম্পর্কে চিন্তা করার প্রয়োজন ছাড়াই ক্লাস প্রসারিত করতে সাহায্য করে। আমরা সহজেই যেকোনো ধরনের পেস্লিপ জেনারেটর যোগ বা অপসারণ করতে পারি।
লিসকভ প্রতিস্থাপন নীতি - LSP
লিসকভ প্রতিস্থাপন নীতি বলে, "যদি S একটি T-এর সাব-টাইপ হয়, তাহলে T টাইপের বস্তুগুলি S টাইপের বস্তুর সাথে প্রতিস্থাপিত হতে পারে" .
এই নীতিটি বুঝতে, আসুন প্রথমে সমস্যাটি বুঝতে পারি। খোলা/বন্ধ নীতির অধীনে, আমরা সফ্টওয়্যারটিকে এমনভাবে ডিজাইন করেছি যাতে এটি বাড়ানো যায়। আমরা একটি সাবক্লাস পেস্লিপ জেনারেটর তৈরি করেছি যা একটি নির্দিষ্ট কাজ করে। কলকারীর জন্য, তারা যে ক্লাসে ডাকছে তা অজানা। এই ক্লাসগুলির একই আচরণ থাকা দরকার যাতে কলকারী পার্থক্য বলতে অক্ষম হয়। আচরণ দ্বারা, আমরা বলতে চাই যে ক্লাসের পদ্ধতিগুলি সামঞ্জস্যপূর্ণ হওয়া উচিত। এই ক্লাসগুলির পদ্ধতিগুলির নিম্নলিখিত বৈশিষ্ট্যগুলি থাকা উচিত:
- একই নাম আছে
- একই ডেটা টাইপের সাথে একই সংখ্যক আর্গুমেন্ট নিন
- একই ডেটা টাইপ ফেরত দিন
আসুন পেস্লিপ জেনারেটরের উদাহরণ দেখি। আমাদের দুটি জেনারেটর রয়েছে, একটি পূর্ণকালীন কর্মীদের জন্য এবং অন্যটি ঠিকাদারদের জন্য। এখন, এই পে-স্লিপগুলির সামঞ্জস্যপূর্ণ আচরণ রয়েছে তা নিশ্চিত করার জন্য, আমাদের তাদের একটি বেস ক্লাস থেকে উত্তরাধিকারসূত্রে পেতে হবে। User
নামে একটি বেস ক্লাস সংজ্ঞায়িত করা যাক .
class User
def generate
end
end
উন্মুক্ত/বন্ধ নীতির উদাহরণে আমরা যে সাবক্লাস তৈরি করেছি তার একটি বেস ক্লাস ছিল না। আমরা এটিকে বেস ক্লাস User
করার জন্য পরিবর্তন করি :
class ContractorPayslipGenerator < User
def generate
# Code to generate payslip
end
end
class FullTimePayslipGenerator < User
def generate
# Code to generate payslip
end
end
এর পরে, আমরা User
উত্তরাধিকারসূত্রে প্রাপ্ত যে কোনও সাবক্লাসের জন্য প্রয়োজনীয় পদ্ধতিগুলির একটি সেট সংজ্ঞায়িত করি। ক্লাস আমরা বেস ক্লাসে এই পদ্ধতিগুলি সংজ্ঞায়িত করি। আমাদের ক্ষেত্রে, আমাদের শুধুমাত্র একটি একক পদ্ধতি প্রয়োজন, যাকে জেনারেট বলা হয়।
class User
def generate
raise "NotImplemented"
end
end
এখানে, আমরা জেনারেট পদ্ধতি সংজ্ঞায়িত করেছি, যার একটি raise
আছে বিবৃতি সুতরাং, বেস ক্লাসের উত্তরাধিকারী যে কোনো সাবক্লাসের জেনারেট পদ্ধতি থাকা দরকার। এটি উপস্থিত না থাকলে, এটি একটি ত্রুটি উত্থাপন করবে যে পদ্ধতিটি বাস্তবায়িত হয়নি। এইভাবে, আমরা নিশ্চিত করতে পারি যে সাবক্লাসটি সামঞ্জস্যপূর্ণ। এটির সাহায্যে, কলকারী সর্বদা নিশ্চিত হতে পারে যে generate
পদ্ধতি বর্তমান।
এই নীতিটি যেকোনও সাবক্লাসকে সহজে প্রতিস্থাপন করতে সাহায্য করে জিনিসগুলিকে না ভেঙে এবং অনেক পরিবর্তন করার প্রয়োজন ছাড়াই৷
ইন্টারফেস পৃথকীকরণ নীতি - ISP
ইন্টারফেস বিভাজন নীতিটি স্ট্যাটিক ভাষার ক্ষেত্রে প্রযোজ্য, এবং যেহেতু রুবি একটি গতিশীল ভাষা, তাই ইন্টারফেসের কোন ধারণা নেই। ইন্টারফেসগুলি ক্লাসগুলির মধ্যে বিমূর্তকরণের নিয়মগুলিকে সংজ্ঞায়িত করে৷
নীতিতে বলা হয়েছে,
ক্লায়েন্টদের ইন্টারফেসের উপর নির্ভর করতে বাধ্য করা উচিত নয় যা তারা ব্যবহার করে না। - রবার্ট সি. মার্টিন
এর মানে হল যে কোন ক্লাস ব্যবহার করতে পারে এমন একটি সাধারণ ইন্টারফেসের চেয়ে অনেকগুলি ইন্টারফেস থাকা ভাল। যদি আমরা একটি সাধারণ ইন্টারফেস সংজ্ঞায়িত করি, তাহলে ক্লাসটিকে এমন একটি সংজ্ঞার উপর নির্ভর করতে হবে যা এটি ব্যবহার করে না।
রুবির কোনো ইন্টারফেস নেই, তবে আসুন আমরা ক্লাস এবং সাবক্লাসের ধারণাটি দেখি অনুরূপ কিছু তৈরি করতে।
লিস্কভ প্রতিস্থাপন নীতির জন্য ব্যবহৃত উদাহরণে, আমরা দেখেছি যে সাবক্লাস FullTimePayslipGenerator
সাধারণ শ্রেণীর ব্যবহারকারীর কাছ থেকে উত্তরাধিকারসূত্রে প্রাপ্ত। কিন্তু ব্যবহারকারী একটি খুব সাধারণ শ্রেণী এবং অন্যান্য পদ্ধতি থাকতে পারে। যদি আমাদের অন্য কার্যকারিতা থাকতে হয়, যেমন Leave
, এটি ব্যবহারকারীর একটি উপশ্রেণী হতে হবে। Leave
একটি জেনারেট পদ্ধতির প্রয়োজন নেই, তবে এটি এই পদ্ধতির উপর নির্ভর করবে। সুতরাং, জেনেরিক ক্লাস না করে, আমরা এর জন্য একটি নির্দিষ্ট ক্লাস রাখতে পারি:
class Generator
def generate
raise "NotImplemented"
end
end
class ContractorPayslipGenerator < Generator
def generate
# Code to generate payslip
end
end
class FullTimePayslipGenerator < Generator
def generate
# Code to generate payslip
end
end
এই জেনারেটরটি পেস্লিপ জেনারেশনের জন্য নির্দিষ্ট, এবং সাবক্লাসকে জেনেরিক User
উপর নির্ভর করতে হবে না ক্লাস।
নির্ভরতা বিপরীত নীতি - DIP
ডিপেন্সি ইনভার্সন একটি নীতি যা সফ্টওয়্যার মডিউলগুলিকে ডিকপল করার ক্ষেত্রে প্রয়োগ করা হয়৷
৷একটি উচ্চ-স্তরের মডিউল একটি নিম্ন-স্তরের মডিউলের উপর নির্ভর করা উচিত নয়; উভয়ই বিমূর্ততার উপর নির্ভরশীল।
নকশা, উপরে বর্ণিত নীতিগুলি ব্যবহার করে, নির্ভরতা বিপরীত নীতির দিকে আমাদের গাইড করে। যে কোনো শ্রেণির একটি একক দায়িত্ব আছে তাদের কাজ করার জন্য অন্য শ্রেণির জিনিসের প্রয়োজন হয়। পে-রোল জেনারেট করতে, আমাদের ডাটাবেসে অ্যাক্সেসের প্রয়োজন, এবং রিপোর্ট তৈরি হয়ে গেলে আমাদের একটি ফাইলে লিখতে হবে। একক দায়িত্ব নীতির সাথে, আমরা একটি একক শ্রেণীর জন্য একটি মাত্র কাজ করার চেষ্টা করছি। কিন্তু, ডাটাবেস থেকে পড়া এবং ফাইলে লেখার মতো জিনিস একই ক্লাসের মধ্যে সম্পাদন করতে হবে।
এই নির্ভরতাগুলি অপসারণ করা এবং মূল ব্যবসায়িক যুক্তিকে দ্বিগুণ করা গুরুত্বপূর্ণ। এটি পরিবর্তনের সময় কোডটিকে তরল হতে সাহায্য করবে এবং পরিবর্তন অনুমানযোগ্য হয়ে উঠবে। নির্ভরতা উল্টানো প্রয়োজন, এবং মডিউলের কলকারীর নির্ভরতার উপর নিয়ন্ত্রণ থাকা উচিত। আমাদের পেস্লিপ জেনারেটরে, নির্ভরতা হল রিপোর্টের ডেটার উৎস; এই কোডটি এমনভাবে সংগঠিত হওয়া উচিত যাতে কলার উৎস নির্দিষ্ট করতে পারে। নির্ভরতার নিয়ন্ত্রণ উল্টানো প্রয়োজন এবং কলকারী দ্বারা সহজেই পরিবর্তন করা যেতে পারে।
আমাদের উপরের উদাহরণে, ContractorPayslipGenerator
মডিউল নির্ভরতা নিয়ন্ত্রণ করে, যেখানে ডেটা পড়তে হবে এবং কীভাবে আউটপুট সংরক্ষণ করতে হবে তা নির্ধারণ করে ক্লাস দ্বারা নিয়ন্ত্রিত হয়। এটি প্রত্যাবর্তন করতে, আসুন একটি UserReader
তৈরি করি ক্লাস যা ব্যবহারকারীর ডেটা পড়ে:
class UserReader
def get
raise "NotImplemented"
end
end
এখন, ধরুন আমরা পোস্টগ্রেস থেকে ডেটা পড়তে চাই। আমরা এই উদ্দেশ্যে UserReader-এর একটি সাবক্লাস তৈরি করি:
class PostgresUserReader < UserReader
def get
# Code to read data from Postgres
end
end
একইভাবে, FileUserReader
থেকে আমাদের একটি পাঠক থাকতে পারে , InMemoryUserReader
, বা অন্য কোন ধরনের পাঠক আমরা চাই। আমাদের এখন FullTimePayslipGenerator
পরিবর্তন করতে হবে ক্লাস যাতে এটি PostgresUserReader
ব্যবহার করে নির্ভরতা হিসাবে।
class FullTimePayslipGenerator < Generator
def initialize(datasource)
@datasource = datasource
end
def generate
# Code to generate payslip
data = datasource.get()
end
end
কলার এখন PostgresUserReader
পাস করতে পারে নির্ভরতা হিসাবে:
datasource = PostgresUserReader.new()
FullTimePayslipGenerator.new(datasource)
কলার নির্ভরতার উপর নিয়ন্ত্রণ রাখে এবং প্রয়োজনে সহজে উৎস পরিবর্তন করতে পারে।
নির্ভরতা উল্টানো শুধুমাত্র ক্লাসে প্রযোজ্য নয়। আমাদের কনফিগারেশনগুলিও উল্টাতে হবে। উদাহরণস্বরূপ, Postgres সার্ভার সংযোগ করার সময়, আমাদের নির্দিষ্ট কনফিগারেশনের প্রয়োজন, যেমন DBURL, ব্যবহারকারীর নাম এবং পাসওয়ার্ড। ক্লাসে এই কনফিগারেশনগুলিকে হার্ডকোড করার পরিবর্তে, আমাদের কলারের কাছ থেকে সেগুলি পাস করতে হবে৷
class PostgresUserReader < UserReader
def initialize(config)
config = config
end
def get
# initialize DB with the config
self.config
# Code to read data from Postgres
end
end
কলার দ্বারা কনফিগারেশন প্রদান করুন:
config = { url: "url", user: "user" }
datasource = PostgresUserReader.new(config)
FullTimePayslipGenerator.new(datasource)
কলারের এখন নির্ভরতার উপর সম্পূর্ণ নিয়ন্ত্রণ রয়েছে এবং পরিবর্তন পরিচালনা সহজ এবং কম বেদনাদায়ক।
উপসংহার
সলিড ডিজাইন কোড ডিকপল করতে এবং পরিবর্তন কম বেদনাদায়ক করতে সাহায্য করে। প্রোগ্রামগুলিকে এমনভাবে ডিজাইন করা গুরুত্বপূর্ণ যাতে সেগুলি ডিকপল, পুনরায় ব্যবহারযোগ্য এবং পরিবর্তনের জন্য প্রতিক্রিয়াশীল হয়। পাঁচটি সলিড নীতির সবকটিই একে অপরের পরিপূরক এবং সহাবস্থান করা উচিত। একটি ভাল-ডিজাইন করা কোডবেস নমনীয়, পরিবর্তন করা সহজ এবং কাজ করা মজাদার। যেকোন নতুন ডেভেলপার ঝাঁপিয়ে পড়তে পারে এবং সহজেই কোডটি বুঝতে পারে।
SOLID কোন ধরনের সমস্যার সমাধান করে এবং আমরা কেন এটি করছি তা বোঝা সত্যিই গুরুত্বপূর্ণ। সমস্যাটি বোঝা আপনাকে ডিজাইনের নীতিগুলি গ্রহণ করতে এবং আরও ভাল সফ্টওয়্যার ডিজাইন করতে সহায়তা করে৷