সফটওয়্যার ডেভেলপমেন্টে প্রজেক্ট যখন বড় হতে থাকে, তখন কোডবেস অনেক সময় জট পাকিয়ে যায় বা একে অপরের সাথে ওতপ্রোতভাবে জড়িয়ে (intertwined) পড়ে। প্রায়শই দেখা যায়, একটিমাত্র ক্লাস বা ফাংশন একই সাথে ডেটা প্রসেস করছে, ইউজার ইন্টারফেস (UI) হ্যান্ডেল করছে এবং ডেটাবেসের সাথেও যোগাযোগ করছে। এই ধরনের মনোলিথিক বা জগাখিচুড়ি কোড পরবর্তীতে মেইনটেইন করা, বাগ ফিক্স করা বা স্কেল করা অত্যন্ত কঠিন হয়ে দাঁড়ায়। এই সমস্যার একটি চমৎকার ও প্রফেশনাল সমাধান হলো Separation of Concerns (SoC) প্রিন্সিপাল।
Separation of Concerns (SoC) কী?
SoC হলো একটি অত্যন্ত গুরুত্বপূর্ণ সফটওয়্যার ডিজাইন গাইডলাইন, যা বলে: একটি প্রোগ্রামের প্রতিটি অংশ বা মডিউলকে কেবল একটি নির্দিষ্ট দায়িত্ব বা 'Concern' হ্যান্ডেল করতে হবে। কোনো মডিউল বা ক্লাস একাধিক দায়িত্ব একসাথে পালন করবে না।
একটি কারখানার (factory) কথা চিন্তা করো। সেখানে বিভিন্ন কর্মী থাকে—একজন শুধু যন্ত্রাংশ জোড়া লাগায় (Assembly), আরেকজন কোয়ালিটি চেক করে (QA), এবং অন্যজন প্যাকেজিংয়ের কাজ করে। যদি সবাই সব কাজ একসাথে করতে যায়, তবে পুরো কারখানায় চরম বিশৃঙ্খলা (chaos) দেখা দেবে। কিন্তু প্রত্যেকে যদি শুধু নিজের নির্দিষ্ট কাজটি করে, তবে কাজের গতি ও দক্ষতা বহুগুণ বেড়ে যায়। সফটওয়্যারের ক্ষেত্রেও ঠিক একইভাবে প্রতিটি মডিউল বা ক্লাসকে একটি নির্দিষ্ট দায়িত্ব দেওয়া উচিত।
পাইথন কোড উদাহরণ: SoC না মানলে যা হয় (Bad Practice)
নিচের কোডটিতে একটি ক্লাস একই সাথে তিনটি ভিন্ন কাজ করছে: ডেটা হোল্ড করা, ডেটাবেসে সেভ করা এবং ইউআই-তে ডেটা দেখানো।
import sqlite3
class User:
def __init__(self, name, email):
self.name = name
self.email = email
# Database logic mixed with business logic
def save(self):
conn = sqlite3.connect("database.db")
cursor = conn.cursor()
# Executing raw SQL query directly
cursor.execute(f"INSERT INTO users (name, email) VALUES ('{self.name}', '{self.email}')")
conn.commit()
conn.close()
# Presentation/UI logic mixed here
def display(self):
print(f"User: {self.name}, Email: {self.email}")সমস্যা: এই User ক্লাসটি একাধিক Concern হ্যান্ডেল করছে। ভবিষ্যতে যদি তুমি SQLite বাদ দিয়ে PostgreSQL ব্যবহার করতে চাও, অথবা কনসোলে প্রিন্ট করার বদলে ওয়েব পেজে ডেটা দেখাতে চাও, তবে তোমাকে এই মূল ক্লাসটিকেই বারবার পরিবর্তন করতে হবে। এটি মেইনটেইন করা কঠিন এবং স্কেলেবল নয়।
পাইথন কোড উদাহরণ: SoC মেনে চললে (Good Practice)
SoC মানার জন্য আমরা এই কাজগুলোকে তিনটি স্বাধীন ক্লাসে ভাগ করে দেব।
# 1. Entity Class: Handles only business data (Business Logic)
class User:
def __init__(self, name, email):
self.name = name
self.email = email
# 2. Repository Class: Handles only database interactions (Database Access)
import sqlite3
class UserRepository:
def save(self, user):
conn = sqlite3.connect("database.db")
cursor = conn.cursor()
cursor.execute(f"INSERT INTO users (name, email) VALUES ('{user.name}', '{user.email}')")
conn.commit()
conn.close()
# 3. View Class: Handles only data presentation (Presentation/UI)
class UserView:
def display(self, user):
print(f"User: {user.name}, Email: {user.email}")সুবিধা: এখন প্রতিটি ক্লাস একটি নির্দিষ্ট বা Single Concern হ্যান্ডেল করছে। যদি তোমার ডেটাবেস পরিবর্তন করার প্রয়োজন হয়, তবে তুমি শুধুমাত্র UserRepository পরিবর্তন করবে; User বা UserView ক্লাসে কোনো হাত দিতে হবে না। এতে পুরো সিস্টেমটি অনেক বেশি ফ্লেক্সিবল হয়ে গেল।
SoC প্রিন্সিপাল মেনে চলার গভীরে থাকা সুবিধাসমূহ
১. Maintainability (সহজ রক্ষণাবেক্ষণ): কোড পরিবর্তন করা অনেক সহজ এবং প্রেডিক্টেবল হয়। তুমি জানো ঠিক কোন ফাইলের কোথায় পরিবর্তন করলে কাঙ্ক্ষিত ফলাফল পাওয়া যাবে।
২. Testability (টেস্টিংয়ের সুবিধা): যেহেতু ডেটাবেস এবং UI লজিক আলাদা, তাই ইউনিট টেস্ট (Unit Test) লেখা অনেক সহজ হয়। তুমি চাইলে ডেটাবেস কানেকশন ছাড়াই শুধু বিজনেস লজিক টেস্ট করতে পারবে।
৩. Reusability (পুনর্ব্যবহারযোগ্যতা): মডিউলগুলো স্বাধীন হওয়ায় একটি প্রজেক্টের নির্দিষ্ট অংশ (যেমন: ডেটাবেস কানেকশন ক্লাস) অন্য প্রজেক্টে সহজেই রিইউজ করা যায়।
৪. Scalability (স্কেলেবিলিটি): প্রজেক্টের আকার যত বড়ই হোক না কেন, আর্কিটেকচার গোছানো ও মডুলার থাকে।
বাস্তব জীবনে লেয়ার্ড আর্কিটেকচার যেমন MVC (Model-View-Controller) বা থ্রি-টায়ার আর্কিটেকচার মূলত এই Separation of Concerns (SoC) প্রিন্সিপালের ওপর ভিত্তি করেই তৈরি করা হয়েছে। এটি মেনে চলা মানেই হলো সফটওয়্যারকে ক্লিন, অর্গানাইজড এবং ভবিষ্যৎ পরিবর্তনের জন্য সম্পূর্ণ প্রস্তুত রাখা।