Prototype Pattern: জিরো থেকে না বানিয়ে ফটোকপি করো
বিয়ের কার্ডের দোকানের গল্প দিয়ে Prototype design pattern শেখো — TypeScript আর Python-এর সহজ উদাহরণ, আর shallow vs deep copy-এর পরিষ্কার demo সহ।
বিয়ের কার্ডের দোকানের গল্প
ধরো রহিম দশম শ্রেণিতে পড়ে। তার বড়ো আপু ফাতেমা আপু-র বিয়ে হচ্ছে ডিসেম্বরে। ফাতেমা আপু বিয়ে করছেন তারিক ভাইয়া-কে, আর পুরো বাড়ি উত্তেজনায় উলটপালট। রহিমের আব্বা তাকে একটাই কাজ দিলেন: "বাজারে করিম চাচার প্রিন্টিং দোকানে যা। আমাদের ৫০০টা বিয়ের কার্ড লাগবে।"
রহিম দোকানে গেল। করিম চাচা বিশ বছর ধরে এই ছোট্ট প্রিন্টিং আর ফটোকপির দোকান চালাচ্ছেন। তাঁর ছেলে রুবেল কলেজের পরে সাহায্য করে। এখন একটু ভাবো — করিম চাচা কি প্রতিটা কার্ড আলাদা করে ডিজাইন করেন? ৫০০ বার কাগজ বাছেন? ৫০০ বার ফুলের বর্ডার আঁকেন? পরিবারের নাম, ভেন্যু, তারিখ — এগুলো ৫০০ বার টাইপ করেন?
অবশ্যই না! সেটা করতে মাসের পর মাস লেগে যেত। আর ৩৮৭ নম্বর কার্ডে গিয়ে নিশ্চিত "তারিক"-কে "তারেক" লিখে ফেলতেন।
বরং করিম চাচা একটা চালাক কাজ করেন:
- তিনি একটা নিখুঁত sample কার্ড তৈরি করেন। রহিমের পুরো পরিবার সেটা মনোযোগ দিয়ে দেখে — নাম, তারিখ, ভেন্যু, "সানরাইজ গার্ডেন, ঢাকা"-র বানান, সবকিছু।
- সবাই "হ্যাঁ, এটা পারফেক্ট!" বললে, তিনি শুধু সেই sample-এর কপি বানান। মেশিন বোরিং কাজটা করে দেয়।
- কিছু বিশেষ কার্ডের জন্য — বরের পরিবারের জন্য, VIP অতিথিদের জন্য — রুবেল একটা কপি নিয়ে এক-দুটো ছোট জিনিস বদলে দেয়। হয়তো সোনালি বর্ডার। হয়তো উপরে হাতে লেখা নাম।
এটাই হলো Prototype pattern। তুমি প্রতিটা object শূন্য থেকে বানাও না। একটা নিখুঁতভাবে তৈরি object রেখে দাও — prototype, যেমন sample কার্ড — আর নতুন একটা দরকার হলে সেটার কপি বানাও। কপিতে ছোট পরিবর্তন দরকার হলে শুধু সেই অংশটুকু বদলাও।
একই আইডিয়া দোকানের কোণের ফটোকপি মেশিনেও আছে। রহিমের যখন একটা ভরা school form-এর দশটা কপি দরকার, সে দশ বার হাতে লেখে না। একটা ভরা form মেশিনে দেয়, মেশিন বিশ্বস্ত কপি দিয়ে দেয়। মেশিনকে form-টা বুঝতে হয় না। শুধু কপি করতে হয়।
রহিমের পুরো সকালটা এভাবে দেখো:
এই দোকানটা মাথায় রাখো। রহিম, করিম চাচা, রুবেল আর sample কার্ড পুরো article জুড়ে আমাদের সাথে থাকবে — আর হ্যাঁ, আমরা এই দোকানটা কোডেও লিখব।
Prototype pattern আসলে কী?
Prototype pattern হলো একটা creational design pattern। "Creational" মানে এটা object কীভাবে তৈরি হয় তা নিয়ে।
সহজ সংজ্ঞাটা হলো:
Prototype তোমাকে একটা বিদ্যমান object কপি করে (যাকে prototype বলা হয়) নতুন object তৈরি করতে দেয় —
newদিয়ে শূন্য থেকে অনেক setup কোড লিখে বানানোর বদলে। কপি করার logic টা object-এর ভেতরেই থাকে, সাধারণতclone()নামের একটা method-এ।
তিনটা ছোট আইডিয়া আছে এখানে:
- Object নিজেই নিজেকে কপি করে। তুমি বাইরে দাঁড়িয়ে field by field কপি করো না। Object-কে বলো, "আমাকে তোমার একটা কপি দাও," আর সে কাজটা করে দেয়। Object নিজের private তথ্য জানে, তাই তার কপি সম্পূর্ণ হয়। করিম চাচা তাঁর কার্ড সবচেয়ে ভালো জানেন — রহিমকে জানতে হয় না কোন কাগজ বা কালি ব্যবহার হয়েছে।
- Caller-কে exact class জানতে হয় না। তুমি যদি কিছু সাধারণ
Cardহিসেবে ধরে রাখো, তুমিcard.clone()ডাকতে পারবে আর সঠিক ধরনের কার্ড পাবে — এমনকি সেটা গোপনে একটাVipCardহলেও। রহিম শুধু বলে "এটা কপি করো", কখনো জিজ্ঞেস করে না "এটা offset print নাকি digital print?" - Clone করার পরে adjust করা যায়। আগে clone করো, তারপর যে এক-দুটো field আলাদা সেগুলো বদলাও। যেমন রুবেলের সোনালি বর্ডার।
এক লাইনে মনে রাখো: "আবার বানাবো কেন যখন ফটোকপি করা যায়?" Prototype মানে হলো একটা নিখুঁত sample রাখো, তারপর clone করো।
Pattern টাকে Clone-ও বলা হয়, কারণটা স্পষ্ট। এটা বিখ্যাত Design Patterns বইয়ের মূল ২৩টা pattern-এর একটা, Gang of Four (Gamma, Helm, Johnson আর Vlissides, ১৯৯৪) লিখেছিলেন।
কলেজ কর্নার: GoF-এর ভাষায়, Prototype class-based creation-এর বদলে object-based creation করে। তোমার কোড concrete constructor-এর উপর নির্ভর না করে clone() সহ একটা interface-এর উপর নির্ভর করে — এটা Dependency Inversion Principle মেনে চলে। এটা subclass explosion-ও কমায়: প্রতিটা configuration-এর জন্য আলাদা subclass না বানিয়ে, তুমি প্রতিটা configuration-এর জন্য একটা configured instance রাখো।
এটা কোন সমস্যা সমাধান করে
আগে ব্যথাটা অনুভব করা যাক। ধরো Prototype pattern নেই, আর আমাদের প্রথমটার মতো হুবহু একটা দ্বিতীয় কার্ড দরকার। "সহজ" উপায় হলো নতুন object বানিয়ে হাতে হাতে প্রতিটা field কপি করা:
// কষ্টের, হাতে কপি করার উপায় — এটা করো না!
const original = new WeddingCard("Fatima weds Tariq", "Dhaka", "12 Dec 2026");
const copy = new WeddingCard(
original.coupleNames, // field 1 কপি
original.venue, // field 2 কপি
original.date // field 3 কপি
);
// ...আর field 4, 5, 6... কোনোটা ভুলিনি তো!তিনটা field-এর জন্য ঠিকঠাক লাগছে। কিন্তু বাস্তব object এত ছোট হয় না। দেখো কোথায় এই approach ভেঙে পড়ে:
- Exact class জানতে হবে।
new WeddingCard(...)লিখতে গেলে, তোমার কোড সেই specific class-এর সাথে আটকে গেল। যদি তুমি শুধু একটা variableCardহিসেবে ধরে থাকো, আর জানো না এটাWeddingCardনাকিBirthdayCard? তাহলে প্রতিটা type-এর জন্য বাজেif-elsecheck লিখতে হবে। - Private field দেখা যায় না। অনেক object private field-এ গোপন internal state রাখে। বাইরের কোড সেগুলো পড়তে পারে না। তাই তোমার হাতে-বানানো কপি সবসময় অসম্পূর্ণ থাকবে — দুই পাতার form-এর শুধু সামনের পাতা ফটোকপি করার মতো।
- Setup ব্যয়বহুল হতে পারে। হয়তো আসল object টা database call, network request বা ভারী calculation-এর মাধ্যমে তৈরি হয়েছিল। শূন্য থেকে দ্বিতীয়টা বানানো সেই পুরো খরচ আবার করে। খাবার টেবিলে থাকতে আবার কেন রান্না করবে?
- একটা field ভুলে যাওয়া সহজ। কাল কেউ class-এ ৭ নম্বর field যোগ করল কিন্তু তোমার কপি কোড update করতে ভুলে গেল। এখন তোমার কপিগুলো চুপচাপ ভুল হয়ে যাচ্ছে। এই bug গুলো খুঁজে পাওয়া অনেক কঠিন।
শূন্য থেকে একটা সমৃদ্ধ object বানাতে সময় কোথায় যায়? মোটামুটি এরকম:
Cloning প্রায় সবটাই বাদ দিয়ে দেয়, কারণ load করা data আর computed field গুলো ইতিমধ্যে sample-এর ভেতরে বসে আছে। সিদ্ধান্তটা একটা ছবিতে দেখো:
Prototype pattern বলে: বাইরে থেকে কপি করা বন্ধ করো। class-এর ভেতরে একটা clone() method রাখো। Object নিজের সব field-এ — এমনকি private গুলোতেও — সম্পূর্ণ access পায়, তাই সে নিখুঁত কপি বানাতে পারে। Caller শুধু original.clone() বলে শেষ।
আর সাশ্রয়টা ছোট না। একটা কার্ড শূন্য থেকে তৈরি করতে যদি ভারী setup লাগে, cloning-এ শুধু একটা memory copy লাগে:
সংখ্যাগুলো তোমার program-এর উপর নির্ভর করে, কিন্তু এই chart-এর আকারটা খুব common: একবার বানাও, অনেকবার clone করো, অনেক সাশ্রয় করো।
কীভাবে কাজ করে, ধাপে ধাপে
এই হলো standard recipe। ছোট, আর যেকোনো ভাষায় follow করতে পারবে:
- একটা common interface তৈরি করো একটা method দিয়ে:
clone()। যে class কপিযোগ্য হতে চায় সে এই interface implement করে। - প্রতিটা class-এ একটা copy constructor (বা copy logic) লেখো। এই constructor একই class-এর আরেকটা object নিয়ে তার সব field কপি করে। child class-এ, inherited field গুলোও কপি হওয়ার জন্য আগে parent-এর copy logic ডাকো।
clone()implement করোnew SameClass(this)return করতে। প্রতিটা class তার নিজের type-এ clone করে। তাই একটা plainCardvariable দিয়েVipCardclone করলেও আসলVipCardপাওয়া যায়।- প্রতিটা reference field-এর জন্য shallow বা deep সিদ্ধান্ত নাও। যদি একটা field আরেকটা object-এর দিকে point করে, তোমাকে বেছে নিতে হবে: সেই object share করবে (shallow) নাকি সেটাও কপি করবে (deep)? এটাই পুরো pattern-এর সবচেয়ে গুরুত্বপূর্ণ সিদ্ধান্ত। নিচের কোডে পরিষ্কার দেখব।
- (Optional) একটা prototype registry তৈরি করো। এটা একটা ছোট catalogue — করিম চাচার counter-এর পেছনে sample কার্ডের তাকের মতো। নাম দিয়ে ready-made prototype register করো ("simple-card", "vip-card"), আর client registry-কে জিজ্ঞেস করে: "আমাকে vip-card-এর একটা তাজা কপি দাও।"
লক্ষ্য করো registry sample ধরে রাখে আর সবসময় clone return করে। তাকের sample গুলো কখনো দোকান ছেড়ে যায় না — ঠিক যেমন করিম চাচা তাঁর master কার্ড কাউকে দিয়ে দেন না।
Client আর prototype-এর মধ্যে কথোপকথন ছোট — এটাই সৌন্দর্য:
একটা sample কার্ডেরও একটা ছোট জীবন আছে। একবার তৈরি হয়, একবার approval পায়, তারপর সারাজীবন clone হতে থাকে:
চিত্র ৭-এর loop টা মনোযোগ দিয়ে পড়ো। প্রতিটা clone() call Cloning-এর মধ্য দিয়ে যায় আর একটা CopyReady object তৈরি করে, কিন্তু sample নিজে সবসময় নিরাপদে SampleReady-তে ফিরে আসে। যদি কোনো clone কখনো sample নষ্ট করে দেয়, তোমার pattern ভেঙে গেছে — shallow copy section-এ দেখব ঠিক কীভাবে এই দুর্ঘটনা ঘটে।
TypeScript-এ বিয়ের কার্ডের দোকান: real কোড উদাহরণ
পুরো দোকানটা বানাই। Comment গুলো ধীরে ধীরে পড়ো — প্রতিটা ধাপ বুঝিয়ে দেওয়া হয়েছে।
// Step 1: the common interface. Anything copyable has clone().
interface CardPrototype {
clone(): CardPrototype;
}
// A small nested object: the visual design of the card.
// This will help us understand shallow vs deep copy.
class Design {
constructor(
public borderColor: string,
public font: string
) {}
// The design knows how to copy itself too.
clone(): Design {
return new Design(this.borderColor, this.font);
}
}
// Step 2 + 3: the concrete prototype with a copy constructor.
class WeddingCard implements CardPrototype {
coupleNames: string;
venue: string;
date: string;
design: Design; // reference field — careful here!
constructor(coupleNames: string, venue: string, date: string, design: Design) {
this.coupleNames = coupleNames;
this.venue = venue;
this.date = date;
this.design = design;
}
// clone() builds a new WeddingCard from "this".
clone(): WeddingCard {
return new WeddingCard(
this.coupleNames, // string: copied by value, safe
this.venue, // string: safe
this.date, // string: safe
this.design.clone() // DEEP copy of the nested design!
);
}
describe(): string {
return `${this.coupleNames} | ${this.venue} | ${this.date} ` +
`| border: ${this.design.borderColor}, font: ${this.design.font}`;
}
}এখন registry — counter-এর পেছনে sample কার্ডের তাক:
// Step 5: the prototype registry (the sample shelf).
class CardRegistry {
private samples = new Map<string, CardPrototype>();
register(name: string, sample: CardPrototype): void {
this.samples.set(name, sample);
}
// Never hand out the sample itself — always a fresh clone!
get(name: string): CardPrototype {
const sample = this.samples.get(name);
if (!sample) throw new Error(`No sample named "${name}"`);
return sample.clone();
}
}
// ---------- Client code: a day in the shop ----------
// করিম চাচা একটাই নিখুঁত sample তৈরি করেন।
const sample = new WeddingCard(
"Fatima weds Tariq",
"Sunrise Garden, Dhaka",
"12 Dec 2026",
new Design("maroon", "Devanagari Classic")
);
const registry = new CardRegistry();
registry.register("fatima-tariq", sample);
// রহিমের পরিবার কার্ড অর্ডার করে। প্রতিটা অর্ডার = একটা clone।
const card1 = registry.get("fatima-tariq") as WeddingCard;
const card2 = registry.get("fatima-tariq") as WeddingCard;
// একজন VIP অতিথি সোনালি বর্ডার চান। রুবেল শুধু কপিটা বদলায়।
card2.design.borderColor = "golden";
console.log("Card 1:", card1.describe());
console.log("Card 2:", card2.describe());
console.log("Sample:", sample.describe());প্রত্যাশিত output:
Card 1: Fatima weds Tariq | Sunrise Garden, Dhaka | 12 Dec 2026 | border: maroon, font: Devanagari Classic
Card 2: Fatima weds Tariq | Sunrise Garden, Dhaka | 12 Dec 2026 | border: golden, font: Devanagari Classic
Sample: Fatima weds Tariq | Sunrise Garden, Dhaka | 12 Dec 2026 | border: maroon, font: Devanagari Classicচমৎকার! Card 2 সোনালি বর্ডার পেল, কিন্তু Card 1 আর master sample maroon-ই রইল। প্রতিটা কপি স্বাধীন। করিম চাচার তাক নিরাপদ।
Shallow copy-এর ফাঁদ — live demo
এখন পুরো article-এর সবচেয়ে গুরুত্বপূর্ণ অংশ। যদি clone() অলস হয়ে design share করত কপি না করে? এটাকে বলে shallow copy:
// একটা ভুল clone — design-এর shallow copy।
clone(): WeddingCard {
return new WeddingCard(
this.coupleNames,
this.venue,
this.date,
this.design // ⚠️ একই Design object SHARED!
);
}এই version দিয়ে, একই client কোড চালাও:
Card 1: ... | border: golden, font: Devanagari Classic ← বদলে গেছে!
Card 2: ... | border: golden, font: Devanagari Classic
Sample: ... | border: golden, font: Devanagari Classic ← master-ও বদলে গেছে!বিপর্যয়! রুবেল Card 2-এর বর্ডার বদলাল, কিন্তু Card 1 আর master sample-ও সোনালি হয়ে গেল। একটু ভাবো — করিম চাচার মুখের অবস্থা কী হবে: এক অতিথি সোনালি চাইলেন, আর এখন দোকানের প্রতিটা ভবিষ্যৎ কার্ড সোনালি প্রিন্ট হচ্ছে। কেন হলো? কারণ তিনটা কার্ডই memory-তে একই single Design object-এর দিকে point করছিল। আমরা কার্ড ফটোকপি করলাম কিন্তু সব কপি একটাই share করা design sheet-এ staple করে দিলাম।
এই ছবিটা দেখায় উভয় ক্ষেত্রে memory-তে কী বসে আছে:
সহজ কথায় নিয়মটা হলো:
- Shallow copy = শুধু উপরের বাক্স কপি করো। ভেতরের nested object গুলো তখনও share। দ্রুত, কিন্তু mutable nested object-এর জন্য ঝুঁকিপূর্ণ।
- Deep copy = বাক্স আর তার ভেতরের সব কিছু, একদম নিচ পর্যন্ত কপি করো। Clone সম্পূর্ণ স্বাধীন হয়ে যায়।
| Field-এর ধরন | Shallow copy কী করে | নিরাপদ? |
|---|---|---|
| Numbers, booleans | মান কপি করে | ✅ সবসময় নিরাপদ |
| Strings (JS/TS, Python, C#, Java-তে) | Reference কপি করে, কিন্তু string বদলানো যায় না | ✅ নিরাপদ (immutable) |
Nested mutable object (যেমন Design) | আসল আর clone-এর মধ্যে একটা object share করে | ❌ ঝুঁকিপূর্ণ — পরিবর্তন দুদিকেই leak করে |
| Mutable item-এর array আর list | একই list share করে | ❌ ঝুঁকিপূর্ণ — একটায় push করলে দুটোই বাড়ে |
| Shared read-only object (যেমন global config) | Share করে | ✅ সাধারণত ঠিক আছে, share করাই উদ্দেশ্য |
| খোলা file handle, socket, DB connection | Live resource share করে | ⚠️ Share করাটাই হয়তো একমাত্র বুদ্ধিমানের কাজ — একটা live socket নকল করা অর্থহীন |
একটা ভালো clone() সাধারণত মিশ্রণ: সহজ মান সরাসরি কপি করো, immutable জিনিস share করো, আর যে mutable object সত্যিই এই object-এর নিজের তা deep-copy করো। Field by field সিদ্ধান্ত নাও, আর প্রতিটা সিদ্ধান্তের জন্য একটা ছোট comment লেখো।
কলেজ কর্নার — copy semantics ঠিকমতো: আমরা যাকে সহজে "deep copy" বলি সেটা আসলে object graph traversal-এর একটা প্রশ্ন। একটা পূর্ণ deep copy হলো reachable graph-এর উপর একটা recursive walk, আর তিনটা কঠিন case আসে। (১) Cycles: যদি A, B-কে point করে আর B আবার A-কে, naive recursion কখনো শেষ হয় না; Python-এর copy.deepcopy-এর মতো বাস্তব implementation একটা memo dictionary রাখে যেটা ইতিমধ্যে কপি করা id-গুলো তাদের copy-তে map করে। (২) Aliasing: যদি দুটো field একই object-কে point করে, একটা সঠিক deep copy-কে সেই sharing clone-এর ভেতরে preserve করতে হবে। (৩) Identity vs equality: একটা সঠিক deep copy-র পরে, clone == original সত্য হতে পারে (সমান মান) কিন্তু clone is original মিথ্যা (আলাদা identity) — আর এটা recursively ধরে রাখতে হবে। JavaScript-এর structuredClone cycle handle করে কিন্তু function আর class method কপি করতে পারে না। Trust করার আগে তোমার language-এর default জানো।
Python-এ একই আইডিয়া
Python-এ Prototype খুব সুন্দর, কারণ standard library-তে copy module তৈরিই আছে উভয় ধরনের copy সহ।
import copy
class Design:
def __init__(self, border_color: str, font: str):
self.border_color = border_color
self.font = font
class WeddingCard:
def __init__(self, couple, venue, date, design: Design):
self.couple = couple
self.venue = venue
self.date = date
self.design = design
def clone(self) -> "WeddingCard":
# deepcopy walks the whole object and copies
# every nested mutable object too.
return copy.deepcopy(self)
sample = WeddingCard(
"Fatima weds Tariq",
"Sunrise Garden, Dhaka",
"12 Dec 2026",
Design("maroon", "Devanagari Classic"),
)
vip_card = sample.clone()
vip_card.design.border_color = "golden"
print(sample.design.border_color) # maroon ← master নিরাপদ!
print(vip_card.design.border_color) # goldenPython-এর copy module থেকে মনে রাখার দুটো function:
copy.copy(x)→ shallow copy (শুধু উপরের level)।copy.deepcopy(x)→ deep copy (সবকিছু, recursively; এটা নিজেদের দিকে point করা object-ও handle করে, কলেজ কর্নারে দেখা memo dictionary-র কারণে)।
তুমি class-এ __copy__ আর __deepcopy__ method লিখে copying customize করতে পারো — এটাই Python-এর Prototype pattern-এর official hook। ব্যবহার করো যখন কিছু field share করতে হবে (একটা logger, একটা cache) deep copy-এর সময়ও।
বাস্তব software-এ যেখানে দেখা যায়
এই pattern শুধু পাঠ্যবইয়ের গল্প না। প্রতিদিনই এটার সাথে দেখা হয়:
- JavaScript-এর
structuredClone()। আধুনিক browser আর Node.js (17+)-এ একটা built-in deep copy function আছে। এটাDate,Map,Set, typed array, এমনকি circular reference সহ object-ও সঠিকভাবে কপি করে — যেটা পুরনোJSON.parse(JSON.stringify(x))কৌশল নষ্ট করে দেয়। যখন একটা web app undo history রাখে state snapshot করে, এটা Prototype করছে। - Python-এর
copy.deepcopy()। ডেটা science (tweak করার আগে model configuration কপি করা) থেকে game পর্যন্ত সর্বত্র ব্যবহার হয়। python-patterns repository-তে registry/dispatcher সহ একটা পরিষ্কার Prototype উদাহরণ আছে। - Game development — object spawn করা। একটা game প্রতিটা enemy শূন্য থেকে বানায় না। একটা পুরোপুরি তৈরি enemy template রাখে (model, sound, default health) আর নতুন enemy আসলে clone করে। Unity-র
Instantiate(prefab)ঠিক এই আইডিয়া: prefab হলো prototype, আর game প্রতি সেকেন্ডে ষাটবার clone করতে পারে। - Document আর graphics editor। Presentation software-এ "Duplicate slide", Photoshop-এ "duplicate layer", আঁকা shape copy-paste — প্রতিটা duplicate একটা configured object-এর clone। লক্ষ্য করো duplicated slide স্বাধীন: edit করলে আসলটা বদলায় না। কেউ তোমার জন্য একটা সাবধানী deep copy implement করেছে।
- Java-র
Cloneable/ copy constructor। Java-তে language-এর শুরু থেকেই cloning আছে। iluwatar/java-design-patterns repository-তে একটা পরিষ্কার আধুনিক Prototype উদাহরণ আছে যেটা দশ মিনিটে পড়তে পারবে। - JS object system নিজেই। JavaScript আক্ষরিক অর্থে একটা prototype-based ভাষা: object গুলো prototype chain-এর মাধ্যমে অন্য object থেকে inherit করে, আর
Object.create(proto)একটা বিদ্যমান object-এর উপর ভিত্তি করে নতুন object বানায়। নামটা coincidence না — language design একই আইডিয়া ধার করেছিল।
কখন ব্যবহার করব আর কখন না
| পরিস্থিতি | Prototype ব্যবহার করব? |
|---|---|
| Object বানানো ব্যয়বহুল (DB call, network, ভারী math) আর অনেক মিলে যাওয়া object দরকার | ✅ হ্যাঁ — তৈরি-করাটা clone করো |
| একটা baseline থেকে সামান্য পার্থক্যের অনেক object দরকার | ✅ হ্যাঁ — clone করো, তারপর tweak করো |
| তোমার কোড শুধু interface দিয়ে object ধরে রাখে আর concrete class না জেনে কপি করতে হবে | ✅ হ্যাঁ — clone() আসল type ধরে রাখে |
| শুধু ভিন্ন configuration store করতে অনেক subclass বানাতে ইচ্ছে হচ্ছে | ✅ হ্যাঁ — বরং configured prototype রাখো |
| Object ছোট আর সস্তা, যেমন x আর y সহ একটা ছোট point | ❌ না — plain new সহজ আর পরিষ্কার |
| Object-এ এমন জিনিস আছে যা duplicate করা উচিত না (খোলা file handle, socket, DB connection) | ❌ না — একটা live connection clone করা অর্থহীন |
| Object graph-এ জট পাকানো circular reference আছে আর deep copy সাবধানে নিয়ন্ত্রণ করতে পারবে না | ❌ সাবধান — cycle-এর deep copy-এ বিশেষ handling লাগে |
| Object গুলো এমনিতেই immutable | ❌ সাধারণত না — immutable object নিরাপদে share করা যায়, কপি লাগে না |
যদি একটা ছবি দিয়ে সিদ্ধান্ত নিতে চাও, তোমার case এই map-এ রাখো। Prototype উপর-ডানের কোণে সবচেয়ে ভালো — ব্যয়বহুল object, অনেক কপি দরকার:
ছাত্রছাত্রীরা যে সাধারণ ভুলগুলো করে
ভুল ১: mutable nested object-এর shallow copy করা। এটা পুরো পৃথিবীতে এক নম্বর Prototype bug। কার্ড clone করলে, কপির design বদলালে, আর রহস্যজনকভাবে আসলটাও বদলে যায় — করিম চাচার সোনালি বিপর্যয়। যখনই কোনো field একটা mutable object ধরে রাখে, জোরে জিজ্ঞেস করো: "share নাকি কপি?" — আর উত্তরটা কোডে লিখে রাখো।
ভুল ২: subclass-এ parent field ভুলে যাওয়া। যদি VipCard extends WeddingCard হয়, VipCard copy constructor-কে আগে parent-এর copy logic (super(source)) ডাকতে হবে, নাহলে দম্পতির নাম আর ভেন্যু clone-এ চুপচাপ ফাঁকা থাকবে। দম্পতির নাম ছাড়া বিয়ের কার্ড শুধু দামি কাগজ।
ভুল ৩: registry থেকে prototype নিজেই দিয়ে দেওয়া। Registry-কে অবশ্যই sample.clone() return করতে হবে, কখনো sample নয়। যদি master sample return করো, প্রথম customer যে সেটায় কিছু লেখে সে সবার জন্য নষ্ট করে দেবে — দোকানের একমাত্র master কার্ড দিয়ে দেওয়ার মতো।
ভুল ৪: JavaScript-এ deep copy-এর জন্য JSON.parse(JSON.stringify(obj)) ব্যবহার করা। এটা function ফেলে দেয়, Date-কে plain string-এ রূপান্তর করে, Map আর Set হারিয়ে ফেলে, আর circular reference-এ crash করে। Data-র জন্য structuredClone() ব্যবহার করো, অথবা যখন behaviour-ও গুরুত্বপূর্ণ তখন proper clone() method।
আত্মীয় pattern গুলোর সাথে তুলনা
Prototype পাঁচটা classic creational pattern-এর একটা। এর পাশে আত্মীয়রা কীভাবে দাঁড়ায়:
| Pattern | এটা কোন প্রশ্নের উত্তর দেয় | কীভাবে object জন্ম নেয় | Prototype থেকে মূল পার্থক্য |
|---|---|---|---|
| Prototype | "এটার মতো আরেকটা কীভাবে পাব?" | বিদ্যমান object কপি করো | — |
| Factory Method | "কোন subclass কী তৈরি করবে তা ঠিক করে?" | Subclass একটা creation method override করে | Inheritance আর new ব্যবহার করে; Prototype copying ব্যবহার করে, subclass লাগে না |
| Abstract Factory | "সম্পর্কিত object-এর পুরো পরিবার কীভাবে তৈরি করব?" | অনেক create method সহ একটা factory object | ভেতরে prototype দিয়ে implement করা যায় |
| Builder | "জটিল object ধাপে ধাপে কীভাবে সাজাব?" | ধাপে ধাপে construction | Builder নতুন করে তৈরি করে; Prototype ready-made নকল করে |
| Singleton | "কীভাবে নিশ্চিত করব শুধু একটাই object আছে?" | একটা instance, সর্বত্র পুনরায় ব্যবহার | সম্পূর্ণ বিপরীত লক্ষ্য: Prototype object গুণ করে, Singleton গুণ করতে নিষেধ করে! |
Gang of Four বইয়ের একটা মজার তথ্য: design প্রায়ই Factory Method দিয়ে শুরু হয় (সহজ, কিন্তু subclass লাগে) আর আরো flexibility দরকার হলে Abstract Factory বা Prototype-এর দিকে বাড়ে।
এক নজরে পুরো landscape review করতে, এই mind map দেখো:
দ্রুত revision বক্স
+-------------------------------------------------------------+
| PROTOTYPE PATTERN — REVISION |
+-------------------------------------------------------------+
| Idea : Make new objects by COPYING a sample object |
| Nickname : Clone ("the xerox pattern") |
| Key method : clone() — lives INSIDE the object |
| Story : One perfect wedding card -> 500 copies |
| |
| Steps : 1. Interface with clone() |
| 2. Copy constructor in each class |
| 3. clone() returns new SameClass(this) |
| 4. Decide SHALLOW vs DEEP per field |
| 5. Optional: registry of named samples |
| |
| Shallow : top level only, nested objects SHARED ⚠️ |
| Deep : nested objects copied too, fully independent ✅ |
| |
| Use when : creation is costly; many similar objects; |
| cannot depend on concrete classes |
| Avoid when : tiny cheap objects; live connections/handles |
| Real world : structuredClone(), copy.deepcopy(), |
| game prefabs, "duplicate slide" |
+-------------------------------------------------------------+নিজে করে দেখো
নিজে নিজে এই কাজগুলো করে দেখো। সহজ থেকে একটু কঠিনের দিকে যাচ্ছে — রহিম-লেভেল থেকে রুবেল-লেভেল।
-
স্কুল ID কার্ড মেশিন। একটা
StudentIdCardclass তৈরি করোname,className,rollNumber, আর একটা nestedSchoolInfoobject সহ (স্কুলের নাম, ঠিকানা, principal-এর নাম)। ১,২০০ ছাত্রছাত্রী একই school info share করে, কিন্তু প্রতিটা কার্ডে ভিন্ন নাম আর roll নম্বর।clone()implement করো যাতেSchoolInfoshare হয় (shallow — এটা প্রতি ছাত্রের জন্য বদলায় না) কিন্তু বাকিটা কপি হয়। তারপর এক বাক্যে বলো কেন এখানে share করাটাই সঠিক ছিল। -
Bug ধরো। একটা
TiffinBoxclass লেখো যাতেcompartmentsarray আছে যেমন roti, sabzi, pickle। একটা ভুল shallowclone()implement করো, box clone করো, কপির pickle "sweet"-এ বদলাও, আর bug টা দেখাতে দুটো box print করো। তারপর array-এর proper deep copy দিয়েclone()ঠিক করো আর corrected output দেখাও। -
Mehendi design registry। একটা
DesignRegistryতৈরি করো যেটা তিনটা prototype mehendi pattern ("bridal", "simple", "arabic") store করে, প্রতিটায় একটা nestedPricingobject। Clientregistry.get("bridal")ডাকে আর সবসময় স্বাধীন clone পাবে। একটা test দিয়ে prove করো: "bridal"-এর দুটো clone নাও, একটার দাম বাড়াও, আর assert করো অন্য clone আর registry-র master অপরিবর্তিত। -
কলেজ কর্নারের challenge। দুটো card object বানাও যেগুলো পরস্পরকে point করে (card A-এর field
partnerCardহলো B, আর B-এর হলো A)। একটা naive recursive deep copy লেখো আর দেখো এটা চিরকাল recurse করে। তারপর object identity দিয়ে key করা একটা memo map দিয়ে ঠিক করো —copy.deepcopy-এর মতো একই কৌশল। লিখে রাখো কেন memo shared reference-ও সঠিকভাবে preserve করে।
যদি তুমি এই article-এ ফিরে না তাকিয়ে ৩ নম্বর কাজ শেষ করতে পারো, তুমি Prototype সত্যিই বুঝে ফেলেছ। বেশ হয়েছে — একটু বিশ্রাম নাও। করিম চাচা তোমাকে চা দিতেন।
সচরাচর জিজ্ঞাসা
- এক লাইনে Prototype pattern কী?
- Prototype হলো একটা creational pattern যেখানে তুমি new দিয়ে শূন্য থেকে না বানিয়ে একটা তৈরি-করা object (prototype) কপি করে নতুন object বানাও।
- shallow copy আর deep copy-এর পার্থক্য কী?
- shallow copy শুধু উপরের level কপি করে, তাই ভেতরের nested object গুলো আসল আর কপির মধ্যে share হয়। deep copy nested object গুলোও কপি করে, ফলে দুটো object সম্পূর্ণ আলাদা হয়ে যায়।
- কখন constructor-এর বদলে Prototype ব্যবহার করব?
- Prototype ব্যবহার করো যখন object বানানো ব্যয়বহুল বা জটিল, যখন সামান্য পার্থক্যসহ অনেক object দরকার, অথবা যখন তোমার কোড concrete class-এর নামের উপর নির্ভর করা উচিত না।
- JavaScript-এর structuredClone() কি Prototype pattern?
- structuredClone() হলো একটা built-in deep copy tool। এটা কপি করার কাজটা করে দেয়, যেটা Prototype-এর মূল কথা। কিন্তু পূর্ণ pattern-এ object-এর ভেতরে একটা clone() method আর কখনো কখনো একটা prototype registry-ও থাকে।
- prototype registry কী?
- prototype registry হলো একটা ছোট catalogue যেটা নাম দিয়ে তৈরি-করা prototype object রাখে। তুমি নাম দিয়ে চাইলে সে তাজা একটা clone দিয়ে দেয় — নতুন class ছাড়াই একটা সহজ factory-র মতো কাজ করে।
আরো দেখো
সম্পর্কিত পাঠ
Factory Method Pattern: Branch নিজেই ঠিক করুক কোন Vehicle
Factory Method design pattern শেখো রাহিমের টিফিন সার্ভিসের গল্পের মাধ্যমে — সহজ TypeScript আর C# code, diagram, real-world উদাহরণ, আর practice সহ।
Abstract Factory Pattern: এক অর্ডার, এক ম্যাচিং থালি
বাংলাদেশি বিয়ের ক্যাটারিং গল্পের মাধ্যমে Abstract Factory design pattern বুঝে নাও — সহজ TypeScript ও Python কোড, diagram, আর practice সহ।
Builder Pattern: একটু একটু করে বানাও, ওস্তাদ দর্জির মতো
ঢাকার একটা দর্জির দোকানের গল্পের মাধ্যমে Builder design pattern শেখো — fluent TypeScript ও C# কোড, telescoping constructor এর সমস্যা, আর practice টাস্কসহ।
Singleton Pattern: পুরো স্কুলে একজনই হেড স্যার
স্কুলের হেড স্যারের গল্প দিয়ে Singleton design pattern বোঝো — সহজ TypeScript ও C# কোড, thread safety, আর কেন অনেক সিনিয়র ডেভেলপার এটাকে anti-pattern বলেন।