DRY, KISS ও YAGNI প্রিন্সিপল
এই পাঠে যা শিখবেন
- DRY প্রিন্সিপল এবং কেন ডুপ্লিকেট লজিক একটি বাস্তব, সাধারণ বাগের উৎস
- KISS প্রিন্সিপল এবং সরলতা বনাম জেনারেলিটির সৎ ট্রেড-অফ
- YAGNI প্রিন্সিপল এবং কেন speculative future-proofing প্রায়ই অপচয়
- Python-এ একটি ডুপ্লিকেট ডিসকাউন্ট ফরমুলা বাস্তবে ভাঙার এবং তারপর DRY দিয়ে ঠিক করার একটি সম্পূর্ণ, কোড-ভেরিফায়েড ডেমো
১ · তিনটি ক্লাসিক ডিজাইন মাক্সিম
প্রতিটি জ্ঞান/লজিকের সিস্টেমে একটিমাত্র, দ্ব্যর্থহীন প্রতিনিধিত্ব থাকা উচিত।
সমস্যাটি সঠিকভাবে সমাধান করে এমন সবচেয়ে সরল উপায় বেছে নিন।
যা এখনই সত্যিকারে প্রয়োজন নেই, তা এখনই তৈরি করবেন না।
২ · DRY — জ্ঞানের একক উৎস
DRYDon't Repeat Yourselfপ্রতিটি জ্ঞান/লজিকের সিস্টেমে একটিমাত্র, দ্ব্যর্থহীন প্রতিনিধিত্ব থাকা উচিত। বলে — যদি একই বিজনেস রুল বা ক্যালকুলেশন একাধিক জায়গায় ডুপ্লিকেট থাকে, তাহলে ভবিষ্যতে একটি পরিবর্তন আনতে হলে অবশ্যই প্রতিটি কপি মনে রেখে আপডেট করতে হবে — একটি কপি মিস হয়ে গেলে সেটাই একটি বাস্তব, সাধারণ বাগের উৎস। সমাধান হলো শেয়ার্ড লজিককে একটি একক পুনঃব্যবহারযোগ্য ফাংশন/ক্লাসে বের করে আনা।
৩ · KISS — সরলতা প্রথমে
KISSKeep It Simple, Stupidসমস্যাটি সঠিকভাবে সমাধান করে এমন সবচেয়ে সরল সমাধান বেছে নেওয়ার নীতি, অপ্রয়োজনীয় জটিলতা এড়িয়ে চলা। বলে — সমস্যাটি সঠিকভাবে সমাধান করে এমন সবচেয়ে সরল সমাধান বেছে নিন, অপ্রয়োজনীয় জটিলতা এড়িয়ে চলুন — L03-এর মেইনটেইনেবিলিটি অ্যাট্রিবিউটের সাথে সরাসরি সম্পর্কিত (সরল কোড বোঝা ও রক্ষণাবেক্ষণ করা সহজ)। একটি সৎ, প্রকৃত টেনশন এখানে আছে: সরলতা মাঝেমাঝে জেনারেলিটি/ফ্লেক্সিবিলিটির বিপরীতে ট্রেড-অফ করে — KISS যুক্তি দেয়, যতক্ষণ না বাস্তব সমস্যা সত্যিকারে জটিলতা দাবি করছে, ততক্ষণ সরলতার দিকেই ঝুঁকে থাকা ভালো।
৪ · YAGNI — এখন যা লাগবে তা বানান
YAGNIYou Aren't Gonna Need Itকাল্পনিক ভবিষ্যৎ প্রয়োজনের জন্য এখনই ফাংশনালিটি/ফ্লেক্সিবিলিটি তৈরি না করার নীতি — শুধু বর্তমানে প্রকৃতপক্ষে প্রয়োজনীয় কাজটুকু বানানো। বলে — কাল্পনিক ভবিষ্যৎ প্রয়োজনের জন্য ফাংশনালিটি/ফ্লেক্সিবিলিটি তৈরি করবেন না যা এখনও সত্যিকারে প্রয়োজন নেই — M2/L08-এর অ্যাজাইলের "changing requirements মেনে নেওয়া" মূল্যবোধের সরাসরি সহযোগী: এখন যা প্রয়োজন সেটাই বানান; রিকোয়ারমেন্ট বদলাবেই, তাই speculative future-proofing প্রায়ই একটি এমন প্রয়োজনের জন্য বাড়তি জটিলতা (KISS-এর লঙ্ঘন) যোগ করে, যা হয়তো কখনোই বাস্তবে দেখা দেবে না।
৫ · DRY-লঙ্ঘনের বাস্তব বাগ ঝুঁকি — কোড দিয়ে
একটি ডিসকাউন্ট ফরমুলা তিনটি ভিন্ন ফাংশনে হুবহু কপি-পেস্ট করা আছে। প্রথমে দেখা যাক তিনটিই একমত। তারপর একটি বাস্তবসম্মত পরিস্থিতি সিমুলেট করা হবে — বিজনেস রুল বদলে গেছে, কিন্তু তিনটির মধ্যে মাত্র দুটি জায়গায় আপডেট করা হয়েছে (একটি বাস্তব, সাধারণ ভুল)।
# --- DRY লঙ্ঘন: ডিসকাউন্ট ফরমুলা তিনটি জায়গায় হুবহু কপি-পেস্ট করা ---
def calculate_invoice_total(quantity, unit_price):
if quantity >= 10:
discount = 0.15
elif quantity >= 5:
discount = 0.10
else:
discount = 0.0
return quantity * unit_price * (1 - discount)
def calculate_quote_total(quantity, unit_price):
if quantity >= 10:
discount = 0.15
elif quantity >= 5:
discount = 0.10
else:
discount = 0.0
return quantity * unit_price * (1 - discount)
def calculate_cart_preview_total(quantity, unit_price):
if quantity >= 10:
discount = 0.15
elif quantity >= 5:
discount = 0.10
else:
discount = 0.0
return quantity * unit_price * (1 - discount)
print("=== আপডেটের আগে (তিনটি কপি একমত) ===")
print("Invoice:", calculate_invoice_total(10, 100))
print("Quote: ", calculate_quote_total(10, 100))
print("Cart: ", calculate_cart_preview_total(10, 100))
# ব্যবসায়িক নিয়ম বদলে গেছে: ১০+ পরিমাণে discount এখন ১৫% থেকে বেড়ে ২০% -- কিন্তু বাস্তব বাগের মতো,
# তিনটির মধ্যে মাত্র দুটি জায়গায় আপডেট করা হলো (cart preview-তে আপডেট করতে ভুলে যাওয়া হয়েছে)
def calculate_invoice_total(quantity, unit_price):
if quantity >= 10:
discount = 0.20 # আপডেট হয়েছে
elif quantity >= 5:
discount = 0.10
else:
discount = 0.0
return quantity * unit_price * (1 - discount)
def calculate_quote_total(quantity, unit_price):
if quantity >= 10:
discount = 0.20 # আপডেট হয়েছে
elif quantity >= 5:
discount = 0.10
else:
discount = 0.0
return quantity * unit_price * (1 - discount)
# calculate_cart_preview_total() আপডেট করতে ভুলে যাওয়া হয়েছে -- এখনও পুরনো 0.15 discount ব্যবহার করছে
print("\n=== আপডেটের পরে (মাত্র ২টি জায়গায় ঠিক করা হলো -- বাস্তব বাগ) ===")
print("Invoice:", calculate_invoice_total(10, 100))
print("Quote: ", calculate_quote_total(10, 100))
print("Cart: ", calculate_cart_preview_total(10, 100), " <-- ভুল! এখনও পুরনো ১৫% discount দিচ্ছে")
calculate_invoice_total ও
calculate_quote_total সঠিক নতুন মূল্য ৮০০ (২০% ছাড়) দেয়, কিন্তু calculate_cart_preview_total
এখনও পুরনো ৮৫০ দেয় — তিনটি জায়গায় একই লজিক ডুপ্লিকেট থাকার কারণে সিস্টেম এখন অসংগত, এবং এই
বাগটি শুধু তখনই ধরা পড়বে যখন কেউ কার্ট প্রিভিউ ও ইনভয়েসের মূল্য পাশাপাশি তুলনা করবে।
এবার একই লজিক একটি একক জায়গায় নিয়ে DRY-কমপ্লায়েন্ট সমাধান দেখা যাক:
# --- DRY-কমপ্লায়েন্ট সমাধান: ডিসকাউন্ট লজিক একটিমাত্র জায়গায় ---
def compute_discount_rate(quantity):
if quantity >= 10:
return 0.20
elif quantity >= 5:
return 0.10
return 0.0
def dry_invoice_total(quantity, unit_price):
return quantity * unit_price * (1 - compute_discount_rate(quantity))
def dry_quote_total(quantity, unit_price):
return quantity * unit_price * (1 - compute_discount_rate(quantity))
def dry_cart_preview_total(quantity, unit_price):
return quantity * unit_price * (1 - compute_discount_rate(quantity))
print("=== DRY সংস্করণ: তিনটিই compute_discount_rate() থেকে লজিক নেয় ===")
print("Invoice:", dry_invoice_total(10, 100))
print("Quote: ", dry_quote_total(10, 100))
print("Cart: ", dry_cart_preview_total(10, 100))
# ব্যবসায়িক নিয়ম আবার বদলে গেলে (ধরুন ১০+ discount এখন ২৫%), শুধু ১টি জায়গায় বদলালেই সব consistent থাকে
def compute_discount_rate(quantity):
if quantity >= 10:
return 0.25
elif quantity >= 5:
return 0.10
return 0.0
print("\n=== compute_discount_rate() একবার বদলানোর পরে (এখন সব automatically consistent) ===")
print("Invoice:", dry_invoice_total(10, 100))
print("Quote: ", dry_quote_total(10, 100))
print("Cart: ", dry_cart_preview_total(10, 100))
compute_discount_rate()-কে একবার ২৫% এ পরিবর্তন করার পরে তিনটি ফাংশনই স্বয়ংক্রিয়ভাবে ৭৫০
(১০০০-এর উপর ২৫% ছাড়) দেয় — সম্পূর্ণ সামঞ্জস্যপূর্ণ, কারণ প্রতিটি ফাংশন একই একক উৎস থেকে ডিসকাউন্ট রেট
নিচ্ছে। এই "একবার বদলালেই সব জায়গায় ঠিক" আচরণটাই DRY-এর বাস্তব, প্র্যাকটিক্যাল সুবিধা — কোনো দাবি নয়, উপরের
কোড দিয়ে সরাসরি প্রমাণিত।
DRY ডুপ্লিকেশনের বাগ-ঝুঁকি দূর করে, KISS অপ্রয়োজনীয় জটিলতা এড়ায়, আর YAGNI বলে যা লাগবে না তা এখনই না বানাতে। তিনটিই একে অপরকে শক্তিশালী করে — একটি DRY-কমপ্লায়েন্ট, KISS-অনুসারী কোডবেস, যা YAGNI মেনে শুধু প্রয়োজনীয় জিনিস ধারণ করে, সবচেয়ে সহজে রক্ষণাবেক্ষণযোগ্য।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ DRY সবসময় "একই দুই লাইন কোড দুইবার লিখবেন না" বোঝায় কি — নাকি এর চেয়ে গভীর কিছু?
এর চেয়ে গভীর — DRY মূলত জ্ঞান/লজিক নিয়ে, শুধু টেক্সট নিয়ে নয়। দুটি ভিন্ন জায়গার কোড দেখতে একই রকম হতে পারে কিন্তু সম্পূর্ণ ভিন্ন বিজনেস কারণে বদলাতে পারে — সেক্ষেত্রে সেগুলো একত্র করা DRY-এর অপব্যবহার (accidental duplication-কে জোর করে একত্র করা, যা ভুলভাবে দুটি অসম্পর্কিত জিনিসকে বেঁধে ফেলে)। আসল DRY প্রশ্নটা হলো — "এই দুই জায়গার লজিক কি একই কারণে বদলাবে?"
প্র ০২ KISS-এর "সরলতা" আর YAGNI-এর "এখন যা লাগবে তা বানান" — এই দুটো নীতি একে অপরকে কীভাবে সমর্থন করে?
একটি সিস্টেমে যদি ভবিষ্যতের কাল্পনিক প্রয়োজনের জন্য আগে থেকেই ফ্লেক্সিবল, জেনারেল-পারপাস কোড লেখা হয় (YAGNI-এর লঙ্ঘন), সেটি প্রায় সবসময়ই বাড়তি জটিলতা যোগ করে (KISS-এর লঙ্ঘন) — এক্সট্রা প্যারামিটার, এক্সট্রা abstraction layer, যেগুলো বর্তমান সমস্যার জন্য অপ্রয়োজনীয়। তাই YAGNI মেনে চলা প্রায় স্বয়ংক্রিয়ভাবে KISS মেনে চলার দিকে নিয়ে যায়।
প্র ০৩
উপরের কোড সেলে যদি calculate_cart_preview_total()-ও আপডেট করা হতো, তাহলে কী পরিবর্তন দেখা যেত — এবং সেটা কেন এখনও DRY-লঙ্ঘন থেকে যায়?
তিনটি ফাংশনই ৮০০ দিত — কোনো দৃশ্যমান বাগ থাকত না। কিন্তু এটি এখনও DRY-লঙ্ঘন, কারণ ডিসকাউন্ট লজিক এখনও তিন জায়গায় ডুপ্লিকেট — শুধু এবার কাকতালীয়ভাবে তিনটিই সঠিকভাবে ম্যানুয়ালি আপডেট করা হয়েছে। ঝুঁকিটা রয়েই যায়: পরের বার কেউ যদি একটি জায়গা মিস করে (যেমন এই লেসনের ডেমোতেই দেখানো হয়েছে), বাগটি আবার ফিরে আসবে — DRY-কমপ্লায়েন্ট সমাধান এই ঝুঁকিটাই কাঠামোগতভাবে দূর করে দেয়।
অনুশীলন
-
চিন্তা করুন: একজন ডেভেলপার একটি নতুন ফিচারের জন্য এখনই ৫টি ভিন্ন পেমেন্ট গেটওয়ে সাপোর্ট করার একটি জেনারেল, প্লাগইন-স্টাইল আর্কিটেকচার তৈরি করছেন, যদিও প্রোডাক্টটি এখনও মাত্র ১টি গেটওয়ে ব্যবহার করে এবং নিকট ভবিষ্যতে আর কোনো গেটওয়ে যোগ করার পরিকল্পনা নেই। এটি কোন প্রিন্সিপলের লঙ্ঘন এবং কেন?
এটি YAGNI-এর একটি সরাসরি লঙ্ঘন — একটি কাল্পনিক ভবিষ্যৎ প্রয়োজনের (৫টি গেটওয়ে) জন্য এখনই জটিলতা তৈরি করা হচ্ছে, যদিও বাস্তব বর্তমান প্রয়োজন মাত্র ১টি গেটওয়ে। এটি একই সাথে KISS-ও লঙ্ঘন করে — কারণ একটি অপ্রয়োজনীয় প্লাগইন আর্কিটেকচার কোডবেসে বাড়তি জটিলতা যোগ করে যা বর্তমান সমস্যার জন্য ন্যায্য নয়। ভালো পন্থা: এখন শুধু ১টি গেটওয়ে সরলভাবে ইমপ্লিমেন্ট করা, প্রয়োজন সত্যিকারে দেখা দিলে তখন রিফ্যাক্টর করা।
-
পরীক্ষা করুন: DRY-কমপ্লায়েন্ট কোড সেলে
compute_discount_rate()-এ একটি নতুন টিয়ার যোগ করুন (যেমনquantity >= 20হলেdiscount = 0.30) এবং Run চেপে দেখুন তিনটি ফাংশনই সঠিকভাবে নতুন নিয়ম প্রতিফলিত করে কিনা।হ্যাঁ — যেহেতু তিনটি ফাংশনই
compute_discount_rate()কল করে, নতুন টিয়ার যোগ করার সাথে সাথে তিনটিই স্বয়ংক্রিয়ভাবে নতুন নিয়ম অনুসরণ করবে, কোনো অতিরিক্ত পরিবর্তন ছাড়াই। এটাই DRY-এর মূল প্রতিশ্রুতি — একটি একক পরিবর্তন সব নির্ভরশীল জায়গায় স্বয়ংক্রিয়ভাবে সঠিকভাবে ছড়িয়ে পড়ে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পাঠ ১৬ · SOLID প্রিন্সিপল — SRP ও OCP পরবর্তী পাঠ DRY/KISS/YAGNI-এর মতোই ব্যবহারিক, কিন্তু আরও সুনির্দিষ্ট পাঁচটি অবজেক্ট-ওরিয়েন্টেড ডিজাইন নিয়ম।
- পাঠ ১৪ · কাপলিং ও কোহেশন আগের পাঠ "Low coupling, high cohesion" — এই তিনটি মাক্সিমের পাশাপাশি আরেকটি মৌলিক ডিজাইন লক্ষ্য।