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

Proxy Pattern: দারোয়ান যে মালিকের সাথে দেখা করার আগেই চেক করে

দারোয়ানের গল্প দিয়ে Proxy pattern শেখো। একটা real object-এর সামনে same interface-এর একটা stand-in রেখে সেই object-এ access নিয়ন্ত্রণ করো।

22 মিনিট আপডেট: June 11, 2026beginner
design-patternsstructural-patternsproxylazy-loadingaccess-controltypescriptcsharp

🚪 বাড়ির গেটে দারোয়ান

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

আজকে চৌদ্দ বছরের সুমাইয়া এলো। তার স্কুল বার্ষিক ফেয়ারের জন্য ডোনেশন সংগ্রহ করছে। কেউ বলেছে জামাল সাহেব উদারভাবে দেন। দেখো রহিম ভাই কী করলেন। সুমাইয়া বলল, "জামাল সাহেবের সাথে দেখা করতে চাই।" রহিম ভাই নাম আর উদ্দেশ্য জিজ্ঞেস করলেন। ভিজিটর লিস্ট চেক করলেন। স্কুলের ডোনেশন কালেক্টর approved list-এ আছে — তাই রেজিস্টারে entry time লিখে তারপর ভেতরে খবর পাঠালেন।

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

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

সবচেয়ে গুরুত্বপূর্ণ বিষয়টা লক্ষ্য করো: ভিজিটরের কাছে গেটটাই বাড়ি। তুমি গেটে ঠিক সেভাবেই request করো যেভাবে মালিককে করতে। একই কথা, একই request। কিন্তু সেই একই মুখের পেছনে দারোয়ান তিনটা জিনিস ঠিক করেন: তোমার request ভেতরে যাবে কিনা, কখন যাবে, আর সেটা নিয়ে কী লেখা হবে।

দারোয়ান কখনো মালিকের কাজ করেন না। কখনো চুক্তিতে সই করেন না। তিনি শুধু access নিয়ন্ত্রণ করেন সেই মানুষটার কাছে যিনি আসল কাজ করেন।

Software-এ এই দারোয়ানকে বলা হয় Proxy। এটা একটা real object-এর সামনে দাঁড়ায়, same interface পরে, আর প্রতিটা call intercept করে — চেক করে, delay করে, রেকর্ড করে, বা refuse করে — real object-কে ছোঁয়ার আগে।

গেটে দুই ভিজিটরের journey

Journey-এর শেষ লাইনটা পড়ো। মালিকের দিন পারফেক্ট কারণ গেট দুটো ভিজিটই absorb করেছে — একটা allowed, একটা refused — তিনি একটু নড়াচড়া না করেই। সেই নিরবচ্ছিন্ন মালিকটাই তোমার ভারী, মূল্যবান real object।

Proxy Pattern কী?

Proxy হলো একটা structural design pattern। এটা একটা real object-এর সামনে একটা stand-in object রাখে। Proxy same interface implement করে real object-এর মতো, তাই client code পার্থক্য ধরতে পারে না। যেহেতু প্রতিটা call proxy-র মধ্য দিয়ে যায়, proxy অনেক কিছু করতে পারে call forward করার আগে, পরে, বা পরিবর্তে:

  • ভারী real object তৈরি delay করতে পারে যতক্ষণ না সত্যিই দরকার হয় — এটাই virtual proxy / lazy loading
  • কে call করছে চেক করতে পারে আর forbidden request refuse করতে পারে — এটাই protection proxy / access control
  • বারবার জিজ্ঞেস করা প্রশ্নের উত্তর মনে রাখতে পারে — এটাই caching proxy
  • প্রতিটা call-এর রেজিস্টার লিখতে পারে — এটাই logging proxy
  • অন্য machine-এ থাকা object-এ network-এ call পৌঁছে দিতে পারে — এটাই remote proxy

সংজ্ঞাটা ছোট: same interface, controlled access। Proxy কখনো বদলায় না object কী করে। এটা বদলায় কখন, কীভাবে, আর কোন condition-এ real কাজটা হবে।

💡

Proxy চেনার quick test: wrapper কি real জিনিসের exact same methods offer করে, আর এটা কি মূলত decide করে whether বা when call real জিনিসে পৌঁছাবে? তাহলে এটা proxy। যদি অনেক object-এর উপর নতুন, সহজ methods offer করে — তাহলে এটা Facade। যদি এমন behaviour যোগ করে যেটা client ইচ্ছা করে stack করে — তাহলে এটা Decorator।

Pattern-এর পুরনো নাম হলো Surrogate — একটা substitute যেটা real subject-এর হয়ে কাজ করে।

চারটা classic দারোয়ানের দায়িত্ব, পাশাপাশি:

Proxy typeকী নিয়ন্ত্রণ করেরহিম ভাই versionSoftware example
Virtualকখন real object তৈরি হবেশুধু real কাজ থাকলে মালিককে ডাকেPhoto first view-এ load হয়; ORM lazy fields
Protectionকে ঢুকতে পারবেভিজিটর লিস্ট চেক করে, সেলসম্যান ফেরায়Admin-only delete; role check
Remoteকোথায় real object আছেশহরের বাইরের মালিকের কাছে message পাঠায়gRPC stubs, RMI, API clients
Smartকী রেকর্ড বা reuse হবেরেজিস্টার লেখে, জানা উত্তর বলেCaching, logging, reference counting

College corner — চারটা classic type, formally: একটা virtual proxy creation time manage করে। এটা real subject build করতে যা লাগে তা রাখে (একটা path, একটা ID) আর first use পর্যন্ত ব্যয়বহুল constructor defer করে — lazy initialization object-এ wrapped। একটা protection proxy authority manage করে। Forward করার আগে caller-এর rights evaluate করে, domain class-কে authorization থেকে মুক্ত রাখে। একটা remote proxy location manage করে। Real subject অন্য process বা machine-এ, proxy wire-এ arguments marshal করে local-call illusion বজায় রেখে (এভাবেই gRPC stubs, RMI, আর পুরনো CORBA কাজ করে — আর কেন একটা "method call" হঠাৎ network timeout throw করতে পারে)। একটা smart proxy bookkeeping manage করে: caching, logging, metrics, reference counting। Real framework সাধারণত runtime-এ এগুলো generate করে। Java-র java.lang.reflect.Proxy, .NET-এর Castle DynamicProxy (Moq আর EF ব্যবহার করে), আর JavaScript-এর built-in Proxy object — সবাই interface description থেকে তোমার জন্য watchman তৈরি করে।

সমস্যাটা কী?

ধরো সুমাইয়ার স্কুলের app-এ ১,০০০ বার্ষিকী অনুষ্ঠানের ছবির gallery আছে। প্রতিটা ছবি high resolution। একটা object তৈরি মানে disk থেকে megabytes পড়া আর decode করা:

// The heavy real object — expensive the moment it is constructed
class HighResPhoto {
  private pixels: Uint8Array;
 
  constructor(private path: string) {
    console.log(`Loading ${path}... (slow! megabytes from disk)`);
    this.pixels = new Uint8Array(5_000_000); // pretend decode
  }
 
  display(): void {
    console.log(`Displaying ${this.path}`);
  }
}
 
// Gallery start-up — the naive way
const photos = paths.map((p) => new HighResPhoto(p)); // loads ALL 1000!

User gallery খুলল, আর app আধা মিনিট freeze হয়ে গেল — gigabytes RAM খেল হাজারটা ছবি load করতে। অথচ user scroll করে হয়তো দশটাই দেখবে। তুমি প্রতিটা object-এর পুরো cost আগেই দিচ্ছ, যে object কখনো use-ই নাও হতে পারে। এটা যেন জামাল সাহেব সারাদিন গেটে নিজে দাঁড়িয়ে থাকেন, কেউ আসতে পারে বলে।

তুমি gallery code-এ if (!loaded) load(); check ছড়িয়ে দিতে পারো। কিন্তু তাতে loading logic প্রতিটা call site-এ ছড়িয়ে পড়বে। আর একই ধরনের সমস্যা বারবার অন্য রূপে আসে:

  • শুধু principal-এর account ছবি delete করতে পারবে — কিন্তু photo class-এর login সম্পর্কে জানার দরকার নেই।
  • তুমি log রাখতে চাও কে কী দেখল — photo class না বদলেই।
  • ছবিগুলো server-এ থাকতে পারে — কিন্তু gallery সেগুলো local object-এর মতো call করতে চায়।

প্রতিটা ক্ষেত্রে তুমি চাও object-এর চারপাশে behaviour যোগ করতে, object না ছুঁয়ে আর client না জানিয়ে।

সব কিছু আগেই load করা বনাম proxy যেটা চাহিদামতো load করে

এখানে startup-time পার্থক্য দেখো। Naive line প্রতিটা ছবির সাথে বাড়ে। Proxy line flat থাকে কারণ একটা watchman তৈরি প্রায় কিছুই cost করে না:

ছবির সংখ্যা বাড়ার সাথে gallery startup time

একটা typical session-এ দেখো কতটুকু কাজ আসলে দরকার ছিল:

এক session-এ ১০০০ photo proxy-এর পরিণতি

প্রায় ৯৯% ভারী object কখনো তৈরিই হলো না। সেই বিশাল "never loaded" অংশটাই virtual proxy-এর পুরো কাজ। Lazy loading তখনই জেতে যখন অনেক তৈরি হয় কিন্তু কম ব্যবহার হয় — gallery, feed, ORM relation, plugin system।

⚙️ কীভাবে কাজ করে, ধাপে ধাপে

  1. Subject interface define করো যেটা client call করে — display(), remove()। Real class-এ যদি এখনো interface না থাকে, একটা extract করো।
  2. RealSubject যেমন আছে তেমনই রাখো। HighResPhoto real, ভারী কাজ করতে থাকে। এটার কোনো edit দরকার নেই — এটাই অর্ধেক সৌন্দর্য।
  3. Same interface implement করে Proxy class তৈরি করো। এটা রাখে real subject পরে পেতে যা লাগে — এখানে শুধু file path — আর একটা reference যেটা null দিয়ে শুরু।
  4. প্রতিটা method "extra work + delegate" দিয়ে implement করো। Access check করো। Allowed হলে, first use-এ lazily real subject build করো, তারপর call forward করো। Refuse, log, বা cache থেকে উত্তর দাও।
  5. Client-কে real object-এর বদলে proxy দাও। Factory বা wiring code এটা করে। Client শুধু Subject interface-এর বিরুদ্ধে program করে আর জানতেই পারে না।
Proxy pattern-এর class structure

Diagram-এর মূল লাইনটা নিচের: Gallery শুধু Photo interface-এর উপর নির্ভর করে। এটা watchman না মালিক ধরছে — বলতে পারে না। আর সেটাই মূল কথা।

একটা proxy-এর পেছনে real subject-এর lazy জীবন একটা ছোট state machine। এটা মুখস্ত করা ভালো কারণ এটা pattern-এর সবচেয়ে বড় জয় (সস্তা শুরু) আর সবচেয়ে বড় অবাক করা বিষয় (প্রথম call slow) দুটোই ব্যাখ্যা করে:

একটা virtual proxy-এর পেছনে real object-এর lifecycle

দুটো detail দ্বিতীয়বার দেখার দরকার। NotLoaded → NotLoaded self-loop হলো protection proxy কাজে: একটা denied request গেট থেকে bounce করে আর ব্যয়বহুল Loading state-এ কখনো ঢোকা হয় না। আর single Loading transition হলো যেখানে hidden first-call delay থাকে — cost উধাও হয়নি, এটা প্রথম real use-এ সরে গেছে।

College corner — lazy initialization, সঠিকভাবে: proxy-এর if (real == null) real = new RealSubject() লাইন single-threaded code-এ ঠিক আছে। কিন্তু multi-threaded server-এ দুটো thread null check-এর মধ্য দিয়ে race করতে পারে আর দুইবার ভারী object build করতে পারে। আরও খারাপ হতে পারে — একটা thread অর্ধেক-তৈরি object দেখতে পারে। Classic fix: পুরো accessor lock করো (সহজ, সামান্য cost), double-checked locking with volatile field (fast, সঠিক করা ঝামেলা), বা language-level lazy holders — C#-তে Lazy<T>, Kotlin-এ lazy val, Java-তে initialization-on-demand holder idiom। Coursework আর interview-এর rule of thumb: বলো "hand-rolling double-checked locking-এর বদলে platform-এর lazy primitive ব্যবহার করতাম।" তাহলে মনে হবে তুমি আগে পুড়েছ।

বাস্তব কোড উদাহরণ

TypeScript-এ স্কুলের gallery, দুটো সবচেয়ে useful proxy কাজ একসাথে: lazy loading আর access control (bonus হিসেবে visitor register সহ)।

// ----- The Subject interface: what every "photo" must offer -----
interface Photo {
  display(user: string): void;
  remove(user: string): void;
}
 
// ----- The RealSubject: heavy, honest, knows nothing of security -----
class HighResPhoto implements Photo {
  private pixels: Uint8Array;
 
  constructor(private path: string) {
    // Imagine megabytes read and decoded here.
    console.log(`  (slow) Loading ${this.path} from disk...`);
    this.pixels = new Uint8Array(5_000_000);
  }
 
  display(user: string): void {
    console.log(`  Showing ${this.path} (${this.pixels.length} bytes)`);
  }
 
  remove(user: string): void {
    console.log(`  ${this.path} deleted from disk.`);
  }
}
 
// ----- The Proxy: same interface, watchman duties -----
class PhotoProxy implements Photo {
  private real: HighResPhoto | null = null;  // LAZY: not loaded yet!
 
  constructor(
    private path: string,
    private admins: string[],                // who may delete
  ) {}
 
  // Virtual proxy part: build the real object only on first need.
  private getReal(): HighResPhoto {
    if (this.real === null) {
      this.real = new HighResPhoto(this.path);
    }
    return this.real;
  }
 
  display(user: string): void {
    console.log(`[Watchman] ${user} wants to view ${this.path}. Noted in register.`);
    this.getReal().display(user);            // load now (if not already), then delegate
  }
 
  // Protection proxy part: check before letting the call through.
  remove(user: string): void {
    if (!this.admins.includes(user)) {
      console.log(`[Watchman] DENIED. ${user} cannot delete ${this.path}.`);
      return;                                 // real object never disturbed
    }
    console.log(`[Watchman] ${user} is admin. Allowing delete.`);
    this.getReal().remove(user);
  }
}
 
// ----- The client: builds 1000 photos in a blink -----
const admins = ["principal"];
const gallery: Photo[] = [];
 
console.log("Opening gallery with 1000 photos...");
for (let i = 1; i <= 1000; i++) {
  gallery.push(new PhotoProxy(`annual-day-${i}.jpg`, admins));
}
console.log("Gallery open instantly! Nothing loaded yet.\n");
 
// Riya views just two photos — only those two load.
gallery[0].display("riya");
gallery[0].display("riya");     // second view: already loaded, no slow line
gallery[41].display("arjun");
 
console.log("");
 
// Access control in action.
gallery[0].remove("riya");          // student — denied
gallery[0].remove("principal");     // admin — allowed

Output:

Opening gallery with 1000 photos...
Gallery open instantly! Nothing loaded yet.
 
[Watchman] riya wants to view annual-day-1.jpg. Noted in register.
  (slow) Loading annual-day-1.jpg from disk...
  Showing annual-day-1.jpg (5000000 bytes)
[Watchman] riya wants to view annual-day-1.jpg. Noted in register.
  Showing annual-day-1.jpg (5000000 bytes)
[Watchman] arjun wants to view annual-day-42.jpg. Noted in register.
  (slow) Loading annual-day-42.jpg from disk...
  Showing annual-day-42.jpg (5000000 bytes)
 
[Watchman] riya cannot delete annual-day-1.jpg. DENIED.
[Watchman] principal is admin. Allowing delete.
  (slow) ... already loaded ...
  annual-day-1.jpg deleted from disk.

তিনটা জয় trace করো:

  1. Lazy loading। আমরা ১,০০০ photo object তৈরি করলাম, কিন্তু slow "Loading..." line print হলো মাত্র দুইবার — যে দুটো ছবি আসলে দেখা হয়েছে সেগুলোর জন্য। ৯৯৮টা ভারী object কখনো তৈরিই হলো না।
  2. Reuse। সুমাইয়ার দ্বিতীয় view-এ কোনো loading line নেই। Proxy আগে-তৈরি real object রেখে দিয়েছিল আর শুধু delegate করল।
  3. Access control। সুমাইয়ার delete গেটেই refuse হলো — real photo object ছোঁয়াই হলো না। Principal-এর delete পাস হলো। আর HighResPhoto-তে security code শূন্য — এটা clean আর reusable থাকল।

এখানে সুমাইয়ার দুটো view sequence হিসেবে — প্রথমটা loading cost দেয়, দ্বিতীয়টা cache থেকে serve হয়:

প্রথম view load করে, দ্বিতীয় view cache থেকে serve হয়

এই সবকিছু, আর gallery code কিছুই শিখল না — এটা Photo reference ধরে আর সারাক্ষণ একই দুটো method call করে।

C#-তে একই ধারণা

Virtual + protection proxy-এর একটা compact C# version:

public interface IPhoto
{
    void Display(string user);
    void Remove(string user);
}
 
// Heavy real subject
public class HighResPhoto : IPhoto
{
    private readonly string _path;
    public HighResPhoto(string path)
    {
        _path = path;
        Console.WriteLine($"  (slow) Loading {_path}...");
    }
    public void Display(string user) => Console.WriteLine($"  Showing {_path}");
    public void Remove(string user)  => Console.WriteLine($"  {_path} deleted");
}
 
// The watchman
public class PhotoProxy : IPhoto
{
    private readonly string _path;
    private readonly HashSet<string> _admins;
    private HighResPhoto? _real;                       // lazy: null until needed
 
    public PhotoProxy(string path, IEnumerable<string> admins)
        => (_path, _admins) = (path, new HashSet<string>(admins));
 
    private HighResPhoto Real => _real ??= new HighResPhoto(_path);
 
    public void Display(string user)
    {
        Console.WriteLine($"[Watchman] {user} viewing {_path}. Logged.");
        Real.Display(user);                            // load on first use
    }
 
    public void Remove(string user)
    {
        if (!_admins.Contains(user))
        {
            Console.WriteLine($"[Watchman] DENIED for {user}.");
            return;                                    // owner never disturbed
        }
        Real.Remove(user);
    }
}
 
// Client
IPhoto photo = new PhotoProxy("annual-day-1.jpg", new[] { "principal" });
photo.Display("riya");        // loads now, then shows
photo.Remove("riya");         // denied at the gate
photo.Remove("principal");    // allowed

??= operator lazy initialization-কে one-liner করে দেয়: _real এখনো null থাকলেই real subject তৈরি করো। Multi-threaded server-এ Lazy<HighResPhoto> ব্যবহার করো — উপরে College corner দেখো।

🐍 Python flavor: স্কুলের internet watchman

ধরো সুমাইয়ার স্কুলের computer lab-এ একটা আলাদা গেট আছে — internet connection। Lab-এর proxy game site block করে আর page cache করে যাতে ত্রিশ জন student একই syllabus page খুললে ত্রিশবার fetch না হয়:

import time
 
class WebSite:
    """Subject interface in spirit: open(url) -> page text."""
    def open(self, url):
        raise NotImplementedError
 
class RealWebSite(WebSite):
    """RealSubject: actually fetches over the network. Slow."""
    def open(self, url):
        print(f"  (slow) Fetching {url} over the network...")
        time.sleep(1)                        # pretend network delay
        return f"<html>content of {url}</html>"
 
class SchoolInternetProxy(WebSite):
    """Watchman: blocklist (protection) + page cache (caching)."""
    BLOCKLIST = {"gamezone.com", "timepass.io"}
 
    def __init__(self):
        self._real = RealWebSite()           # could itself be lazy
        self._cache = {}                     # url -> page
 
    def open(self, url):
        if url in self.BLOCKLIST:
            print(f"[Watchman] DENIED: {url} is blocked in school.")
            return None                      # real site never contacted
        if url in self._cache:
            print(f"[Watchman] {url} served from cache, instant.")
            return self._cache[url]
        page = self._real.open(url)          # first time: pay the cost
        self._cache[url] = page              # remember for next student
        return page
 
lab = SchoolInternetProxy()
lab.open("gamezone.com")                     # blocked at the gate
lab.open("ncert.nic.in/syllabus")            # slow fetch, then cached
lab.open("ncert.nic.in/syllabus")            # instant, from cache
[Watchman] DENIED: gamezone.com is blocked in school.
  (slow) Fetching ncert.nic.in/syllabus over the network...
[Watchman] ncert.nic.in/syllabus served from cache, instant.

তিন লাইন output, তিনটা proxy duty: refuse, fetch-once, serve-from-register। Real corporate আর স্কুল network ঠিক এটাই করে — তারা machine-কে "proxy server" বলেও ডাকে।

Real software-এ কোথায় দেখবে

Proxy হলো real system-এ সবচেয়ে বেশি ব্যবহৃত pattern-গুলোর একটা — প্রায়ই framework তোমার জন্য generate করে।

  • JavaScript-এর built-in Proxy object। Language নিজেই pattern ship করে। new Proxy(target, handler) যেকোনো object-এ property read, write, আর function call intercept করতে দেয়। Vue 3-এর পুরো reactivity system এর উপর built — proxy-এর মাধ্যমে property পড়লে dependency record হয়, proxy দিয়ে লিখলে UI update trigger হয়।
  • ORM lazy loading। Hibernate (Java) আর Entity Framework (C#) real database entity-র বদলে proxy object দেয়। Order চাইলে এর customer field একটা proxy ধরে। Customer-এর SQL query fire হয় শুধু তখন যখন তুমি আসলে order.customer.name touch করো। এটা runtime-এ generated virtual + remote proxy।
  • API gateway। Kong, AWS API Gateway, আর similar product internet scale-এ proxy হিসেবে কাজ করে। Authentication, rate limiting, logging, আর routing-এর জন্য প্রতিটা request এদের মধ্য দিয়ে যায় real backend service-এ পৌঁছানোর আগে। Caller-দের কাছে same interface (HTTP), পেছনে full gate control।
  • Virtual scrolling আর image lazy-loading। Photo app আর infinite feed সস্তা placeholder দেখায় আর ভারী media তখনই load করে যখন item scroll করে view-এ আসে। ঠিক আমাদের gallery example, প্রতিটা social app-এ shipped।
  • RPC stub আর gRPC client। তুমি locally যে client object call করো সেটা একটা remote proxy। এটা তোমার argument serialize করে, network-এর উপর পাঠায়, আর reply deserialize করে। তোমার code দেখতে plain method call-এর মতো।
  • Open-source reference। java-design-patterns proxy example-এ একটা wizard tower আছে যেটা শুধু তিনজন wizard admit করে — একটা tiny protection proxy যেটা তুমি নিজেই run করতে পারো।

🧭 কখন use করবে আর কখন না

পরিস্থিতিProxy use করবে?
একটা ভারী object শুধু সত্যিই দরকার হলে তৈরি হবে (অনেক তৈরি, কম use)হ্যাঁ — virtual proxy
Real class না দূষিত করে permission check দরকারহ্যাঁ — protection proxy
Real object অন্য process বা machine-এহ্যাঁ — remote proxy
Subject edit না করে call-এর চারপাশে logging, metrics, বা caching চাওহ্যাঁ — smart proxy
Object build করা সস্তা আর সবার কাছে openনা — proxy শুধু ceremony হবে
Object-কে সহজ, আলাদা interface দিতে চাওনা — এটা Facade
Client সচেতনভাবে multiple add-on behaviour stack করবেনা — এটা Decorator
Hidden first-call delay অগ্রহণযোগ্য (hard real-time path)সতর্ক থাকো — lazy loading কখন slow কাজ হবে সেটা লুকায়

একটা map হিসেবে একই decision। ভারী object যাদের gate control দরকার সেগুলো top-right-এ:

সামনে watchman রাখবে কি?

দুটো half-fit লক্ষ্য করো। একটা সস্তা object যেটার তবুও permission check দরকার (top-left) শুধু pattern-এর protection অর্ধেক চায়। একটা ভারী object যেটা সবার জন্য open (bottom-right) শুধু virtual অর্ধেক চায়। পুরো watchman — check করা, lazy-loading, আর logging — তার চেয়ার earn করে যখন দুটো axis-ই উঁচু।

Students যে ভুলগুলো করে

⚠️

ভুল ১: Proxy-তে আলাদা interface। Client-কে যদি subject-এর display()-এর বদলে proxy.loadAndDisplay() call করতে হয়, substitutability মরে যায়। Real object-এর জন্য লেখা code proxy accept করতে পারবে না। Same interface হলো pattern-এর প্রথম আইন।

ভুল ২: Lazy object cache করতে ভুলে যাওয়া। display()-এর ভেতরে new HighResPhoto(path) লেখা null check ছাড়া মানে প্রতিটা single view-এ ছবি reload হবে। একবার build করো, store করো, reuse করো।

ভুল ৩: Proxy-তে business logic ঢুকে যাওয়া। Watchman নাম চেক করে — চুক্তি সই করে না। যদি তোমার proxy ছবি resize করা বা price compute করা শুরু করে, সেই কাজ real subject-এ (বা decorator-এ) যায়, গেটে না।

ভুল ৪: Stale cache। একটা caching proxy যেটা কখনো invalidate করে না সে আনন্দের সাথে real subject বদলে যাওয়ার পরেও গতকালের data serve করবে। Cache যোগ করার দিনই invalidation rule ঠিক করো, প্রথম bug report-এর পরে না।

ভুল ৫: Debugging blindness। Proxy client-এর কাছে অদৃশ্য। তাই "এই call কেন slow বা denied বা stale?" — এই প্রশ্নের উত্তর প্রায়ই একটা ভুলে-যাওয়া proxy-তে গিয়ে শেষ হয়। Behaviour ভূতুড়ে মনে হলে আগে জিজ্ঞেস করো: কোনো কিছু কি এই object wrap করছে?

Cousin-দের সাথে তুলনা

Proxy, Decorator, আর Adapter হলো তিনটা বিখ্যাত wrapper। Facade চতুর্থ lookalike হিসেবে যোগ দেয়। একবার sort করো, চিরকাল মনে রাখো:

প্রশ্নProxyDecoratorAdapterFacade
Client-এ interfaceSubject-এর sameSubject-এর sameDifferent (converted)New, simpler — অনেক object-এর উপর
মূল উদ্দেশ্যAccess control (lazy, permissions, cache, remote)Behaviour যোগঅমিল জিনিস fit করানোSubsystem সহজ করা
Wrapper কে manage করে?Framework বা wiring — client প্রায়ই জানে নাClient, ইচ্ছাকৃতভাবেIntegration codeApplication architecture
Layer-এ stack হয়?সাধারণত singleহ্যাঁ, by designসাধারণত singleসাধারণত single
এক কথায়GatekeeperToppingPlug converterFront desk
চারটা wrapper, চারটা intention

Proxy–Decorator pair একটু বেশি বলার দরকার কারণ তাদের structure identical: দুটোই subject-এর interface implement করে আর সেটার reference ধরে। পার্থক্য হলো intent আর control। Decorator-এর client বলে, "আমার object-কে পনির দিয়ে, তারপর মাখন দিয়ে wrap করো" — এটা জেনেশুনে stack build করে, enhance করতে। Proxy system দিয়ে subject-এর সামনে রাখা হয়, প্রায়ই নিজেই subject তৈরি করে আর own করে, guard আর manage করতে। Topping বনাম gatekeeper।

এই article-এর সব কিছু, একটা গাছে:

Proxy pattern-এর পুরো mind map

Quick revision box

+--------------------------------------------------------------+
|                    PROXY — QUICK REVISION                     |
+--------------------------------------------------------------+
| Idea      : A stand-in object with the SAME interface that   |
|             controls access to the real object.              |
| Nickname  : Surrogate                                        |
| Analogy   : Watchman at the gate — checks your name, logs    |
|             your visit, may refuse, owner stays undisturbed. |
| Motto     : Same interface, controlled access.               |
| 4 types   : Virtual (lazy load) | Protection (permissions)   |
|             Remote (other machine) | Smart (cache/log/count) |
| Key moves : real = null at start; build on first use; check  |
|             access BEFORE delegating; cache results.         |
| Lifecycle : NotLoaded -> Loading (once) -> Cached -> Cached  |
| Wins      : Instant startup, clean subject class, security   |
|             and logging without touching business logic.     |
| Watch out : First call can be surprisingly slow; stale       |
|             caches; do not change the interface!             |
| Cousins   : Decorator adds (client stacks it);               |
|             Facade simplifies many; Adapter converts one.    |
| Real life : JS Proxy object (Vue 3), Hibernate/EF lazy       |
|             entities, API gateways, gRPC client stubs        |
+--------------------------------------------------------------+

Practice exercise 📝

  1. ATM card proxy। তোমার bank account হলো real subject — withdraw(amount) আর checkBalance() আছে। একটা AtmCard proxy build করো যেটা (a) এক হাজার টাকার বেশি single withdrawal block করে (access control), (b) first use-এই শুধু account-এ connect করে (lazy loading), আর (c) প্রতিটা operation-এর জন্য mini-statement line print করে (logging)। Client শুধু shared IAccount interface use করবে।
  2. Internet watchman, extended। উপরের Python SchoolInternetProxy নাও আর দুটো জিনিস যোগ করো: (a) প্রতি-student visit counter যেটা ২০টা page open-এর পর warning print করে, আর (b) cache invalidation — ৬০ সেকেন্ডের বেশি পুরনো যেকোনো cached page আবার fetch করতে হবে। দুটো behaviour prove করে এমন output দেখাও।
  3. Challenge — proxy নাকি decorator? Exercise 1 থেকে তোমার AtmCard নাও আর একটা SmsAlertDecorator যোগ করো যেটা প্রতিটা সফল withdrawal-এর পরে SMS message পাঠায়, proxy-এর চারপাশে wrapped। তিনটা sentence লেখো কেন SMS wrapper একটা decorator কিন্তু card নিজেই একটা proxy — যদিও দুটোই same interface wrap করে।
  4. College challenge — lifecycle আঁকো। Exercise 1-এর জন্য card-এর পেছনে bank connection-এর NotLoaded → Loading → Cached state machine আঁকো। Diagram-এ ঠিক চিহ্নিত করো তিনটা duty (block, lazy connect, log) কোথায় fire করে। তারপর দুটো sentence লেখো — দুটো thread একই card একসাথে use করলে কী বদলায়।

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

সহজ ভাষায় Proxy pattern কী?
Proxy হলো একটা stand-in object যেটা real object-এর সামনে দাঁড়িয়ে থাকে। Same interface থাকায় client বুঝতে পারে না পার্থক্য। কিন্তু এই stand-in permission check করতে পারে, creation delay করতে পারে, উত্তর cache করতে পারে, বা real object-এ কাজ পাঠানোর আগে call log করতে পারে।
Proxy-এর প্রধান ধরনগুলো কী কী?
Virtual proxy মানে lazy loading — ভারী object দরকার হলে তখন বানাও। Protection proxy মানে access control — কে ঢুকতে পারবে সেটা ঠিক করো। Remote proxy মানে real object অন্য machine-এ থাকে। Smart proxy মানে caching, logging বা reference counting।
দুটোই object wrap করলে Proxy আর Decorator-এর পার্থক্য কী?
Structure একই, কিন্তু উদ্দেশ্য আলাদা। Decorator behaviour যোগ করে আর client ইচ্ছা করেই stack করে। Proxy access নিয়ন্ত্রণ করে — lazy creation, permissions, caching — সাধারণত client না জেনেই real object-এর lifecycle manage করে।
Proxy-তে lazy loading কী?
Proxy ভারী real object তৈরি করা delay করে যতক্ষণ না কেউ সেটার দরকারি method call করে। হাজারটা proxy তৈরি করা প্রায় বিনামূল্যে। শুধু যে কয়টা আসলে use হয় সেগুলোই real object বানায়।
Real software-এ proxy কোথায় দেখবো?
JavaScript-এর built-in Proxy object, Hibernate আর অন্যান্য ORM যেগুলো database entity lazy-load করে, authentication আর rate limiting করা API gateway, আর virtual scrolling যেটা শুধু screen-এ আসলেই image load করে।

আরো দেখো

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