মূল বিষয়বস্তুতে যান
Clean Code Mastery

Hide Delegate: মনিটরকে জিজ্ঞেস করো, মনিটর নিজেই দৌড়াবে

Hide Delegate রিফ্যাক্টরিং শেখো একটা মজার গল্পের মাধ্যমে। employee.department.manager-এর মতো chain লেখা বন্ধ করো — প্রথম object-কে একটা সহজ method দাও আর ভেতরের জার্নি লুকিয়ে রাখো। TypeScript আর C#-এ ধাপে ধাপে উদাহরণসহ।

18 মিনিট আপডেট: June 11, 2026beginner
refactoringshide-delegatelaw-of-demetermessage-chainsencapsulationcouplingtypescriptcsharp

যেদিন ফাতেমা স্কুল মিস করেছিল

ধরো ফাতেমা ঢাকার একটা স্কুলে ক্লাস এইটে পড়ে। সোমবার তার জ্বর, তাই বাড়িতে ছিল। মঙ্গলবার সকালে সে ব্যাগ নিয়ে স্কুলে ঢুকল মাথায় একটাই সহজ প্রশ্ন নিয়ে: আমার হোমওয়ার্ক কী ছিল?

ফাতেমার স্কুলে এই ছোট্ট কাজটা বিশাল ঝামেলা। ক্লাস মনিটর সুমাইয়া পিরিয়ডের রুটিন রাখে। তাই ফাতেমা আগে সুমাইয়ার কাছে গেল, "সুমাইয়া, গতকাল তৃতীয় পিরিয়ডে কে পড়িয়েছিল?" সুমাইয়া ডায়েরি দেখে বলল, "রহিম স্যার।" এখন ফাতেমাকে দুটো করিডোর হেঁটে স্টাফ রুমে যেতে হবে, রহিম স্যারকে খুঁজতে হবে, তারপর জিজ্ঞেস করতে হবে, "স্যার, গতকাল কী হোমওয়ার্ক দিয়েছিলেন?" স্যার বললেন: পৃষ্ঠা ১০ থেকে ১২। ফাতেমা লিখে নিল, ফিরে এলো। এরপর আবার একই ঝামেলা চতুর্থ পিরিয়ডের জন্য। তারপর পঞ্চম। লাঞ্চের আগেই সে পড়ার চেয়ে বেশি হেঁটেছে।

একটু ভাবো — এই কাজ শেষ করতে ফাতেমাকে কী কী জানতে হয়েছিল:

  • জানতে হয়েছিল যে সুমাইয়ার কাছে রুটিন আছে — নোটিশ বোর্ডে নয়।
  • জানতে হয়েছিল কোন পিরিয়ডে কোন স্যার পড়িয়েছিলেন — গতকালের বিকল্প ব্যবস্থাসহ।
  • জানতে হয়েছিল কোন স্যার কোথায় বসেন — স্টাফ রুম, ল্যাব, নাকি মাঠে।

এত কিছু জানতে হলো শুধু একটা হোমওয়ার্কের তালিকার জন্য। আরও খারাপ কথা হলো: স্কুল যদি এর যেকোনো একটা বদলে দেয় — রুটিন নোটিশ বোর্ডে চলে যায়, নতুন বিকল্প স্যার আসেন, স্টাফ রুম নতুন ভবনে সরে যায় — তাহলে ফাতেমার পুরো রুটিন ভেঙে পড়বে।

এখন ভালো স্কুলের কথা ভাবো। ফাতেমা সুমাইয়ার কাছে যায় আর একটাই প্রশ্ন করে: "সুমাইয়া, আমার হোমওয়ার্ক কী ছিল?" সুমাইয়া ডায়েরি দেখে, বিরতিতে নিজেই স্যারদের জিজ্ঞেস করে, আর ফাতেমাকে পুরো তালিকা দিয়ে দেয়। ফাতেমা জানে না সুমাইয়া কীভাবে তথ্যটা জোগাড় করেছে। এটা সুমাইয়ার ব্যাপার। ফাতেমা একজন বিশ্বস্ত মানুষকে একটা প্রশ্ন করেছে, একটা উত্তর পেয়েছে।

চিত্র ১: ফাতেমার মঙ্গলবার — মেজাজের স্কোরসহ। পুরানো পদ্ধতিতে ক্লান্তিকর treasure hunt; নতুন পদ্ধতিতে একটাই সুখী প্রশ্ন।

ব্যাপারটা হলো: নতুন স্কুলে হাঁটাটা এখনো হচ্ছে। সুমাইয়া এখনো স্যারদের সাথে কথা বলছে। কাজটা উধাও হয়নি — সুমাইয়ার পেছনে চলে গেছে, যেটা ফাতেমা দেখতে পায় না আর তার উপর নির্ভর করে না।

চিত্র ২: নতুন স্কুলে কী হচ্ছে। ফাতেমা একটা প্রশ্ন করে; মনিটর নিজেই ভেতরে দ্বিতীয় hop করে।

কোডে Hide Delegate রিফ্যাক্টরিং ঠিক এটাই করে। যখন তোমার code একটা object-এর ভেতর দিয়ে আরেকটা object ধরতে যায় আর তারপর সেটার উপর কিছু call করে, তুমি নিজেই ফাতেমার স্টাফ রুম ট্যুর করছ। Hide Delegate বলে: থামো। প্রথম object-কে একটা সহজ method দাও, সেটাকে নিজেই ভেতরে দৌড়াতে দাও।

Hide Delegate কী?

Hide Delegate হলো Martin Fowler-এর বই Refactoring থেকে একটা রিফ্যাক্টরিং। recipe ছোট: যখন একটা client একটা object-কে তার একটা অংশ চেয়ে জিজ্ঞেস করে আর তারপর সেই অংশের উপর method call করে, তখন প্রথম object-এ একটা নতুন method যোগ করো যেটা দ্বিতীয় call ভেতরে নিজেই করে। সেই অংশটা — যাকে delegate বলা হয় — client-এর দৃষ্টি থেকে মিলিয়ে যায়।

বিখ্যাত উদাহরণটা দেখো। client একজন employee-র manager চায়:

// Client Employee-র ভেতর দিয়ে Department-এ পৌঁছায়:
const manager = employee.department.manager;

client এখানে দুটো তথ্য জানে: employee-র একটা department আছে, আর department-এর একজন manager আছে। দ্বিতীয় তথ্যটা client-এর জানার দরকার নেই। Hide Delegate করলে:

class Employee {
  constructor(private department: Department) {}
 
  get manager(): Person {
    return this.department.manager; // hop ভেতরে হয়
  }
}
 
// Client একজন বন্ধুকে, একটা প্রশ্ন করে:
const manager = employee.manager;

দুই hop-এর chain এক লাইনে পরিণত হলো। Department এখন Employee-এর একটা private বিষয়। আগামীকাল যদি কোম্পানি সিদ্ধান্ত নেয় যে manager department-এর বদলে project team থেকে আসবে, শুধু Employee বদলাবে। প্রতিটা client কাজ করতে থাকবে, কারণ তারা শুধু employee.manager বলেছিল।

চিত্র ৩: একই request, দুটো আকার। chain version caller-কে path-এর সাথে যুক্ত করে; hidden version শুধু একটা method নামের সাথে।

class-diagram আকারে পরিবর্তনটা ছোট কিন্তু শক্তিশালী: caller থেকে Department-এর পাবলিক রাস্তা বন্ধ, আর Employee-তে একটা ছোট্ট নতুন method খোলে।

চিত্র ৪: Hide Delegate-এর পরে। Employee getManager অফার করে আর Department গোপনে রাখে। Caller-রা কখনো Department স্পর্শ করে না।

বড় ধারণাটার নাম Law of Demeter — "বন্ধুদের সাথে কথা বলো, অপরিচিতদের সাথে নয়।" তোমার method শুধু সেই object-গুলোর সাথে কথা বলবে যেগুলো সে সরাসরি ধরে রাখে, parameter হিসেবে পায়, বা নিজে তৈরি করে। অন্য object থেকে বের করে আনা object-এর সাথে নয়।

কলেজ কর্নার: Law of Demeter (Lieberherr আর Holland, ১৯৮৯) আনুষ্ঠানিকভাবে class C-এর method M-এর জন্য বলে: M শুধু C নিজের, M-এর parameter-দের, M যে object তৈরি করে, আর C-এর direct component object-গুলোর method invoke করতে পারবে। getB() যে object রিটার্ন করে সেটা এগুলোর কোনোটাই নয় — এটা "অপরিচিত" — তাই a.getB().getC() নিয়মটা ভাঙে। কেউ কেউ এই নিয়মটাকে "শুধু একটা dot ব্যবহার করো" বলে সহজ করেন, কিন্তু এটা আসল নিয়ম নয়। query.where(x).orderBy(y) type fluent builder-এ অনেক dot আছে কিন্তু কোনো structural leakage নেই, কারণ প্রতিটা call একই ধরনের object রিটার্ন করে। dot গণনা করে নয়, কী knowledge ফাঁস হচ্ছে সেটা দিয়ে বিচার করো।

💡

তোমার code-এর একটা সহজ পরীক্ষা: লাইনটা উচ্চস্বরে পড়ো। "Employee-র department-এর manager" — দুটো possessive মানে তুমি অন্য কারো ভেতরের structure বর্ণনা করছ। "Employee-র manager" — একটা possessive, একজন বন্ধু, একটা প্রশ্ন। একটার দিকে লক্ষ্য রাখো।

কখন এটা দরকার?

এই লক্ষণগুলো দেখলে Hide Delegate ব্যবহার করো:

  1. Client code-এ train wreck। order.customer.address.city-এর মতো এক dot-এর পর আরেক dot। এটা Message Chains smell, আর Hide Delegate হলো এর এক নম্বর cure।
  2. একই chain সবখানে। বিশটা file সবাই employee.department.manager লিখলে, structure পরিবর্তন হলে বিশটাই ভাঙবে। একটা hidden hop বিশটাকেই ঠিক করে দেয়।
  3. Client অনেক বেশি জানছে। Client code যদি নির্ভর করে object-গুলো ভেতরে কীভাবে সংযুক্ত তার উপর, সেটা Inappropriate Intimacy। Delegate লুকালে privacy ফিরে আসে।
  4. এমন structure যেটা বদলাবে। যদি আগে থেকেই মনে হচ্ছে "manager department থেকে আসে" চিরকাল সত্য থাকবে না, এখনই লুকাও — পঞ্চাশটা call site assumption কপি করার আগেই।
সংকেতকেমন দেখায়শক্তি
Train wreckclient code-এ a.b.c.dশক্তিশালী — দ্রুত refactor করো
পুনরাবৃত্ত chainএকই path অনেক file-এঅনেক শক্তিশালী — একটা পরিবর্তনে সব ভাঙে
পরিবর্তনশীল structureমাঝের link বদলানোর সম্ভাবনা আছেশক্তিশালী — ছড়ানোর আগেই লুকাও
একটা ছোট, stable, local chainএক জায়গায় order.id style accessদুর্বল — ছেড়ে দাও

পুনরাবৃত্ত chain কেন এত গুরুত্বপূর্ণ? কারণ real codebase-এ, একটা structure পরিবর্তনের বেশিরভাগ ব্যথা তুমি যে class edit করেছ সেখান থেকে আসে না — আসে সেই প্রতিটা caller থেকে যারা পুরানো path মুখস্থ করেছিল।

চিত্র ৫: একটা structure পরিবর্তনের ব্যথা সাধারণত কোথায় পড়ে। তুমি যে class পরিবর্তন করেছ সেটা ছোট্ট slice; যে call sites path জানত সেগুলো বড় অংশ।

আর যখন থামবে: একই class-এ দশম forwarding method যোগ করতে গেলে একটু থামো। তুমি হয়তো একটা Middle Man বানাচ্ছ — এমন একটা class যেটা শুধু forward করে। তার cure হলো ঠিক উল্টো রিফ্যাক্টরিং, Remove Middle Man। এই balance নিয়ে একটু পরেই কথা বলব।

আগে আর পরে এক নজরে

সবচেয়ে ছোট্ট সম্পূর্ণ ছবি:

// ---------- BEFORE ----------
class Department {
  constructor(public manager: Person, public chargeCode: string) {}
}
 
class Employee {
  constructor(public department: Department) {}
}
 
// Clients সবখানে chain-এ হাঁটে:
const manager = employee.department.manager;
const code = employee.department.chargeCode;
// ---------- AFTER ----------
class Employee {
  constructor(private department: Department) {}
 
  get manager(): Person {
    return this.department.manager;
  }
 
  get chargeCode(): string {
    return this.department.chargeCode;
  }
}
 
// Clients একটা object-কে জিজ্ঞেস করে:
const manager = employee.manager;
const code = employee.chargeCode;

department field public থেকে private হয়ে গেল। এই একটাই keyword হলো পুরস্কার: বাইরের কেউ আর employee-department link-এর উপর নির্ভর করতে পারবে না।

চিত্র ৬: client থেকে বের হওয়া arrow গণনা করো। আগে: দুটো dependency। পরে: একটা। কম arrow মানে ভাঙার কম কারণ।

ধাপে ধাপে, নিরাপদ উপায়ে

Refactoring মানে "নতুন করে লিখে প্রার্থনা করো" নয়। এটা হলো ছোট ছোট পদক্ষেপ, প্রতিটার পরে test। Hide Delegate-এর নিরাপদ routine এখানে।

Step 1 — Chain খোঁজো। Codebase-এ server.delegate.something pattern খোঁজো। clients যে delegate method-গুলো ব্যবহার করে সেগুলোর তালিকা করো। ধরো clients employee.department.manager আর employee.department.chargeCode ব্যবহার করে।

Step 2 — Server-এ একটা forwarding method যোগ করো। কোনো client এখনো ছুঁও না। শুধু নতুন দরজাটা যোগ করো:

class Employee {
  constructor(public department: Department) {} // এখনো public!
 
  get manager(): Person {
    return this.department.manager;
  }
}

Compile করো আর test চালাও। সব কিছু এখনো pass করবে, কারণ আর কিছুই পরিবর্তন হয়নি।

Step 3 — Client-দের একে একে redirect করো। একটা call site-এ employee.department.manager বদলে employee.manager করো। Test করো। পরের call site। Test করো। একঘেয়ে? হ্যাঁ। নিরাপদ? সম্পূর্ণ। কোনো test fail করলে তুমি জানবে ঠিক কোন এক-লাইনের পরিবর্তনটা কারণ।

Step 4 — প্রতিটা delegate method-এর জন্য পুনরাবৃত্তি করো। get chargeCode() যোগ করো, একইভাবে তার caller-দের redirect করো।

Step 5 — দরজা বন্ধ করো। যখন কোনো client আর employee.department সরাসরি ছুঁচ্ছে না, field-টা private করো:

class Employee {
  constructor(private department: Department) {}
  // ...getters উপরের মতো
}

Compiler যদি complaint করে, অভিনন্দন — সে এইমাত্র একটা call site খুঁজে পেয়েছে যেটা তুমি মিস করেছিলে। ঠিক করো আর আবার try করো।

Step 6 — Server-এর interface দেখো। Employee-কে এখন দেখো। প্রতিটা নতুন method কি তার জায়গা পাওয়ার যোগ্য? দুই-তিনটা অর্থপূর্ণ method যোগ হলে ভালো। পনেরোটা pure forward হলে, তুমি বাড়াবাড়ি করেছ; নিচের balance section পড়ো।

এই refactoring-কে ঘিরে একটা codebase-এর পুরো জীবনচক্র একটা slow swing-এর মতো। তোমার কাজ হলো এটাকে মাঝখানে ধরে রাখা:

চিত্র ৭: একটা real codebase-এ delegation-এর জীবনচক্র। Hide Delegate তোমাকে chain-land থেকে বের করে; বছরের পর বছরের অভ্যাসে wrapper-land-এ চলে যেতে পারো; Remove Middle Man ফিরিয়ে আনে।
⚠️

Step 2 থেকে 5 কখনো test না চালিয়ে একটা বড় commit-এ করো না। Refactoring-এর পুরো নিরাপত্তা আসে rhythm থেকে: ছোট পরিবর্তন, সবুজ test, ছোট পরিবর্তন, সবুজ test। তোমার project-এ এই code-এর আশেপাশে কোনো test না থাকলে, আগে কিছু characterization test লিখে নাও — এমনকি সহজ ones যেগুলো আজকের behavior assert করে — কিছু সরানোর আগেই।

একটা বড় বাস্তব উদাহরণ

ধরো real project-এর কাছাকাছি কিছু: একটা স্কুল result portal। Client code একজন student-এর result card print করে, আর আজ সেটা অনেক গভীরে খোঁজে:

class School {
  constructor(public principal: Person, public board: string) {}
}
 
class ClassSection {
  constructor(public school: School, public classTeacher: Person) {}
}
 
class Student {
  constructor(
    public name: string,
    public section: ClassSection,
    public marks: Map<string, number>
  ) {}
}
 
// Client: result card printer — train wreck ভর্তি
function printResultCard(student: Student): string {
  const teacher = student.section.classTeacher.name;
  const board = student.section.school.board;
  const principal = student.section.school.principal.name;
  return `${student.name} | Teacher: ${teacher} | Board: ${board} | Signed: ${principal}`;
}

এই printer পুরো school hierarchy জানে: student থেকে section, section থেকে school, school থেকে principal। চারটা class, তিনটা link। স্কুল যদি কখনো একজন student-কে দুটো section-এ রাখতে দেয়, এই function ভাঙবে — আর যে প্রতিটা function একই path-এ হেঁটেছে সেগুলোও ভাঙবে।

Hide Delegate প্রয়োগ করো। প্রতিটা object তার নিজের জগত নিয়ে প্রশ্নের উত্তর দেয়:

class ClassSection {
  constructor(private school: School, private classTeacher: Person) {}
 
  get teacherName(): string {
    return this.classTeacher.name;
  }
  get boardName(): string {
    return this.school.board; // school hop এখানে লুকানো
  }
  get principalName(): string {
    return this.school.principal.name;
  }
}
 
class Student {
  constructor(
    public name: string,
    private section: ClassSection,
    public marks: Map<string, number>
  ) {}
 
  get teacherName(): string {
    return this.section.teacherName;
  }
  get boardName(): string {
    return this.section.boardName;
  }
  get principalName(): string {
    return this.section.principalName;
  }
}
 
// Client: এখন শুধু একজন বন্ধুর সাথে কথা বলে
function printResultCard(student: Student): string {
  return `${student.name} | Teacher: ${student.teacherName} | Board: ${student.boardName} | Signed: ${student.principalName}`;
}
চিত্র ৮: result card printer Student-কে জিজ্ঞেস করে। Student চুপচাপ Section-কে জিজ্ঞেস করে। Section চুপচাপ School-কে জিজ্ঞেস করে। প্রতিটা object শুধু তার সরাসরি বন্ধুর সাথে কথা বলে।

এখানে একটা সৎ কথা বলা দরকার: Student-এ তিনটা আর ClassSection-এ তিনটা forwarding getter যোগ করেছি। এটা একটা cost। আগামীকাল কেউ যদি প্রতিটা student-এ আরও দশটা school fact দরকার করে, দুটো layer-এ সব forward করা হাস্যকর হয়ে যাবে। Hide Delegate হলো এমন structure লুকানোর জন্য যেটা client-দের জানা উচিত নয় — পুরো API layer-এ layer-এ tunnel করার জন্য নয়।

কলেজ কর্নার: এই cost-এর design literature-এ নাম আছে: delegation overhead। প্রতিটা forwarding method হলো code-এর একটা লাইন যেটা লিখতে হবে, পড়তে হবে, test করতে হবে, আর delegate-এর signature-এর সাথে sync রাখতে হবে। trade হলো এখন overhead বনাম পরে change amplification — Shotgun Surgery effect যেখানে একটা structural edit ডজন ডজন call-site edit-এ ছড়িয়ে পড়ে। সহজ হিসাব: N call sites-এ লেখা একটা chain প্রতি structure পরিবর্তনে N edit cost করে; একটা hidden hop cost করে ১টা edit। Hide Delegate জেতে যখন call sites forwarded operation-গুলোর চেয়ে বেশি — mature codebase-এ প্রায় সবসময়ই তাই হয়।

C#-এ একই refactoring

C# Hide Delegate-কে property দিয়ে খুব স্বাভাবিক করে তোলে। আগে:

public class Department
{
    public Person Manager { get; }
    public Department(Person manager) => Manager = manager;
}
 
public class Employee
{
    public Department Department { get; }
    public Employee(Department department) => Department = department;
}
 
// Client code, ছড়িয়ে ছিটিয়ে:
var manager = employee.Department.Manager;

পরে — delegate টা private field হয়, আর একটা এক-লাইন expression-bodied property call forward করে:

public class Employee
{
    private readonly Department _department;
 
    public Employee(Department department) => _department = department;
 
    public Person Manager => _department.Manager; // hop ভেতরে লুকানো
}
 
// Client code:
var manager = employee.Manager;

Step 5-এ C# compiler তোমার বন্ধু: public Department Department কে private field-এ বদলাও, build করো, আর প্রতিটা বাকি chain compile error হয়ে যাবে যেটা তুমি mechanically ঠিক করতে পারবে। বড় solution-এ এই "compiler-কে caller খুঁজে পেতে দাও" কৌশলটা অনেক নিরাপদ।

Python-এ একবার

Python-এ private keyword নেই, কিন্তু convention আর property দিয়ে একই refactoring কাজ করে:

class Employee:
    def __init__(self, department):
        self._department = department  # underscore = please treat as private
 
    @property
    def manager(self):
        return self._department.manager  # hop, লুকানো
 
# Client code:
print(employee.manager)        # ভালো — একজন বন্ধু, একটা প্রশ্ন
# print(employee._department)  # সম্ভব, কিন্তু প্রতিটা reader জানে এটা অনুপ্রবেশ

Python একটা দৃঢ় অনুপ্রবেশকারীকে থামাতে পারে না, কিন্তু underscore আর clean public property সঠিক path-কে সহজ path বানায়। Code review-তে client code-এ employee._department.manager দেখলে সাথে সাথে red flag।

তোমার codebase কি drift করছে? Dial পড়ো

কীভাবে মাপবে যে তোমার project এই refactoring দরকার? দুটো সহজ chart গল্প বলবে। প্রথমে track করো কতটা distinct file একই link-এর মধ্য দিয়ে chain ধরে। এই সংখ্যা বাড়তে থাকলে coupling ছড়ানোর শব্দ:

চিত্র ৯: release জুড়ে employee.department.x chain ধারণকারী file-এর সংখ্যা। chain কপি করা প্রতিটা নতুন file একটা ভবিষ্যত breakage।

দ্বিতীয়ত, কোন refactoring প্রয়োগ করবে সেটা decide করতে তোমার class এই map-এ রাখো। সর্বত্র long chain আর wrapper যেটা real meaning যোগ করবে? Hide Delegate। ইতিমধ্যে forward দিয়ে ভর্তি class যেগুলো কিছুই যোগ করে না? উল্টো পদক্ষেপ।

চিত্র ১০: সিদ্ধান্তের map। তোমার code কোথায় আছে সেটা dial কোন দিকে ঘোরাবে তা নির্ধারণ করে।

আমাদের গল্পে, result portal map-এর ডান দিকে শুরু হয়েছিল — স্পষ্ট Hide Delegate এলাকা। দুই বছর পরে উৎসাহী wrapping-এর পরে উপরের বামে চলে গিয়েছিল — তখন বিপরীত ওষুধের সময়।

Seesaw: Hide Delegate বনাম Remove Middle Man

এটাই এই পুরো আলোচনার সবচেয়ে গুরুত্বপূর্ণ শিক্ষা। Hide Delegate-এর একটা exact উল্টো আছে: Remove Middle Man। তারা একটা seesaw-এর দুই প্রান্তে।

  • Client code-এ অনেক বেশি chain — clients অনেক বেশি structure জানছে — Hide Delegate প্রয়োগ করো, forwarding যোগ করো।
  • একটা class-এ অনেক বেশি pure forwarding — class টা একটা ফাঁকা toll booth — Remove Middle Man প্রয়োগ করো, forwarding মুছে delegate expose করো।
চিত্র ১১: delegation dial। বাম প্রান্ত: সর্বত্র train wreck। ডান প্রান্ত: শুধু forward করা class। সুস্থ code মাঝখানে, আর তুমি এই দুটো উল্টো refactoring দিয়ে dial ঘোরাও।

কোনো স্থায়ী সঠিক উত্তর নেই — Fowler নিজেই বলেন সঠিক পরিমাণ hiding পরিবর্তন হয় যখন system পরিবর্তন হয়। গতকালের সহায়ক hidden hop আজকের অকেজো forward হতে পারে। মাঝখানের দিকে refactor করো, তুমি এখন যেদিকে হেলে আছ সেদিক থেকে।

পুরো ধারণাটা মাথায় ধরে রাখার জন্য একটা ছবি:

চিত্র ১২: mind map হিসেবে delegation dial — দুটো failure mode, দুটো cure, একটা balance point।

সুবিধা আর ঝুঁকি

সুবিধাকেন গুরুত্বপূর্ণ
Client একটা path নয়, একটা object-এর উপর নির্ভর করেServer-এর ভেতরের পরিবর্তন caller-দের ভাঙতে পারে না
Delegate private হতে পারেসত্যিকারের encapsulation — link আর public API নয়
Call sites intent হিসেবে পড়েemployee.manager বলে কী চাই, কীভাবে খুঁজে পাবে নয়
Structure স্বাধীনভাবে evolve করতে পারেDelegate বদলাও, মান ভিন্নভাবে derive করো — clients জানবেই না
ঝুঁকিকীভাবে সামলাবে
Server-এর interface forwarding method দিয়ে ভারী হয়শুধু যে chain clients সত্যিই ব্যবহার করে সেগুলোই লুকাও; পরে interface দেখো
অতিরিক্ত hiding Middle Man তৈরি করেForwarding method বনাম real ones-এর ratio দেখো
Rich access দরকার হলে বেকায়দা হয়ে যায়Client যদি অনেক delegate method দরকার করে, সব forward করার বদলে boundary নিয়ে ভাবো
Hidden hop পাঠকদের অবাক করতে পারেMethod-কে path নয়, তার meaning দিয়ে নাম দাও

কোন smell-গুলো এটা সারায়?

SmellHide Delegate কীভাবে সাহায্য করে
Message Chainsসরাসরি cure — a.b.c-কে একটা intention-revealing call-এ collapse করে
Inappropriate IntimacyClients server-এর internal collaborators জানা বন্ধ করে
Shotgun Surgery (structural changes)একটা structure পরিবর্তন একটা class edit করে, প্রতিটা call site নয়
Middle Manসতর্কতা — এটা সারায় না, বরং অতিরিক্ত Hide Delegate প্রয়োগ এটা তৈরি করে

দ্রুত revision box

+--------------------------------------------------------------+
|                    HIDE DELEGATE — CHEAT SHEET               |
+--------------------------------------------------------------+
| Smell to spot : client writes server.delegate.method()       |
| Move          : add server.method() that forwards internally |
| Then          : redirect callers one by one, test each time  |
| Finally       : make the delegate field PRIVATE              |
| Law obeyed    : Law of Demeter — talk to friends only        |
| Cures         : Message Chains, Inappropriate Intimacy       |
| Danger        : too many forwards -> Middle Man smell        |
| Opposite move : Remove Middle Man (same dial, other end)     |
| Safety rule   : tiny step -> run tests -> repeat             |
+--------------------------------------------------------------+

Practice exercise

ধরো এখানে chain-সহ একটা ছোট্ট library system আছে। নিজেই refactor করো।

class Rack {
  constructor(public hall: string, public floor: number) {}
}
 
class Book {
  constructor(public title: string, public rack: Rack) {}
}
 
class Member {
  constructor(public name: string, public borrowed: Book[]) {}
}
 
// Client code — তিনটা train wreck:
function whereIsMyBook(member: Member, title: string): string {
  const book = member.borrowed.find(b => b.title === title)!;
  return `Hall ${book.rack.hall}, floor ${book.rack.floor}`;
}
 
function overdueNotice(member: Member): string {
  const halls = member.borrowed.map(b => b.rack.hall).join(", ");
  return `${member.name}, please return books to: ${halls}`;
}

তোমার কাজ:

  1. Book-এর মধ্য দিয়ে Rack-এ পৌঁছানো প্রতিটা chain খোঁজো। কতটা call site book-rack link-এর উপর নির্ভর করে?
  2. Book-এ একটা location getter যোগ করো (যেমন "Hall A, floor 2" রিটার্ন করে) যাতে caller-রা আর সরাসরি rack না ছোঁয়। দুটো function একে একে redirect করো, পরিবর্তনের মাঝে test চালাতে থাকো।
  3. Book-এ rack private করো। সব কিছু কি এখনো compile হয়?
  4. Figure 10-এর নিজের version আঁকো আর এই library code-কে map-এ রাখো — তোমার পরিবর্তনের আগে আর পরে। তুমি কি এটাকে healthy quadrant-এর দিকে নিয়ে গেছ নাকি পার করে গেছ?
  5. Bonus: Member-কেও কি তার borrowed array findBook(title) মতো method-এর পেছনে লুকানো উচিত? নাকি এটা Member-কে Middle Man-এ পরিণত করতে শুরু করবে? তোমার পছন্দ defend করে একটা বাক্য লেখো — সেই বাক্যটাই সেই seesaw judgement যেটা প্রতিটা professional করে।

যখন তুমি ব্যাখ্যা করতে পারবে কেন তুমি যেখানে থামলে সেখানে থামলে, তখন তুমি সত্যিই Hide Delegate বুঝেছ। আর যখন তোমার hiding অনেক দূর চলে যায় — যখন একটা class হয়ে যায় শুধু একটা ভদ্র forwarding desk — পরের post অপেক্ষা করছে বিপরীত ওষুধ নিয়ে।

সচরাচর জিজ্ঞাসা

Hide Delegate রিফ্যাক্টরিং আসলে কী?
Hide Delegate মানে হলো একটা object-এ ছোট্ট একটা forwarding method যোগ করা, যাতে caller-রা আর সেই object-এর ভেতর দিয়ে অন্য object-এ না পৌঁছায়। employee.department.manager লেখার বদলে Employee-তে একটা manager getter দাও — সে নিজেই ভেতর থেকে department-কে জিজ্ঞেস করবে। caller তখন শুধু একটা object-এর সাথে কথা বলে, আর লুকানো object-টা (delegate) private থাকে।
Hide Delegate আর Law of Demeter-এর সম্পর্ক কী?
Law of Demeter বলে: শুধু তোমার সরাসরি বন্ধুদের সাথে কথা বলো, তাদের মাধ্যমে পৌঁছানো অপরিচিতদের সাথে নয়। a.getB().getC() type chain-এ আসলে অপরিচিতের সাথে কথা হচ্ছে — getB() যে object রিটার্ন করে সেটা। Hide Delegate হলো এই নিয়ম মানার ব্যবহারিক উপায়: প্রথম object নিজে ভেতরে দ্বিতীয় hop করে, caller কখনো অপরিচিতের সাথে দেখা করে না।
কখন Hide Delegate ব্যবহার করা ঠিক হবে না?
যখন caller-রা সত্যিই delegate-এর অনেক method দরকার মনে করে, তখন Hide Delegate ব্যবহার করো না। একে একে দশটা method forward করলে server ফুলে ওঠে আর ধীরে ধীরে Middle Man-এ পরিণত হয় — এমন একটা class যেটা শুধু call forward করে। ছোট, stable, local chain-গুলো ছেড়ে দাও; প্রতিটা dot সমস্যা না।
Hide Delegate কি Remove Middle Man-এর উল্টো?
হ্যাঁ, একদম। Hide Delegate message chain মারতে forwarding method যোগ করে। Remove Middle Man forwarding method মুছে ফেলে যখন একটা class শুধু forward করতেই থাকে। এগুলো একটা dial-এর দুই প্রান্ত। সুস্থ code মাঝখানে থাকে: যে chain-গুলো structure ফাঁস করে সেগুলো লুকাও, কিন্তু প্রতিটা call wrap করো না।
Hide Delegate কি behavior বা performance পরিবর্তন করে?
কোনো behavior পরিবর্তন হয় না — একই call হয়, শুধু call site-এর বদলে server-এর ভেতরে। বাড়তি method call প্রায় সব program-এ একেবারে নগণ্য। যা পরিবর্তন হয় সেটা হলো coupling: caller-রা আর path জানে না, তাই server তার ভেতরের কাঠামো নিজের মতো সাজাতে পারে কাউকে না ভেঙে।

আরো দেখো

সম্পর্কিত পাঠ

Message Chains: বন্ধুকে জিজ্ঞেস করো, সে কাজিনকে জিজ্ঞেস করে, কাজিন চাচাকে জিজ্ঞেস করে

Message Chains code smell শেখো একটা মজার গল্পের মাধ্যমে — রুটি আছে কিনা জানতে চারজন মানুষের মধ্য দিয়ে যেতে হয়। a.getB().getC().getD() লিখলে caller পুরো রাস্তার সাথে coupled হয়ে যায়। Law of Demeter কী, আর Hide Delegate দিয়ে কীভাবে chain ঠিক করতে হয় সেটা শেখো।

আরও পড়ুন

Middle Man: যে helper শুধু তোমার message পৌঁছে দেয়, নিজে কিছু করে না

Middle Man code smell টা বোঝো একটা school-এর সেই পিয়নের গল্প দিয়ে — যে শুধু চিরকুট বহন করে, নিজে কিছু যোগ করে না। যখন একটা class শুধু সব call forward করে, সেটা সরিয়ে দাও। কিন্তু Proxy, Facade, আর Adapter কেন জেনেশুনে middle man হয় — সেটাও জানো।

আরও পড়ুন

Remove Middle Man: পিয়ন শুধু ফরওয়ার্ড করলে, সরাসরি হেড স্যারের কাছে যাও

Remove Middle Man রিফ্যাক্টরিং শেখো একটা স্কুলের পিয়নের গল্প দিয়ে — যে প্রতিটা প্রশ্ন হেডমাস্টারের কাছে ফরওয়ার্ড করে, নিজে কিছু যোগ না করেই। যখন একটা class শুধু delegate-কে call ফরওয়ার্ড করে, তখন সেই ফরওয়ার্ডিং মুছে দাও আর client-দের সরাসরি delegate-এর সাথে কথা বলতে দাও। TypeScript আর C#-এ ধাপে ধাপে walkthrough।

আরও পড়ুন

Inappropriate Intimacy: দুটো class যারা একে অপরের রান্নাঘরে ঢুকে পড়ে

দুই প্রতিবেশীর গল্প দিয়ে Inappropriate Intimacy বোঝো — যারা একে অপরের রান্নাঘর সাজিয়ে দেয়। দুটো class যখন একে অপরের private অংশে হাত দেয়, তখন কেউ একা কিছু বদলাতে পারে না। Law of Demeter আর privacy ফিরিয়ে আনার refactoring শেখো।

আরও পড়ুন