Data Clumps: একসঙ্গে ঘোরা মান এক object-এ আনুন

প্রতিটি function-এ একই দুটি-তিনটি মান একসঙ্গে এলে তারা একটি object হতে চায়। পরীক্ষিত DateRange উদাহরণে দেখুন।

তিন বাক্সে বারবার আসা একই জোড়া মান এক গোষ্ঠীতে

Data Clumps হলো একসঙ্গে সবসময় আসা মানের গোষ্ঠী: start ও end, latitude ও longitude, street, city ও zip। এগুলো function থেকে function-এ পাঠানো হয়, পাশাপাশি field হিসেবে রাখা হয় এবং বারবার যাচাই হয়। নিচের code ও output প্রকাশের আগে Python 3-এ চালানো।

কীভাবে চিনবেন

  • একই দুটি-তিনটি parameter-এর নাম অনেক signature-এ দেখা যায়।
  • একটি class-এ সাধারণ prefix-ওয়ালা কয়েকটি field (billing_street, billing_city, billing_zip)।
  • গোষ্ঠী থেকে একটি মান মুছলে বাকিদের অর্থ থাকে না।
  • একই যাচাই ("শেষ শুরুর আগে হতে পারে না") কয়েক জায়গায় লেখা।

ভালো পরীক্ষা: একটি মান মুছুন। বাকিরা অর্থ হারালে তারা এক object-এ থাকার।

সমাধান: clump-কে object করুন

গোষ্ঠীর জন্য ছোট একটি class বানান, নিয়ম তাতে সরান ও সেটি পাঠান। এটি প্রায়ই একসঙ্গে Long Parameter List-ও সারায় এবং function-এ ছড়ানো আচরণের একটি ঘর দেয়।

from dataclasses import dataclass
from datetime import date

# ---- BEFORE: the same two values travel together through every function ----
def days_between_before(start, end):
    if end < start: raise ValueError("end before start")
    return (end - start).days + 1
def overlaps_before(s1, e1, s2, e2):
    return s1 <= e2 and s2 <= e1
def label_before(start, end):
    return f"{start:%d %b %Y} to {end:%d %b %Y}"

# ---- AFTER: the clump becomes one object that owns the rules ----
@dataclass(frozen=True)
class DateRange:
    start: date
    end: date
    def __post_init__(self):
        if self.end < self.start:
            raise ValueError("end before start")
    @property
    def days(self): return (self.end - self.start).days + 1
    def overlaps(self, other): return self.start <= other.end and other.start <= self.end
    def __str__(self): return f"{self.start:%d %b %Y} to {self.end:%d %b %Y}"

DateRange এখন তৈরির সময় একবার দেখে শেষ শুরুর আগে নয়, তাই অবৈধ range কখনও থাকতেই পারে না। যে function start, end নিত তা হয়েছে property (days), method (overlaps) ও string রূপ।

উত্তর একই, প্রমাণ

a = DateRange(date(2026, 11, 3), date(2026, 11, 9))
b = DateRange(date(2026, 11, 8), date(2026, 11, 12))
print(a, "|", a.days, "days |", "overlaps b:", a.overlaps(b))
assert a.days == days_between_before(a.start, a.end)
assert a.overlaps(b) == overlaps_before(a.start, a.end, b.start, b.end)
assert str(a) == label_before(a.start, a.end)
try: DateRange(date(2026, 11, 9), date(2026, 11, 3))
except ValueError as e: print("error:", e)
print("same answers, and the rule lives in one place")
03 Nov 2026 to 09 Nov 2026 | 7 days | overlaps b: True
error: end before start
same answers, and the rule lives in one place

নতুন object পুরনো function-গুলোর মতো একই দিনের সংখ্যা, overlap ফল ও label দেয়, আর অবৈধ range একটি পরিষ্কার error দিয়ে প্রত্যাখ্যান করে। আগে overlaps_before(s1, e1, s2, e2)-এর মতো function চারটি খোলা তারিখ নিত এমন ক্রমে যা মনে রাখতে হতো।

নিরাপদে refactor করবেন যেভাবে

  1. গোষ্ঠী খুঁজুন: কয়েকটি signature বা field-এ একই নাম দেখুন।
  2. ওই field-ওয়ালা frozen dataclass বানান এবং __post_init__-এ যাচাই দিন।
  3. একটি function-কে object নিতে বদলান। test চালান।
  4. বাকিগুলোয় একই করুন, আর যে logic শুধু এই field ব্যবহার করে তা নতুন class-এ সরান।
  5. নতুন আচরণ খুঁজুন যার এখন স্পষ্ট ঘর আছে (যেমন overlaps)।

কখন ছেড়ে দেবেন

  • যে দুটি মান কাকতালীয়ভাবে একবার একসঙ্গে ব্যবহৃত, তা clump নয়।
  • স্বাধীনভাবে বদলায় এমন মানের গোষ্ঠীর ভাগ করা object লাগে না।
  • লক্ষ লক্ষ আইটেমের hot loop-এ class-এর চেয়ে সাধারণ tuple ভালো বিনিময় হতে পারে।

সাধারণ ভুল

  • class বানিয়ে logic পুরনো function-এই রেখে দেওয়া, ফলে object খালি থলে থেকে যায় (পরের smell, Data Class)।
  • পরিবর্তনযোগ্য করা, ফলে একটি function এমন range বদলায় যা অন্যটি এখনও ব্যবহার করছে।
  • constructor-এ যাচাই না করা।
  • আসল ধারণার বদলে অস্পষ্ট নাম (Info, Data) দেওয়া।

মূল কথা: যে মান সবসময় একসঙ্গে ঘোরে সে লুকানো একটি ধারণা। তাকে নাম দিন, নিয়মসহ object করুন, signature ছোট হয় আর আচরণ ঘর পায়।


Code smell ও refactoring সিরিজ, Bloaters: 5 নম্বর (মোট 5)। আগের লেখা: Long Parameter List: সম্পর্কিত argument গুছিয়ে object করুন.

আরও পড়ুন

Long Parameter List: সম্পর্কিত argument গুছিয়ে object করুন

নয়টি positional argument সহজেই গুলিয়ে যায়। সম্পর্কিত মান object-এ গুছিয়ে ও option keyword-only করে রাখা শিখুন, পরীক্ষিত আগে-পরের code-সহ।

Code smell•৩ মিনিটে পড়া

Primitive Obsession: খোলা string ও float-এর বদলে value object

খোলা string ও float ভুল মান যেকোনো জায়গায় যেতে দেয়। টাকা ও email-এর ছোট value object কীভাবে নিয়ম যোগ করে ও এক শ্রেণির bug দূর করে শিখুন।

Code smell•৩ মিনিটে পড়া