সফটওয়্যার ডিজাইন প্রিন্সিপাল-২
ওপেন ক্লোজ প্রিন্সিপাল (Open Close Principle-OCP)
বব চাচ্চু বলেছেন
Classes should be open for extension and closed to modification
মানে হল, ক্লাস কে বর্ধিত করা যাবে কিন্তু পরিবর্তন করা যাবেনা। অর্থাত ক্লাসের ফিচার কে এক্সটার্নালি বাড়ানো যাবে কিন্তু ক্লাসের মধ্যকার কোডে হাত দেয়া যাবেনা। কেন? ধরেন একটা ক্লাসের কোড লেখা আছে বেশ আগে। প্রোডাকশনে কাজও করছে স্মুথলি। কোড টা টেস্ট ও করা আছে। কোথাও কোন হৈচৈ নেই। এখন কে চায় যে একটা পারফেক্টলি ওয়ার্কিং কোডের মধ্যে হুট করে হাত দিয়ে বাগ তৈরি করতে ! কিন্তু বিধিবাম। আপনাকে তাই ই করতে হবে যদি আপনার কোডকে ওপেন-ক্লোজ প্রিন্সিপাল মেনে ডিজাইন করা না থাকে।
গত পর্বে আমরা একটা বুক শপের ইনভয়েস বানানোর সফটওয়্যার তৈরি করার জন্য ক্লাস ডিজাইন করেছিলাম। সেখানে আমরা ইনভয়েস প্রিন্ট করার জন্য একটি ক্লাস বানিয়েছিলাম। ক্লাসের মধ্যে ইনভয়েস প্রিন্ট করার জন্য পিডিএফ এবং ডক ফরম্যাটের জন্য আলাদা আলাদা মেথড লিখেছিলাম।
class InvoicePrinter {
private Invoice $invoice;
public function __construct(Invoice $invoice): void {
$this->invoice = $invoice;
}
public function printToPDF() {
// PDF print login
}
public function printToDOC(){
// print to doc logic
}
}
ব্যস, ক্লায়েন্ট খুশি। পরিবেশ শান্ত। কিন্ত এতদিন পরে হুট করে ক্লায়েন্টের মাথায় আসল, আরে ! আমার তো csv ফরম্যাটেও ইনভয়েসটা লাগবে। আমাদের কে বলল তার প্রয়োজনের কথা। আমরা কি করব? সিমপ্লি InvoicePrinter ক্লাসের মধ্যে গিয়ে printToCSV নামে একটা মেথড বানালাম। কাজ সমাধা। কিন্তু না। এভাবে না। OCP অনুযায়ী আমরা সেটা করতে পারবনা। তাহলে উক্ত ক্লাসের মধ্যে নতুন কোড না লিখে আমরা কিভাবে এই নতুন ফিচার টা ইমপ্লিমেন্ট করব?
আমাদেরকে abstract এবং interface ব্যবহার করতে হবে।
interface InvoicePrint {
public function print(Invoice $invoice);
}
class PrintToPDF implements InvoicePrint {
public function print(Invoice invoice) {
// Print to PDF
}
}
class PrintToDOC implements InvoicePrint {
public function print(Invoice invoice) {
// Print to DOC
}
}
class PrintToCSV implements InvoicePrint {
public function print(Invoice invoice) {
// Print to CSV
}
}
এখন আমাদের ক্লাসগুলো লুজলি কাপলড হল। ক্লায়েন্ট যদি আরো পাচটা প্রিন্টিং মেথড চায়, আমরা জাস্ট একটা ক্লাস বানায়ে InvoicePrint ইন্টারফেসকে ইমপ্লিমেন্ট করে দেব। পুরাতন কোডে হাতই দেওয়া লাগবেনা।
এখন আপনার মনে হতে পারে যে, আমরা তো সিমপ্লি একাধিক ক্লাস লিখে তার প্রত্যেকটার মধ্যে print মেথড লিখে দিলেই হল। কিন্তু ধরেন আমাদের সফটওয়্যারে শুধু ইনভয়েস প্রিন্ট করবনা, রিপোর্ট ও তো প্রিন্ট করা লাগতে পারে। তখন কিন্তু আমাদের একটা প্রিন্ট ম্যানেজার ক্লাস লাগবে যে সকল প্রিন্ট ক্লাসকে ম্যানেজ করবে।
class PrintManager{
InvoicePrint $invoiceprint;
ReportPrint $reportprint;
public function __construct(InvoicePrint $invoiceprint, ReportPrint $reportprint){
$this->invoiceprint = InvoicePrint $invoiceprint;
$this->reportprint = ReportPrint $reportprint;
}
}
এখন পলিফরমিজমের সাহায্যে আমরা যেকোন ক্লাসকে InvoicePrint ইন্টারফেসকে ইমপ্লিমেন্ট করতে পারব এবং যেকোন সময় যেকোন কিছু প্রিন্ট করার ফরম্যাট চেঞ্জ করতে পারব।