ڈیزائن پیٹرن ہے آرسافٹ ویئر ڈیزائن میں عام مسائل کے دستیاب حل۔ اسے ایک بلیو پرنٹ کے طور پر سوچیں۔ یہ مکمل شدہ کوڈ نہیں ہے، بلکہ ایک ثابت شدہ ٹیمپلیٹ ہے جسے آپ اپنے کوڈ بیس میں مخصوص مسائل کو حل کرنے کے لیے موافقت کر سکتے ہیں۔
یہ ہینڈ بک سافٹ ویئر ڈیزائن کے نمونوں کو سمجھنے کے لیے ایک عملی رہنما کے طور پر کام کرتی ہے۔ میں نے یہ کتاب تمام ڈویلپرز کے لیے لکھی ہے، قطع نظر اس کے کہ وہ کسی بھی زبان میں پروگرام کرتے ہیں۔ اگرچہ مثالیں C# میں لکھی گئی ہیں، یہاں کے تمام تصورات Python، Java، TypeScript، Go، وغیرہ پر یکساں طور پر لاگو ہوتے ہیں۔
سورس کوڈ github.com/Clifftech123/design-patterns-handbook پر ہے۔
ذہن میں رکھنے کی چیزیں:
-
ڈیزائن پیٹرن کوڈ نہیں ہیں. وہ ہیں سوچا بیان کرتا ہے کہ آپ کے کوڈ کی ساخت کیسے بنائی جائے۔ یہ ایک آلہ ہے، مخصوص ڈیزائن کے مسائل کو حل کرنے کے لیے کوئی علاج نہیں۔
-
تصور عالمگیر ہے۔ یہاں کی مثالیں C# میں لکھی گئی ہیں، لیکن تمام زبانوں میں ایک جیسے نمونے موجود ہیں۔ اگر آپ Python، Java، Go، یا TypeScript لکھتے ہیں، تو آپ ان میں سے کچھ کو پہلے ہی استعمال کر رہے ہیں، یہاں تک کہ اسے سمجھے بغیر۔
-
کوئی ایک سائز کے فٹ ہونے والا تمام پیٹرن نہیں ہے۔ ہر پیٹرن ایک مخصوص قسم کے مسئلے کو حل کرنے کے لیے موجود ہے۔ سمجھ مسائل جو پیٹرن حل کرتے ہیں۔ یہ عمل کو یاد رکھنے سے زیادہ اہم ہے۔
ہم یہاں C# کو تدریسی زبان کے طور پر استعمال کرتے ہیں، کیونکہ یہ واضح، پڑھنے کے قابل اور وسیع پیمانے پر سمجھی جاتی ہے۔ اس ہینڈ بک کا مقصد خود پیٹرن کے ساتھ ساتھ C# کوڈ کو سمجھنے میں آپ کی مدد کرنا ہے۔
ڈیزائن پیٹرن کی تین اہم اقسام ہیں: تخلیقی، ساختی اور طرز عمل۔ یہاں ہم تخلیقی کے ساتھ شروع کرتے ہوئے ہر آئٹم کو باری باری دیکھیں گے۔
ہم کیا احاطہ کریں گے:
تخلیقی ڈیزائن پیٹرن
سیدھے الفاظ میں، تخلیقی نمونے وہی ہیں جو اس کے بارے میں ہے۔ اشیاء کیسے بنتی ہیں۔. انہیں طبقاتی تخلیق کے نمونوں میں تقسیم کیا جا سکتا ہے، جو وراثت کا استعمال کرتے ہوئے اس بات کا تعین کرتے ہیں کہ کس کلاس کو انسٹیٹیویٹ کرنا ہے، اور آبجیکٹ تخلیق کے نمونے، جو کام کو انجام دینے کے لیے وفد کا استعمال کرتے ہیں۔
ویکیپیڈیا اسے اس طرح بیان کرتا ہے:
"تخلیق کے نمونوں کا مقصد ایک نظام کو الگ کرنا ہے کہ کس طرح اشیاء کی تخلیق، تعمیر، اور نمائندگی کی جاتی ہے۔ وہ نظام کی لچک کو اس لحاظ سے بڑھاتے ہیں کہ کیا، کیا، کیسے، اور اشیاء کب تخلیق کی جاتی ہیں۔”
(ذریعہ)
لہذا، تخلیق کا نمونہ آبجیکٹ کی تخلیق کی تفصیلات کو محفوظ رکھتا ہے۔ کلائنٹ کوڈ سے پوشیدہسسٹم کا انتظام اور دیکھ بھال آسان ہو جاتا ہے۔
یہ اس بات کا بھی خلاصہ کرتا ہے کہ کس طرح اشیاء کی تخلیق، تعمیر اور نمائندگی کی جاتی ہے، لہذا آپ کو اپنے باقی کوڈ کے بارے میں فکر کرنے کی ضرورت نہیں ہے۔
پانچ تخلیقی ڈیزائن کے نمونے ہیں، جنہیں ہم ذیل میں ایک ایک کرکے دیکھیں گے۔
-
ایک ایک کر کے ہو رہا ہے: اس بات کو یقینی بناتا ہے کہ کلاس میں صرف ایک مثال ہے اور اسے عالمی رسائی پوائنٹ فراہم کرتا ہے۔
-
فیکٹری کا طریقہ: آبجیکٹ کی تخلیق کے لیے ایک انٹرفیس کی وضاحت کرتا ہے، لیکن ذیلی طبقات کو یہ فیصلہ کرنے دیتا ہے کہ کس کلاس کو انسٹینٹیٹ کرنا ہے۔
-
خلاصہ فیکٹری: کسی مخصوص طبقے کی وضاحت کیے بغیر متعلقہ یا منحصر اشیاء کا خاندان بنانے کے لیے ایک انٹرفیس فراہم کرتا ہے۔
-
بلڈر: پیچیدہ اشیاء کی ساخت اور نمائندگی کو الگ کر کے، ایک ہی ساخت کا عمل مختلف نتائج پیدا کر سکتا ہے۔
-
پروٹوٹائپ: شروع سے ایک نیا بنانے کے بجائے، موجودہ مثال کو کلون کرکے ایک نیا آبجیکٹ بنائیں۔
1. سنگلٹن ڈیزائن پیٹرن
حقیقی زندگی کی مثال:
ایک آرکسٹرا کنڈکٹر کے بارے میں سوچو۔ آرکسٹرا میں ایک کنڈکٹر ہوتا ہے۔ اسٹیج پر موجود تمام موسیقاروں کو ایک ہی کنڈکٹر کے ذریعہ بتایا جاتا ہے کہ کب شروع کرنا ہے، کب رکنا ہے، کتنی تیزی سے بجانا ہے، اور کتنی بلند آواز میں بجانا ہے۔
کنڈکٹر اتھارٹی کا واحد نقطہ ہے جہاں سے تمام موسیقار جڑتے ہیں اور فیصلے کیے جاتے ہیں۔ دو کنڈکٹر سامنے کھڑے ہو کر مختلف ہدایات نہیں دے سکتے۔ پھر کنفیوژن ہو گی۔ اس سے کوئی فرق نہیں پڑتا ہے کہ کس موسیقار کو رہنمائی کی ضرورت ہے، وہ سب ایک ہی شخص تک پہنچتے ہیں۔
سنگلٹن آپ کے کوڈ میں اس طرح کام کرتے ہیں۔ ہر ایک جس کو ایک مثال کی ضرورت ہے وہ اسے شیئر کر سکتا ہے اور ایک جگہ پر فیصلے کر سکتا ہے۔
مسائل حل ہوئے:
-
اگر دو موسیقاروں کو مختلف کنڈکٹرز کی طرف سے مختلف ہدایات دی جائیں تو کیا ہوگا؟ کارکردگی ٹوٹ جاتی ہے۔ استثناء کے بغیر، ہر موسیقار میں کم از کم ایک کنڈکٹر ہوگا جس کا وہ احترام کرتے ہیں۔
-
موسیقار کو کنڈکٹر کیسے ملتا ہے؟ وہ تلاش نہیں کرتے۔ ایک مشہور جگہ ہے جہاں ہر کوئی جاتا ہے، اور ایک ہی کنڈکٹر ہمیشہ وہاں ہوتا ہے۔
-
کسی کو سیکنڈ ان کمانڈ مقرر کرنے سے کیا روکتا ہے؟ آرکسٹرا خود اس کو کنٹرول کرتا ہے۔ ایک بار جب کنڈکٹر پوڈیم تک پہنچ جاتا ہے تو کوئی اور اس کی جگہ نہیں لے سکتا۔
سیدھے الفاظ میں، کلاس کی صرف ایک مثال ہوتی ہے، اور سسٹم کے ہر حصے کو جس کی ضرورت ہوتی ہے وہ بالکل اسی مثال تک رسائی رکھتا ہے (لیکن کوئی نئی مثال نہیں)۔
یہاں ویکیپیڈیا سنگلٹن پیٹرن کی وضاحت کرتا ہے:
"آبجیکٹ اورینٹڈ پروگرامنگ میں، سنگلٹن پیٹرن ایک سافٹ ویئر ڈیزائن پیٹرن ہے جو کسی کلاس کے انسٹی ٹیشن کو ایک ہی مثال تک محدود کرتا ہے۔ یہ پیٹرن اس وقت کارآمد ہوتا ہے جب پورے سسٹم میں آپریشنز کو مربوط کرنے کے لیے بالکل ایک چیز کی ضرورت ہو۔” (ماخذ)
پروگرامنگ کی مثال:
اب آئیے اس مشابہت کا نمونہ خود بنائیں۔ کہ OrchestraConductor سنگلٹن ایک ایسی مثال ہے جہاں تمام موسیقار شریک ہوتے ہیں اور تمام فیصلے کرتے ہیں۔
public class OrchestraConductor
{
// Step 1: Hold the one instance here
private static OrchestraConductor _instance;
// Step 2: Private constructor - nobody outside can do: new OrchestraConductor()
private OrchestraConductor() { }
// Step 3: The only way to get the conductor
public static OrchestraConductor GetInstance()
{
if (_instance == null)
{
_instance = new OrchestraConductor();
}
return _instance;
}
// Decisions the conductor makes
public void Start() => Console.WriteLine("Conductor: Begin playing.");
public void Stop() => Console.WriteLine("Conductor: Stop playing.");
public void SetTempo(string tempo) => Console.WriteLine($"Conductor: Tempo is now {tempo}.");
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
// Violinist asks for the conductor
OrchestraConductor violinist = OrchestraConductor.GetInstance();
// Pianist asks for the conductor
OrchestraConductor pianist = OrchestraConductor.GetInstance();
// Are they talking to the same conductor?
Console.WriteLine(object.ReferenceEquals(violinist, pianist)); // True
violinist.SetTempo("Allegro");
pianist.Start();
حساب کتاب:
True
Conductor: Tempo is now Allegro.
Conductor: Begin playing.
دونوں موسیقار ایک ہی کنڈکٹر. کنسٹرکٹر کو دو بار پھانسی نہیں دی جاتی ہے۔ یہ سنگلٹن پیٹرن ہے۔
سنگلٹن پیٹرن کب استعمال کریں۔
جب آپ کو ایک مشترکہ وسائل کی ضرورت ہو تو ایک سنگلٹن استعمال کریں جس سے پوری ایپلیکیشن رابطہ کرتی ہے، جیسے لاگر، کنفیگریشن مینیجر، یا ڈیٹا بیس کنکشن پول۔
یہ بھی ایک اچھا خیال ہے اگر ایک سے زیادہ مثالیں غلط رویے یا ریاستی تنازعات کا باعث بن سکتی ہیں۔
اور یہ اس وقت مفید ہے جب آپ کسی چیز کو ہر جگہ منتقل کیے بغیر اس تک عالمی رسائی کا نقطہ چاہتے ہیں۔
2. فیکٹری کا طریقہ
بھرتی کرنے والی ایجنسی پر غور کریں۔ کمپنی ڈیلرشپ کو کال کرتی ہے اور کہتی ہے، "ہمیں ملازمین کی ضرورت ہے۔” کمپنیاں باہر نہیں جاتیں اور اپنے ملازمین پیدا نہیں کرتیں۔ وہ صرف ایک درخواست کرتے ہیں۔
ایجنسی فیصلہ کرتی ہے کہ آیا مخصوص لوگوں کو بھیجنا ہے – ڈویلپرز، ڈیزائنرز یا ٹیسٹرز – کمپنی کی ضروریات پر منحصر ہے۔ کمپنی کو معلوم نہیں ہے کہ کون آرہا ہے۔ وہ صرف اتنا جانتے ہیں کہ وہ شخص کام کر سکے گا۔
یہ ایک فیکٹری طریقہ ہے۔ آپ کے کوڈ کو ایک آبجیکٹ درکار ہے۔ فیکٹری فیصلہ کرتی ہے کہ کس مخصوص قسم کو بنانا ہے اور اسے واپس کرتا ہے۔ آپ یہ جاننے کے بغیر کام کر سکتے ہیں کہ ہڈ کے نیچے کیا ہے۔
مسائل حل ہوئے:
-
کمپنی کو یہ جاننے کی ضرورت نہیں ہے کہ وہ کون حاصل کر رہے ہیں۔ انہیں صرف کسی ایسے شخص کی ضرورت ہے جو کام کر سکے۔ کس کو بھیجنا ہے اس بارے میں فیصلہ ایجنسی کے ذریعہ کیا جاتا ہے۔ کمپنی کو تفصیلات کے بارے میں فکر کرنے کی ضرورت نہیں ہے۔
-
اگر کل آپ کی کمپنی کو مختلف قسم کے ملازم کی ضرورت ہو تو کیا ہوگا؟ میں اسی ایجنسی کو کال کرتا ہوں۔ ایجنسی فیصلہ کرتی ہے۔ کمپنی کا عمل تبدیل نہیں ہوتا، صرف ایجنسی کے فیصلے بدلتے ہیں۔
-
اگر آپ کو ایک نئی قسم کا ملازم لانے کی ضرورت ہو تو کیا ہوگا؟ اس سے نمٹنے کے لیے ایک نیا ماہر ادارہ بنایا گیا۔ باقی سب کچھ بالکل ویسا ہی رہتا ہے۔
سیدھے الفاظ میں، یہ آبجیکٹ بنانے کے لیے ایک انٹرفیس کی وضاحت کرتا ہے، لیکن ذیلی طبقات کو یہ فیصلہ کرنے دیتا ہے کہ کس کلاس کو فوری بنانا ہے۔ فیکٹری کے طریقے کلاس کو انسٹیٹیوشن کو ذیلی کلاسوں تک موخر کرنے کی اجازت دیتے ہیں۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"آبجیکٹ اورینٹڈ پروگرامنگ میں، فیکٹری میتھڈ پیٹرن ایک ڈیزائن پیٹرن ہے جو اس کی قطعی کلاس کی وضاحت کیے بغیر آبجیکٹ کی تخلیق کے مسئلے سے نمٹنے کے لیے فیکٹری کے طریقے استعمال کرتا ہے۔ فیکٹری کے طریقوں کو انٹرفیس میں بیان کیا جاسکتا ہے اور ذیلی کلاس کے ذریعے لاگو کیا جاسکتا ہے، یا بیس کلاس میں لاگو کیا جاسکتا ہے اور اختیاری طور پر ذیلی طبقے کے ذریعے اوور رائیڈ کیا جاسکتا ہے۔” (ماخذ)
پروگرامنگ کی مثال:
ڈیلرشپ ایک فیکٹری ہے۔ ورکر کی قسم پروڈکٹ ہے۔ کمپنی گاہک ہے۔
// The worker interface - all workers can do a job
public interface IWorker
{
void DoWork();
}
// The concrete workers
public class Developer : IWorker
{
public void DoWork() => Console.WriteLine("Developer: Writing code.");
}
public class Designer : IWorker
{
public void DoWork() => Console.WriteLine("Designer: Creating designs.");
}
// The base agency - declares the factory method
public abstract class RecruitmentAgency
{
// This is the Factory Method - subclasses decide who to hire
public abstract IWorker HireWorker();
}
// Concrete agencies - each one decides which worker to send
public class TechAgency : RecruitmentAgency
{
public override IWorker HireWorker() => new Developer();
}
public class DesignAgency : RecruitmentAgency
{
public override IWorker HireWorker() => new Designer();
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
// Company A needs a tech worker
RecruitmentAgency agency = new TechAgency();
IWorker worker = agency.HireWorker();
worker.DoWork();
// Company B needs a design worker
RecruitmentAgency agency2 = new DesignAgency();
IWorker worker2 = agency2.HireWorker();
worker2.DoWork();
حساب کتاب:
Developer: Writing code.
Designer: Creating designs.
کمپنی نے کبھی استعمال نہیں کیا۔ new Developer() یا new Designer() براہ راست ایجنسی نے یہ فیصلہ کیا۔ یہ ایک فیکٹری طریقہ ہے۔
3. خلاصہ فیکٹری ڈیزائن پیٹرن
حقیقی کیس
فرنیچر کی دکان پر غور کریں جو مجموعہ فروخت کرتا ہے۔ اندر جائیں اور اپنا انداز منتخب کریں: جدید یا وکٹورین۔ ایک بار جب آپ انتخاب کرتے ہیں، آپ کو جو کچھ ملتا ہے وہ اسی مجموعہ سے آتا ہے۔ صوفہ، کرسیاں اور کافی ٹیبل سب ملتے ہیں۔ اسٹور میں وکٹورین کرسیوں اور جدید صوفوں کا مرکب ہے تاکہ یہ یقینی بنایا جا سکے کہ آپ کی جگہ کبھی ختم نہیں ہوگی۔ میں چاہتا ہوں کہ آپ انفرادی ٹکڑوں کو منتخب کرنے کے بجائے ساتھ چلیں۔ مجموعہ اس بات کو یقینی بناتا ہے۔
وہ تجریدی کارخانہ ہے۔ آپ ایک خاندان کا انتخاب کرتے ہیں اور فیکٹری آپ کو ایک ہی خاندان سے درکار تمام اشیاء تیار کرے گی۔ اس کی پیش کردہ ہر چیز کے ساتھ مل کر کام کرنے کی ضمانت ہے۔
مسائل حل ہوئے:
-
اگر کوئی گاہک مختلف مجموعوں سے فرنیچر کو ملاتا ہے تو کیا ہوتا ہے؟ کمرہ متضاد لگتا ہے۔ اسٹورز ہر چیز کو مجموعوں میں گروپ کرکے اس مسئلے کو حل کرتے ہیں۔ آپ ایک مجموعہ کا انتخاب کرتے ہیں اور سب کچھ اس مجموعہ سے آتا ہے۔
-
اگر میں اپنے اسٹور میں ایک نیا مجموعہ دکھانا چاہتا ہوں تو کیا ہوگا؟ ایک نیا مجموعہ سیٹ بنائیں۔ تمام موجودہ مجموعے برقرار رہیں گے۔ کسٹمر کا تجربہ تبدیل نہیں ہوتا، صرف اختیارات بڑھتے ہیں۔
-
کیا ہوگا اگر مختلف اسٹورز مختلف مجموعے فروخت کریں؟ ہر دکان اس کی اپنی فیکٹری ہے۔ گاہک کسی بھی اسٹور میں جاتے ہیں اور اسی عمل کی پیروی کرتے ہیں۔ اسٹور ہینڈل کرتا ہے کہ کون سی مخصوص مصنوعات پیش کی جائیں۔
سیدھے الفاظ میں، یہ مخصوص کلاسوں کی وضاحت کیے بغیر متعلقہ آبادیوں کو بنانے کے لیے ایک انٹرفیس فراہم کرتا ہے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"خلاصہ فیکٹری پیٹرن کنکریٹ کلاسز کو مسلط کیے بغیر متعلقہ اشیاء کے خاندانوں کو تخلیق کرنے کا ایک طریقہ فراہم کرتا ہے، انفرادی فیکٹریوں کے ایک گروپ کو سمیٹ کر جس میں ایک کنکریٹ کلاس کی وضاحت کیے بغیر ایک مشترکہ تھیم ہے۔”
ماخذ: ویکیپیڈیا – خلاصہ فیکٹری پیٹرن
پروگرامنگ کی مثال:
فرنیچر کی دکان ایک تجریدی کارخانہ ہے۔ جدید اور وکٹورین کنکریٹ کے کارخانے ہیں۔ یہ ایک صوفہ اور کرسی کی مصنوعات ہے۔
// The product interfaces - every furniture type has a contract
public interface ISofa { void Describe(); }
public interface IChair { void Describe(); }
// Modern collection
public class ModernSofa : ISofa
{
public void Describe() => Console.WriteLine("Sofa: Sleek modern design.");
}
public class ModernChair : IChair
{
public void Describe() => Console.WriteLine("Chair: Minimalist modern style.");
}
// Victorian collection
public class VictorianSofa : ISofa
{
public void Describe() => Console.WriteLine("Sofa: Ornate Victorian design.");
}
public class VictorianChair : IChair
{
public void Describe() => Console.WriteLine("Chair: Classic Victorian style.");
}
// The abstract factory - every store can produce a sofa and a chair
public interface IFurnitureFactory
{
ISofa CreateSofa();
IChair CreateChair();
}
// Concrete factories - each one produces its own collection
public class ModernFurnitureFactory : IFurnitureFactory
{
public ISofa CreateSofa() => new ModernSofa();
public IChair CreateChair() => new ModernChair();
}
public class VictorianFurnitureFactory : IFurnitureFactory
{
public ISofa CreateSofa() => new VictorianSofa();
public IChair CreateChair() => new VictorianChair();
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
// Customer orders a Modern collection
IFurnitureFactory factory = new ModernFurnitureFactory();
ISofa sofa = factory.CreateSofa();
IChair chair = factory.CreateChair();
sofa.Describe();
chair.Describe();
// Customer orders a Victorian collection
IFurnitureFactory factory2 = new VictorianFurnitureFactory();
ISofa sofa2 = factory2.CreateSofa();
IChair chair2 = factory2.CreateChair();
sofa2.Describe();
chair2.Describe();
حساب کتاب:
Sofa: Sleek modern design.
Chair: Minimalist modern style.
Sofa: Ornate Victorian design.
Chair: Classic Victorian style.
تمام ٹکڑے ایک ہی مجموعہ سے آتے ہیں۔ کلائنٹ نے کبھی استعمال نہیں کیا۔ new ModernSofa() یا new VictorianChair() براہ راست فیکٹری نے خاندان کو اکٹھا کیا۔ وہ تجریدی کارخانہ ہے۔
خلاصہ فیکٹریوں کا استعمال کرتے وقت:
جب آپ کے سسٹم کو متعلقہ اشیاء کے متعدد خاندانوں کے ساتھ کام کرنے کی ضرورت ہو اور آپ کو اس بات کو یقینی بنانا ہوگا کہ وہ ہمیشہ ایک ساتھ استعمال ہوں۔
اگر آپ باقی کوڈ کو چھوئے بغیر پوری آبادی کو ایک جگہ پر تبدیل کرنا چاہتے ہیں تو یہ بھی اچھا کام کرتا ہے۔
اگر آپ متعلقہ اشیاء میں مستقل مزاجی کو نافذ کرنا چاہتے ہیں تو یہ بھی ایک اچھا انتخاب ہے تاکہ ایک سیریز کے آئٹمز غلطی سے دوسری سیریز کے ساتھ نہ مل جائیں۔
4. بلڈر ڈیزائن پیٹرن
حقیقی کیس
سوٹ بنانے والے درزی کے بارے میں سوچیں۔ وزٹ کرنے والا ہر گاہک پیمائش لینے، تانے بانے کا انتخاب کرنے، استر کا انتخاب کرنے، بٹنوں کا انتخاب کرنے اور لیپل کے انداز کا فیصلہ کرنے کے اسی عمل سے گزرتا ہے۔
ہمارے درزی ہر آرڈر کے لیے ایک جیسے اقدامات پر عمل کرتے ہیں۔ تاہم، تیار سوٹ ہر گاہک کے لیے بالکل منفرد ہے۔ ایک تاجر صاف ستھرا سوٹ پہن کر باہر نکل رہا ہے۔ شادی کا مہمان بالکل مختلف چیز لے کر چلا جاتا ہے۔ ایک ہی عمل، ایک ہی درزی، لیکن ہر بار مختلف نتائج۔
یہ بلڈر ہے۔ تعمیر کا عمل وہی رہتا ہے۔ ہر مرحلے پر انتخاب کیا تبدیلی لاتا ہے؟
مسائل حل ہوئے:
-
کیا ہوگا اگر آپ کو بغیر کسی قدم کے سوٹ کو ایک ہی بار میں جمع کرنا پڑے؟ آپ کو پہلے سے تمام تفصیلات جاننا ہوں گی اور پہلی بار سب کچھ ٹھیک ہو جائے گا۔ درزی اسے کئی مراحل میں تقسیم کرتا ہے تاکہ ہر فیصلہ واضح طور پر کیا جائے، ایک وقت میں ایک فیصلہ۔
-
اگر دو گاہک بالکل مختلف سوٹ چاہتے ہیں، لیکن ایک ہی درزی کے پاس جانا پڑے تو کیا ہوگا؟ درزی دونوں کے لیے ایک ہی عمل کی پیروی کرتے ہیں۔ قدم نہیں بدلتے، ہر قدم کے اندر صرف انتخاب ہوتے ہیں۔
-
اگر آپ کو نیا سوٹ اسٹائل متعارف کروانا پڑے تو کیا ہوگا؟ اس انداز کے لیے انتخاب کا ایک نیا سیٹ بیان کیا گیا ہے۔ بنیاد کا عمل خود ایک ہی رہتا ہے۔
سادہ لفظوں میں، پیچیدہ اشیاء کی ساخت اور نمائندگی کو الگ کر کے، ایک ہی ساخت کا عمل مختلف نتائج پیدا کر سکتا ہے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"بلڈر پیٹرن پیچیدہ اشیاء کی تعمیر اور نمائندگی کو الگ کرتا ہے، لہذا ایک ہی تعمیراتی عمل مختلف نمائندگی پیدا کرسکتا ہے۔” (ذریعہ)
پروگرامنگ کی مثال:
درزی ایک فورمین ہوتا ہے۔ ایک سوٹ ایک پروڈکٹ ہے۔ معمار مراحل میں تعمیر کو سنبھالتے ہیں۔
// The product
public class Suit
{
public string Fabric { get; set; }
public string Lining { get; set; }
public string Buttons { get; set; }
public void Describe()
{
Console.WriteLine($"Suit: {Fabric} fabric, {Lining} lining, {Buttons} buttons.");
}
}
// The builder - defines the steps
public interface ISuitBuilder
{
void SetFabric();
void SetLining();
void SetButtons();
Suit GetSuit();
}
// Business suit builder
public class BusinessSuitBuilder : ISuitBuilder
{
private Suit _suit = new Suit();
public void SetFabric() => _suit.Fabric = "Dark wool";
public void SetLining() => _suit.Lining = "Silk";
public void SetButtons() => _suit.Buttons = "Black horn";
public Suit GetSuit() => _suit;
}
// Wedding suit builder
public class WeddingSuitBuilder : ISuitBuilder
{
private Suit _suit = new Suit();
public void SetFabric() => _suit.Fabric = "Ivory linen";
public void SetLining() => _suit.Lining = "Satin";
public void SetButtons() => _suit.Buttons = "Pearl";
public Suit GetSuit() => _suit;
}
// The tailor - the director who runs the process
public class Tailor
{
public Suit MakeSuit(ISuitBuilder builder)
{
builder.SetFabric();
builder.SetLining();
builder.SetButtons();
return builder.GetSuit();
}
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
Tailor tailor = new Tailor();
Suit businessSuit = tailor.MakeSuit(new BusinessSuitBuilder());
businessSuit.Describe();
Suit weddingSuit = tailor.MakeSuit(new WeddingSuitBuilder());
weddingSuit.Describe();
حساب کتاب:
Suit: Dark wool fabric, Silk lining, Black horn buttons.
Suit: Ivory linen fabric, Satin lining, Pearl buttons.
ایک ہی درزی دو بالکل مختلف سوٹ بنانے کے لیے ایک ہی عمل سے گزرتا ہے۔ یہ بلڈر ہے۔
بلڈر ڈیزائن پیٹرن کب استعمال کریں۔
بلڈر پیٹرن کا استعمال کریں جب کسی چیز کے بہت سے حصے یا کنفیگریشن ہوں اور ان سب کو ایک ساتھ بنانا الجھن کا باعث ہوگا۔
یہ بھی ایک اچھا انتخاب ہے اگر آپ چاہتے ہیں کہ ایک ہی تعمیراتی عمل آپ کے ہر قدم پر کیے گئے انتخاب کے لحاظ سے مختلف نتائج پیدا کرے۔
اور اس خصوصیت کا استعمال کریں اگر آپ اپنی کنفیگریشن منطق کو خود اشیاء سے الگ رکھنا چاہتے ہیں، ہر شے کو آزادانہ طور پر تبدیل کرنے کی اجازت دیتے ہیں۔
5. پروٹوٹائپ ڈیزائن پیٹرن
حقیقی کیس
تصور کریں کہ آپ ڈرائنگ ایپلی کیشن بنا رہے ہیں۔ صارف اپنے رنگ، سائز اور پوزیشن کے ساتھ دائرے، مستطیل، یا مثلث جیسی شکلیں بنا سکتے ہیں۔
اب تصور کریں کہ صارف پورے کینوس پر 10 یکساں سائز کے سرخ دائرے لگانا چاہتا ہے۔ ہر ایک کو شروع سے تخلیق کرنے کا مطلب ہے ایک ہی سیٹ اپ کو 10 بار دہرانا۔ کیا ہوگا اگر شکل بہت سی ترتیب شدہ خصوصیات کے ساتھ پیچیدہ ہے؟ یہ مہنگا اور بار بار ہو جاتا ہے.
پروٹوٹائپ پیٹرن اس مسئلے کو مکمل طور پر تشکیل شدہ شکل لے کر اور اسے نقل کرنے کی اجازت دے کر حل کرتا ہے۔ ایک کلون ایک عین مطابق کاپی کے طور پر شروع ہوتا ہے۔ اس کے بعد صارف آزادانہ طور پر حرکت کرتا ہے، دوبارہ رنگ دیتا ہے یا ان کا سائز تبدیل کرتا ہے۔ اصل شکل کو کبھی ہاتھ نہیں لگایا جاتا۔ اس کا مطلب یہ بھی ہے کہ نئی شکل کی اقسام کو رن ٹائم پر شامل کیا جا سکتا ہے بغیر ایپلی کیشن کو ان کے بارے میں پہلے سے جاننے کی ضرورت ہے۔
مسائل حل ہوئے:
-
ہر بار شروع سے ایک نئی شکل بنانا مہنگا ہے۔ اگر کسی شکل میں بہت سی خصوصیات ہیں، تو ان سب کو بار بار ترتیب دینا وسائل کا ضیاع ہے۔ پہلے سے تشکیل شدہ آبجیکٹ کو نقل کرنا بہت سستا ہے۔
-
ایپلیکیشن کو یہ جاننے کی ضرورت نہیں ہے کہ وہ کس شکل کی کاپی کر رہی ہے۔ شکلیں رن ٹائم پر متحرک طور پر شامل یا ہٹائی جا سکتی ہیں۔ ایپ کسی بھی قسم کی نقل کو کال کرتی ہے اور تیار شدہ چیز کو واپس حاصل کرتی ہے۔
-
کاپی میں ترمیم اصل پر اثر انداز نہیں ہونا چاہئے. ہر نقل شدہ شکل مکمل طور پر آزاد ہے۔ آپ اپنی کاپی میں جو بھی تبدیلیاں کرتے ہیں وہ کاپی کے ساتھ ہی رہتی ہے۔
سیدھے الفاظ میں، آپ موجودہ آبجیکٹ کو کاپی کرکے ایک نیا آبجیکٹ بنا سکتے ہیں۔ ایک کاپی اصل سے ملتی جلتی شروع ہوتی ہے اور بعد میں اسے آزادانہ طور پر تبدیل کیا جا سکتا ہے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"پروٹوٹائپ پیٹرن استعمال کیا جاتا ہے جب تخلیق کی جانے والی آبجیکٹ کی قسم کا تعین ایک پروٹو ٹائپ مثال کے ذریعہ کیا جاتا ہے جسے ایک نئی چیز بنانے کے لئے کلون کیا جاتا ہے۔” (ماخذ)
پروگرامنگ کی مثال:
ہر شکل خود کو نقل کرنا جانتی ہے۔ درخواست کال نہیں کرتی ہے۔ new Circle() یا new Rectangle() براہ راست رن ٹائم پر۔ یہ کسی ایسی چیز کو نقل کرتا ہے جو پہلے سے موجود ہے۔
// The prototype interface - every shape must be able to clone itself
public abstract class Shape
{
public string Colour { get; set; }
public int Size { get; set; }
public abstract Shape Clone();
public abstract void Describe();
}
// Concrete shapes
public class Circle : Shape
{
public override Shape Clone() => (Shape)this.MemberwiseClone();
public override void Describe() => Console.WriteLine($"Circle | Colour: {Colour} | Size: {Size}");
}
public class Rectangle : Shape
{
public override Shape Clone() => (Shape)this.MemberwiseClone();
public override void Describe() => Console.WriteLine($"Rectangle | Colour: {Colour} | Size: {Size}");
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
// Create one configured circle
Circle original = new Circle { Colour = "Red", Size = 50 };
// Clone it instead of building from scratch
Shape clone1 = original.Clone();
Shape clone2 = original.Clone();
// Modify the clones independently
clone2.Colour = "Blue";
original.Describe();
clone1.Describe();
clone2.Describe();
حساب کتاب:
Circle | Colour: Red | Size: 50
Circle | Colour: Red | Size: 50
Circle | Colour: Blue | Size: 50
clone2 یہ نیلے رنگ میں بدل گیا۔ اصل سرخ ہی رہا۔ ہر شے مکمل طور پر آزاد ہے۔ یہ پروٹو ٹائپ ہے۔
پروٹوٹائپ ڈیزائن پیٹرن کب استعمال کریں:
ایک پروٹو ٹائپ استعمال کریں جب شروع سے کوئی نئی چیز بنانا مہنگا یا پیچیدہ ہو اور ایک موجودہ آبجیکٹ پہلے سے ہی سب کچھ کنفیگر ہو چکا ہو۔
یہ اس وقت بھی مفید ہے جب آپ کی ایپلیکیشن کو رن ٹائم پر کسی چیز کو اس کی صحیح قسم کو پہلے سے جانے بغیر بنانے کی ضرورت ہو۔
اور یہ اس وقت مفید ہے جب آپ کو کسی چیز کی متعدد تغیرات کی ضرورت ہو اور آپ اسے ہر بار دوبارہ بنانے کے بجائے کسی معروف اچھی حالت سے شروع کرنا چاہتے ہیں۔
ساختی ڈیزائن پیٹرن
سادہ لفظوں میں، ساختی نمونے ہیں۔ بڑے ڈھانچے کی تشکیل کے لیے کلاسز اور اشیاء کو کیسے منظم کیا جائے۔. وراثت اور ساخت کا استعمال کرتے ہوئے، آپ ہر چیز کو شروع سے دوبارہ لکھے بغیر لچکدار اور موثر ڈھانچے بنا سکتے ہیں۔
ویکیپیڈیا اسے اس طرح بیان کرتا ہے:
"سافٹ ویئر انجینئرنگ میں، ساختی پیٹرن ایک ڈیزائن پیٹرن ہے جو اداروں کے درمیان تعلقات کو محسوس کرنے کے آسان طریقوں کی نشاندہی کرکے ڈیزائن کی سہولت فراہم کرتا ہے۔” (ماخذ)
ساختی ڈیزائن کے نمونے اس بات کی وضاحت کرتے ہیں کہ کس طرح اشیاء اور طبقات ایک ساتھ مل کر ایک شکل بناتے ہیں۔ بڑے اور زیادہ پیچیدہ ڈھانچے ان ڈھانچے کو لچکدار اور موثر رکھنے کے دوران۔
وہ وراثت کے بجائے ساخت پر توجہ مرکوز کرتے ہیں: چیزیں کیسے جڑی ہوئی ہیں، نہ کہ وہ کیا ہیں۔
ساختی ڈیزائن کے سات نمونے ہیں:
-
اڈاپٹر: ایک انٹرفیس کو دوسرے انٹرفیس میں تبدیل کرتا ہے جس کی کلائنٹ کی طرف سے توقع کی جاتی ہے، غیر موافق انٹرفیس کو ایک ساتھ کام کرنے کی اجازت دیتا ہے۔
-
ٹانگ: تجرید کو نفاذ سے الگ کرتا ہے، دونوں کو آزادانہ طور پر مختلف ہونے کی اجازت دیتا ہے۔
-
جامع: جزوی مکمل درجہ بندی کی نمائندگی کرنے کے لیے اشیاء کو درخت کے ڈھانچے میں منظم کرتا ہے، جس سے کلائنٹس کو انفرادی اشیاء اور کنفیگریشنز کو یکساں طور پر ہینڈل کرنے کی اجازت ملتی ہے۔
-
ڈیکوریٹر: کسی شے کے ساتھ اضافی ذمہ داریاں متحرک طور پر منسلک کرنے کے لیے ذیلی طبقے کا ایک لچکدار متبادل۔
-
سامنے والا: پیچیدہ ذیلی نظاموں کو ایک آسان، مربوط انٹرفیس فراہم کرتا ہے۔
-
فلائی ویٹ: شیئرنگ کا استعمال کرتے ہوئے بڑی تعداد میں باریک آبجیکٹ کو موثر طریقے سے سپورٹ کرتا ہے۔
-
نائب: اس آبجیکٹ کے لیے مندوب یا پلیس ہولڈر فراہم کرکے کسی اور چیز تک رسائی کو کنٹرول کرتا ہے۔
1. اڈاپٹر ڈیزائن پیٹرن
حقیقی کیس
کاروباری میٹنگ میں زبان کے مترجم پر غور کریں۔ ایک برطانوی سی ای او کو جاپانی ٹیم کو تقریر کرنی ہوگی۔ سی ای او صرف انگریزی بولتا ہے۔ ٹیم صرف جاپانی بولتی ہے۔ ایک مترجم ان کے درمیان بیٹھتا ہے، ہر انگریزی جملے کو جاپانی میں تبدیل کر کے ٹیم کو دے دیتا ہے۔ دونوں فریق اپنی اپنی زبان استعمال کرتے رہتے ہیں۔ نہ تو CEO اور نہ ہی ٹیم اس بارے میں کچھ بھی تبدیل کرتی ہے کہ وہ کیسے بات چیت کرتے ہیں۔ مترجم انہیں ہم آہنگ بناتا ہے۔
یہ ایک اڈاپٹر ہے۔ کلائنٹ ایک انٹرفیس بولتا ہے اور دوسری طرف دوسرا انٹرفیس بولتا ہے۔ ایک اڈاپٹر دونوں کے درمیان بیٹھتا ہے، جس سے وہ دونوں میں سے کسی ایک کو تبدیل کیے بغیر مل کر کام کر سکتے ہیں۔
مسائل حل ہوئے:
-
سی ای او جاپانی نہیں بول سکتا، اور ٹیم انگریزی نہیں بول سکتی۔ مطابقت نہیں رکھتا۔ مترجم دونوں میں سے کسی ایک کو تبدیل کیے بغیر ایک دوسرے سے موافقت کرتے ہیں۔
-
اب اگر سی ای او کو فرانسیسی ٹیم سے خطاب کرنا پڑے تو کیا ہوگا؟ ایک فرانسیسی مترجم آتا ہے۔ سی ای او کا عمل تبدیل نہیں ہوتا ہے۔ صرف مترجم بدلتا ہے۔
-
اگر کسی موجودہ کلاس میں مفید طریقے ہیں، لیکن انٹرفیس غلط ہے تو کیا ہوگا؟ اڈاپٹر کے ساتھ پیک۔ باقی نظام موجودہ کلاسوں کو برقرار رکھتے ہوئے اڈاپٹر کے ساتھ بات چیت کرتا ہے۔
سیدھے الفاظ میں، آپ ایک موجودہ کلاس کو ایک نئے انٹرفیس میں لپیٹتے ہیں تاکہ کلائنٹ اسے کسی بھی طرف کو تبدیل کیے بغیر استعمال کر سکیں۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"سافٹ ویئر انجینئرنگ میں، اڈاپٹر پیٹرن ایک سافٹ ویئر ڈیزائن پیٹرن ہے (جسے ریپر بھی کہا جاتا ہے) جو موجودہ کلاس کے انٹرفیس کو دوسرے انٹرفیس کے ذریعے استعمال کرنے کی اجازت دیتا ہے۔ یہ اکثر موجودہ کلاس کو سورس کوڈ میں ترمیم کیے بغیر کسی دوسری کلاس کے ساتھ کام کرنے کی اجازت دینے کے لیے استعمال ہوتا ہے۔” (ذریعہ)
پروگرامنگ کی مثال:
سی ای او گاہک ہے اور جاپانی ٹیم کے ارکان اڈاپٹر ہیں۔ مفید، لیکن غلط انٹرفیس استعمال کرتا ہے۔ مترجم ایک اڈاپٹر ہے۔
// What the CEO expects, someone who can receive a message in English
public interface IEnglishSpeaker
{
void Speak(string message);
}
// The Japanese team member, speaks only Japanese (the adaptee)
public class JapaneseTeamMember
{
public void SpeakJapanese(string message)
{
Console.WriteLine($"Team member (Japanese): {message}");
}
}
// The Translator, adapts the Japanese speaker to the English interface
public class Translator : IEnglishSpeaker
{
private readonly JapaneseTeamMember _teamMember;
public Translator(JapaneseTeamMember teamMember)
{
_teamMember = teamMember;
}
public void Speak(string message)
{
string translated = TranslateToJapanese(message);
_teamMember.SpeakJapanese(translated);
}
private string TranslateToJapanese(string english) => english switch
{
"Good morning, team." => "おはようございます、チームの皆さん。",
"Please review the proposal." => "提案書を確認してください。",
_ => $"[Japanese: {english}]"
};
}
// The CEO, only knows how to talk to an IEnglishSpeaker
public class CEO
{
private readonly IEnglishSpeaker _speaker;
public CEO(IEnglishSpeaker speaker)
{
_speaker = speaker;
}
public void Address(string message)
{
Console.WriteLine($"CEO (English): {message}");
_speaker.Speak(message);
}
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
JapaneseTeamMember teamMember = new JapaneseTeamMember();
IEnglishSpeaker translator = new Translator(teamMember);
CEO ceo = new CEO(translator);
ceo.Address("Good morning, team.");
ceo.Address("Please review the proposal.");
حساب کتاب:
CEO (English): Good morning, team.
Team member (Japanese): おはようございます、チームの皆さん。
CEO (English): Please review the proposal.
Team member (Japanese): 提案書を確認してください。
سی ای او کو کوئی اندازہ نہیں تھا۔ JapaneseTeamMember. ٹیم کو سی ای او کے انٹرفیس کے بارے میں کوئی اندازہ نہیں تھا۔ کہ Translator ہم نے دونوں اطراف کو کسی بھی طرف کو چھوئے بغیر ایک ساتھ کام کرنے پر مجبور کیا۔ یہ ایک اڈاپٹر ہے۔
کب استعمال کرنا ہے۔
اگر آپ موجودہ کلاس استعمال کرنا چاہتے ہیں، لیکن اس کا انٹرفیس آپ کے کوڈ کی توقع کے مطابق نہیں ہے، تو اڈاپٹر استعمال کریں۔
یہ بھی مفید ہے اگر آپ دوبارہ قابل استعمال کلاس بنانا چاہتے ہیں جو کسی ایسی کلاس کے ساتھ تعاون کرتی ہے جس کا انٹرفیس نہیں ہے۔
اور جب آپ کو تھرڈ پارٹی لائبریریوں یا لیگیسی کوڈ میں ترمیم کیے بغیر انٹیگریٹ کرنے کی ضرورت ہو تو اس سے فائدہ اٹھائیں۔
2. پل ڈیزائن پیٹرن
حقیقی کیس
ایک ٹی وی ریموٹ کنٹرول اور ٹیلی ویژن کے بارے میں سوچئے۔ ریموٹ کنٹرول ایک چیز ہے، ٹی وی دوسری چیز ہے۔ آپ بنیادی ریموٹ کنٹرول یا سمارٹ ریموٹ کنٹرول استعمال کر سکتے ہیں۔ آپ Sony TV یا Samsung TV استعمال کر سکتے ہیں۔ تمام ریموٹ کنٹرول کسی بھی کھلے ٹی وی کے ساتھ کام کرتے ہیں۔ یہاں تک کہ اگر آپ نیا Samsung TV خریدتے ہیں، تب بھی آپ کا موجودہ ریموٹ کنٹرول کام کرتا رہے گا۔ اگر آپ ایک سمارٹ یونیورسل ریموٹ خریدتے ہیں، تو یہ آپ کے اپنے ہر ٹی وی کے ساتھ کام کرے گا۔ کسی بھی فریق کو دوسرے کی اندرونی تفصیلات جاننے کی ضرورت نہیں ہے۔
یہ ایک پل ہے۔ خلاصہ (ریموٹ) اور عمل درآمد (ٹی وی) دو الگ الگ پرتیں ہیں جو ایک دوسرے سے مکمل طور پر آزادانہ طور پر بڑھ سکتی ہیں اور تبدیل ہوسکتی ہیں۔
مسائل حل ہوئے:
-
کیا ہوگا اگر آپ کے تمام ریموٹ ٹی وی کے ایک مخصوص برانڈ پر ہارڈ وائرڈ ہوں؟ آپ کو SonyBasicRemote، SamsungBasicRemote، SonySmartRemote، SamsungSmartRemote، وغیرہ کی ضرورت ہوگی۔ ہر امتزاج کے لیے ایک کلاس۔ ایک نیا TV برانڈ شامل کرنے سے دستیاب ریموٹ کلاسز کی تعداد دگنی ہو جاتی ہے۔ پل اس دھماکے کو روکتا ہے۔
-
اپنے ٹی وی کو چھوئے بغیر ریموٹ کنٹرول کی نئی قسم شامل کرنا چاہتے ہیں؟ پل آپ کو نئی ریموٹ کلاسز بنانے کی اجازت دیتا ہے۔ ٹی وی اچھوتا تھا۔
-
اگر آپ ریموٹ کنٹرول کو چھوئے بغیر نیا ٹی وی برانڈ شامل کرنا چاہتے ہیں تو کیا ہوگا؟ ایک ہی جواب۔ ایک نئی ٹی وی کلاس شامل کریں۔ تمام موجودہ ریموٹ کنٹرول پہلے سے کام کر رہے ہیں۔
سیدھے الفاظ میں، یہ ایک بڑی کلاس کو دو الگ الگ تہوں (تجزیہ اور نفاذ) میں تقسیم کرتا ہے تاکہ ہر تہہ کو دوسری تہوں کو متاثر کیے بغیر تبدیل اور بڑھایا جا سکے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"برج پیٹرن ایک ڈیزائن پیٹرن ہے جو سافٹ ویئر انجینئرنگ میں استعمال ہوتا ہے جو ایک تجرید کو اس کے نفاذ سے الگ کرتا ہے، جس سے دونوں آزادانہ طور پر مختلف ہوتے ہیں۔” (ماخذ)
پروگرامنگ کی مثال:
ریموٹ کنٹرول ایک تجریدی ہے، اور ٹی وی برانڈ ایک مجسم ہے۔ وہ ایک پل سے جڑے ہوئے ہیں ( ITV انٹرفیس)، لیکن کوئی بھی پرت دوسرے کی تفصیلات پر منحصر نہیں ہے۔
// The implementation interface — what any TV must be able to do
public interface ITV
{
void TurnOn();
void TurnOff();
void SetChannel(int channel);
void SetVolume(int volume);
}
// Concrete implementations — each brand handles things its own way
public class SonyTV : ITV
{
public void TurnOn() => Console.WriteLine("Sony TV: Powering on. BRAVIA display ready.");
public void TurnOff() => Console.WriteLine("Sony TV: Shutting down.");
public void SetChannel(int ch) => Console.WriteLine($"Sony TV: Switching to channel {ch}.");
public void SetVolume(int vol) => Console.WriteLine($"Sony TV: Volume set to {vol}.");
}
public class SamsungTV : ITV
{
public void TurnOn() => Console.WriteLine("Samsung TV: Turning on. Smart Hub loading.");
public void TurnOff() => Console.WriteLine("Samsung TV: Powering off.");
public void SetChannel(int ch) => Console.WriteLine($"Samsung TV: Channel {ch} selected.");
public void SetVolume(int vol) => Console.WriteLine($"Samsung TV: Volume at {vol}.");
}
// The abstraction — the remote holds a reference to whichever TV it controls
public abstract class RemoteControl
{
protected ITV _tv;
protected RemoteControl(ITV tv) { _tv = tv; }
public abstract void TurnOn();
public abstract void TurnOff();
public abstract void SetChannel(int channel);
}
// Refined abstraction — a basic remote, does exactly what the TV does
public class BasicRemote : RemoteControl
{
public BasicRemote(ITV tv) : base(tv) { }
public override void TurnOn() => _tv.TurnOn();
public override void TurnOff() => _tv.TurnOff();
public override void SetChannel(int ch) => _tv.SetChannel(ch);
}
// Refined abstraction — a smart remote, adds its own behaviour on top
public class SmartRemote : RemoteControl
{
public SmartRemote(ITV tv) : base(tv) { }
public override void TurnOn()
{
Console.WriteLine("Smart Remote: Activating voice control.");
_tv.TurnOn();
}
public override void TurnOff()
{
Console.WriteLine("Smart Remote: Saving watch history.");
_tv.TurnOff();
}
public override void SetChannel(int ch)
{
Console.WriteLine("Smart Remote: Looking up channel guide.");
_tv.SetChannel(ch);
}
public void SetVolume(int vol) => _tv.SetVolume(vol);
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
// Basic remote paired with a Sony TV
Console.WriteLine("--- Basic Remote + Sony TV ---");
RemoteControl basicSony = new BasicRemote(new SonyTV());
basicSony.TurnOn();
basicSony.SetChannel(5);
basicSony.TurnOff();
// Smart remote paired with a Samsung TV
Console.WriteLine("\n--- Smart Remote + Samsung TV ---");
SmartRemote smartSamsung = new SmartRemote(new SamsungTV());
smartSamsung.TurnOn();
smartSamsung.SetChannel(10);
smartSamsung.SetVolume(20);
smartSamsung.TurnOff();
// Swap freely — smart remote now with Sony, no code changes needed
Console.WriteLine("\n--- Smart Remote + Sony TV ---");
SmartRemote smartSony = new SmartRemote(new SonyTV());
smartSony.TurnOn();
smartSony.SetChannel(3);
smartSony.TurnOff();
حساب کتاب:
--- Basic Remote + Sony TV ---
Sony TV: Powering on. BRAVIA display ready.
Sony TV: Switching to channel 5.
Sony TV: Shutting down.
--- Smart Remote + Samsung TV ---
Smart Remote: Activating voice control.
Samsung TV: Turning on. Smart Hub loading.
Smart Remote: Looking up channel guide.
Samsung TV: Channel 10 selected.
Samsung TV: Volume at 20.
Smart Remote: Saving watch history.
Samsung TV: Powering off.
--- Smart Remote + Sony TV ---
Smart Remote: Activating voice control.
Sony TV: Powering on. BRAVIA display ready.
Smart Remote: Looking up channel guide.
Sony TV: Switching to channel 3.
Smart Remote: Saving watch history.
Sony TV: Shutting down.
ایک ہی SmartRemote ہم نے بغیر کسی تبدیلی کے Sony اور Samsung دونوں کے ساتھ تعاون کیا ہے۔ LG جیسے نئے TV برانڈ کو شامل کرنے کا مطلب ہے ایک نئی کلاس بنانا، اور تمام موجودہ ریموٹ باکس سے باہر کام کریں گے۔ یہ ایک پل ہے۔
کب استعمال کرنا ہے۔
ایک تجرید اور نفاذ کے درمیان مستقل پابندی سے بچنے کے لیے برج پیٹرن کا استعمال کریں اور رن ٹائم پر ایک یا دوسرے کو تبدیل کرنے کی اجازت دیں۔
آپ اسے اس وقت بھی استعمال کر سکتے ہیں جب خلاصہ اور نفاذ دونوں کو ذیلی طبقے کے ذریعے آزادانہ طور پر قابل توسیع ہونا چاہیے۔
اور یہ مناسب ہے جب نفاذ میں تبدیلیاں کلائنٹ کوڈ کو متاثر نہ کریں۔ کلائنٹ کو دوبارہ مرتب کرنے کی ضرورت نہیں ہے۔
3. جامع ڈیزائن پیٹرن
حقیقی کیس
اپنی کمپنی کے تنظیمی چارٹ کے بارے میں سوچیں۔ ایک کمپنی میں سی ای او ہوتا ہے۔ سی ای او کے نیچے محکمے کے سربراہ ہیں، جن میں سے ہر ایک ملازمین سے بھرے محکمے کی قیادت کرتا ہے۔ کچھ محکموں میں چھوٹی ذیلی ٹیمیں ہوتی ہیں۔
اب تصور کریں کہ آپ اپنی کل تنخواہ کے اخراجات جاننا چاہتے ہیں۔ آپ کسی ملازم سے اپنی تنخواہ بتانے کو کہہ سکتے ہیں۔ اگر آپ پورے محکمے سے پوچھتے ہیں، تو یہ اس کے اندر موجود ہر فرد کو شامل کرتا ہے، بشمول نیسٹڈ ٹیمیں یا آپ پوری کمپنی سے پوچھ سکتے ہیں۔ تمام سطحوں پر تمام تنخواہیں شامل کریں۔
چاہے آپ ایک شخص سے بات کر رہے ہوں یا ہزاروں سے، ایک جیسے سوالات اسی طرح پوچھے جاتے ہیں۔
یہ ایک مرکب پیٹرن ہے. انفرادی آئٹمز اور آئٹمز کے گروپس ایک ہی انٹرفیس کا اشتراک کرتے ہیں۔ کال کرنے والے کو یہ جاننے کی ضرورت نہیں ہے کہ وہ کس کے ساتھ معاملہ کر رہا ہے۔
مسائل حل ہوئے:
-
کیا ہوگا اگر آپ کو ایک ملازم اور پورے محکمہ کو سنبھالنے کے لیے مختلف کوڈ لکھنا پڑے؟ تم ختم کرو
ifآپ کس چیز کے بارے میں بات کر رہے ہیں یہ جاننے کے لیے ہر جگہ چیک کریں۔ مرکب اسے مکمل طور پر ختم کرتا ہے۔ یہ ہمیشہ ایک انٹرفیس ہے. -
کیا ہوگا اگر ایک محکمہ دوسرے محکموں پر مشتمل ہو؟ کمپوزٹ قدرتی طور پر تمام اوورلیپ گہرائیوں کو سنبھالتے ہیں۔ جب کال کرنے والا درخت کے اوپر سے درخواست کرتا ہے، تو کام خود بخود نیچے آجاتا ہے۔
-
اگر میں نئی قسم کی ٹیم یا کردار شامل کرنا چاہتا ہوں تو کیا ہوگا؟ ایک ہی انٹرفیس کو لاگو کرتا ہے۔ درخت میں اس کے اوپر کی ہر چیز بغیر کسی تبدیلی کے کام کرتی رہتی ہے۔
سیدھے الفاظ میں، یہ اشیاء کو درخت کی ساخت میں ترتیب دیتا ہے۔ یہ انفرادی اشیاء اور اشیاء کے گروپوں کو ایک ہی انٹرفیس کے ذریعے ہینڈل کرنے کی اجازت دیتا ہے، لہذا کال کرنے والوں کو فرق کے بارے میں فکر کرنے کی ضرورت نہیں ہے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"ایک جامع پیٹرن اشیاء کے ایک گروپ کی وضاحت کرتا ہے جس کے ساتھ ایک ہی قسم کی شے کی ایک مثال کے طور پر اسی طرح برتاؤ کیا جاتا ہے۔ جامع کا مقصد اشیاء کو درخت کے ڈھانچے میں ترتیب دے کر جزوی مکمل درجہ بندی کی نمائندگی کرنا ہے۔” (ذریعہ)
پروگرامنگ کی مثال:
درخت میں ہر نوڈ، چاہے ایک ملازم ہو یا پورا محکمہ، لاگو کرتا ہے: IEmployee. بھیجنے والا بھی ان کے ساتھ ایسا ہی سلوک کرتا ہے۔
// The component interface — every leaf and composite shares this contract
public interface IEmployee
{
string Name { get; }
int GetSalary();
void GetDetails(string indent = "");
}
// The leaf — a single employee with no reports
public class Employee : IEmployee
{
private readonly int _salary;
public string Name { get; }
public Employee(string name, int salary)
{
Name = name;
_salary = salary;
}
public int GetSalary() => _salary;
public void GetDetails(string indent = "") => Console.WriteLine($"{indent}- {Name} (£{_salary:N0})");
}
// The composite — a department that holds employees or other departments
public class Department : IEmployee
{
private readonly List _members = new();
public string Name { get; }
public Department(string name) { Name = name; }
public void Add(IEmployee employee) => _members.Add(employee);
public void Remove(IEmployee employee) => _members.Remove(employee);
public int GetSalary() => _members.Sum(m => m.GetSalary());
public void GetDetails(string indent = "")
{
Console.WriteLine($"{indent}[{Name}] Total: £{GetSalary():N0}");
foreach (var member in _members)
member.GetDetails(indent + " ");
}
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
// Individual employees
var ceo = new Employee("Alice (CEO)", 120_000);
var cto = new Employee("Bob (CTO)", 95_000);
var dev1 = new Employee("Carol (Developer)", 65_000);
var dev2 = new Employee("David (Developer)", 62_000);
var cfo = new Employee("Eve (CFO)", 90_000);
var accountant = new Employee("Frank (Accountant)", 55_000);
// Build the Engineering department
var engineering = new Department("Engineering");
engineering.Add(cto);
engineering.Add(dev1);
engineering.Add(dev2);
// Build the Finance department
var finance = new Department("Finance");
finance.Add(cfo);
finance.Add(accountant);
// Build the whole company
var company = new Department("Acme Corp");
company.Add(ceo);
company.Add(engineering);
company.Add(finance);
// Ask the whole company — one call, rolls up everything
Console.WriteLine("=== Full Company ===");
company.GetDetails();
// Ask just one department — same call, same interface
Console.WriteLine("\n=== Engineering Only ===");
engineering.GetDetails();
// Ask a single employee — same call, same interface
Console.WriteLine("\n=== Single Employee ===");
dev1.GetDetails();
حساب کتاب:
=== Full Company ===
[Acme Corp] Total: £487,000
- Alice (CEO) (£120,000)
[Engineering] Total: £222,000
- Bob (CTO) (£95,000)
- Carol (Developer) (£65,000)
- David (Developer) (£62,000)
[Finance] Total: £145,000
- Eve (CFO) (£90,000)
- Frank (Accountant) (£55,000)
=== Engineering Only ===
[Engineering] Total: £222,000
- Bob (CTO) (£95,000)
- Carol (Developer) (£65,000)
- David (Developer) (£62,000)
=== Single Employee ===
- Carol (Developer) (£65,000)
company.GetDetails(), engineering.GetDetails()اور dev1.GetDetails(): درخت کی تین مختلف سطحوں پر ایک ہی کال کی جاتی ہے۔ کال کرنے والے نے کال کے مواد کی تصدیق نہیں کی۔ یہ ایک مرکب پیٹرن ہے.
کب استعمال کرنا ہے۔
جامع استعمال کریں جب آپ کو جزوی مکمل درجہ بندی کی نمائندگی کرنے کی ضرورت ہو، جیسے کہ ایک درخت، جہاں انفرادی اشیاء اور اشیاء کے گروپوں کو ایک دوسرے کے بدلے استعمال کیا جانا چاہیے۔
یہ اس وقت بھی اچھا کام کرتا ہے جب آپ چاہتے ہیں کہ آپ کا کلائنٹ کوڈ کسی خاص کیس کی حساسیت کے بغیر، واحد اشیاء اور اشیاء کے مجموعوں کو یکساں طور پر ہینڈل کرے۔
اور یہ مناسب ہے جب ڈھانچے کو من مانی گہرائیوں میں بنایا جا سکتا ہے اور اس گہرائی کو متاثر نہیں ہونا چاہئے کہ کال کرنے والے ساخت کے ساتھ کیسے تعامل کرتے ہیں۔
4. ڈیکوریٹر ڈیزائن پیٹرن
حقیقی کیس
ایک کیفے میں کافی کا آرڈر دینے کا تصور کریں۔ سادہ یسپریسو کے ساتھ شروع کریں۔ پھر دودھ مانگیں۔ پھر ونیلا شربت۔ پھر اوپر وائپڈ کریم ڈالیں۔ ہر ایک اضافہ اس کی اپنی قیمت اور تفصیل کو شامل کرتے ہوئے پہلے سے موجود چیزوں کو لپیٹتا ہے یا شامل کرتا ہے۔ مرکز میں یسپریسو کبھی تبدیل نہیں ہوتا ہے۔ صرف ایک وقت میں ان کو شامل کریں۔ آپ شربت کے 2 شاٹس بھی شامل کر سکتے ہیں۔ آپ دودھ کو بھی مکمل طور پر چھوڑ سکتے ہیں۔ ہر ایک کے لیے ایک نئی قسم کی کافی بنائے بغیر تمام امتزاج ممکن ہیں۔
یہ ڈیکوریٹر پیٹرن ہے. ایک بنیادی چیز سے شروع کریں اور اسے تہوں میں لپیٹیں۔ ہر پرت اپنا اپنا رویہ شامل کرتی ہے اور پھر اسے اپنے نیچے کی اشیاء میں تفویض کرتی ہے۔
مسائل حل ہوئے:
-
کیا ہوگا اگر ہمیں ہر امتزاج کے لیے ایک کلاس کی ضرورت ہو؟ EspressoWithMilk, EspressoWithMilkAndVanilla, EspressoWithMilkAndVanillaAndCream… فہرست پھٹ رہی ہے۔ ڈیکوریٹر رن ٹائم پر رویے کو شامل کرتے ہیں، لہذا آپ کو متعلقہ کلاس کی ضرورت نہیں ہے۔
-
اگر بیس کافی تبدیل نہیں ہوتی ہے تو کیا ہوگا؟ یہ سچ نہیں ہے۔ ایسپریسو کی کلاسیں وہی رہتی ہیں۔ ایک ڈیکوریٹر اسے لپیٹتا ہے اور اسے آزادانہ طور پر بڑھاتا ہے۔
-
اگر مجھے نئی ٹاپنگز شامل کرنے کی ضرورت ہو تو کیا ہوگا؟ ایک نئی ڈیکوریٹر کلاس بنائیں۔ تمام موجودہ امتزاج پہلے کی طرح کام کرتے ہیں۔
سیدھے الفاظ میں، یہ کسی چیز کو ایک یا زیادہ تہوں میں لپیٹتا ہے۔ یہاں، ہر پرت اپنے نیچے کی پرت کو تفویض کرنے سے پہلے یا بعد میں اپنا رویہ شامل کرتی ہے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"ڈیکوریٹر پیٹرن ایک ڈیزائن پیٹرن ہے جو آپ کو ایک ہی طبقے کی دوسری مثالوں کے رویے کو متاثر کیے بغیر متحرک طور پر انفرادی اشیاء میں رویے کو شامل کرنے کی اجازت دیتا ہے۔” (ذریعہ)
پروگرامنگ کی مثال:
کافی ایک جزو ہے۔ ہر ٹاپنگ ڈیکوریٹر ہے۔ تمام ڈیکوریٹر اجزاء کو لپیٹتے ہیں اور تفصیل اور اخراجات شامل کرتے ہیں۔
// The component interface — every coffee, plain or decorated, shares this
public interface ICoffee
{
string GetDescription();
double GetCost();
}
// The base component — a plain espresso
public class Espresso : ICoffee
{
public string GetDescription() => "Espresso";
public double GetCost() => 1.00;
}
// The base decorator — wraps any ICoffee and delegates to it
public abstract class CoffeeDecorator : ICoffee
{
protected readonly ICoffee _coffee;
protected CoffeeDecorator(ICoffee coffee) { _coffee = coffee; }
public virtual string GetDescription() => _coffee.GetDescription();
public virtual double GetCost() => _coffee.GetCost();
}
// Concrete decorators — each one adds its own layer
public class Milk : CoffeeDecorator
{
public Milk(ICoffee coffee) : base(coffee) { }
public override string GetDescription() => _coffee.GetDescription() + ", Milk";
public override double GetCost() => _coffee.GetCost() + 0.30;
}
public class VanillaSyrup : CoffeeDecorator
{
public VanillaSyrup(ICoffee coffee) : base(coffee) { }
public override string GetDescription() => _coffee.GetDescription() + ", Vanilla Syrup";
public override double GetCost() => _coffee.GetCost() + 0.50;
}
public class WhippedCream : CoffeeDecorator
{
public WhippedCream(ICoffee coffee) : base(coffee) { }
public override string GetDescription() => _coffee.GetDescription() + ", Whipped Cream";
public override double GetCost() => _coffee.GetCost() + 0.75;
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
// A plain espresso
ICoffee order = new Espresso();
Console.WriteLine($"{order.GetDescription()} => £{order.GetCost():F2}");
// Wrap it with milk
order = new Milk(order);
Console.WriteLine($"{order.GetDescription()} => £{order.GetCost():F2}");
// Wrap it with vanilla syrup on top
order = new VanillaSyrup(order);
Console.WriteLine($"{order.GetDescription()} => £{order.GetCost():F2}");
// Wrap it with whipped cream on top of that
order = new WhippedCream(order);
Console.WriteLine($"{order.GetDescription()} => £{order.GetCost():F2}");
حساب کتاب:
Espresso => £1.00
Espresso, Milk => £1.30
Espresso, Milk, Vanilla Syrup => £1.80
Espresso, Milk, Vanilla Syrup, Whipped Cream => £2.55
ہر لائن ایک نئی پرت ہے جو پچھلی لائن کے گرد لپیٹتی ہے۔ ایسپریسو تبدیل نہیں ہوا ہے۔ جب بھی ہم نے ریپنگ پیپر بنایا، لاگت اور ہدایات میں اضافہ ہوا۔ یہ ڈیکوریٹر پیٹرن ہے.
کب استعمال کرنا ہے۔
ایک ہی کلاس کی دیگر اشیاء کو متاثر کیے بغیر انفرادی اشیاء میں ذمہ داریاں شامل کرنے کے لیے ڈیکوریٹر پیٹرن کا استعمال کریں۔
یہ بھی ایک اچھا انتخاب ہے جب ذیلی کلاس کرنے سے کلاسوں کے دھماکے ہوتے ہیں جو تمام ممکنہ رویے کے امتزاج کا احاطہ کرتے ہیں۔
آپ اسے اس وقت بھی استعمال کرتے ہیں جب آپ کو رن ٹائم پر ایک دوسرے سے آزادانہ طور پر اور کسی بھی ترتیب میں آپریشنز کو اسٹیک کرنے کے قابل ہونے کی ضرورت ہو۔
5. بیرونی ڈیزائن پیٹرن
حقیقی کیس
کسی شاپنگ ویب سائٹ پر "پلیس آرڈر” پر کلک کرنے کا تصور کریں۔ ایک کلک پردے کے پیچھے کئی کام انجام دیتا ہے۔ سسٹم چیک کرے گا کہ آیا آئٹم اسٹاک میں ہے، آپ کی ادائیگی چارج کرے گا، شپنگ لیبل بنائے گا، اور آپ کو ایک تصدیقی ای میل بھیجے گا۔ تم کچھ نہیں دیکھ سکتے۔ ایک بٹن پر کلک کریں اور ایک نتیجہ حاصل کریں۔ ایک صاف آپریشن کے پیچھے چار الگ الگ نظاموں کی پیچیدگی ہے۔
یہ Facade پیٹرن ہے. بہت سے پیچیدہ متحرک حصوں کے سامنے ایک سادہ انٹرفیس۔ کال کرنے والے کو یہ جاننے کی ضرورت نہیں ہے کہ پردے کے پیچھے کیا ہو رہا ہے۔
مسائل حل ہوئے:
-
کیا ہوگا اگر کلائنٹ کو ہر سب سسٹم کو براہ راست کال کرنا پڑے؟ ہم انوینٹری چیک کرتے ہیں، پھر ادائیگیوں پر کارروائی کرتے ہیں، لیبل بناتے ہیں، اور ہر ناکامی کو انفرادی طور پر سنبھالنے کے لیے درست ترتیب میں ای میلز بھیجتے ہیں۔ اگواڑا ان سب کو ایک کال میں بنڈل کرتا ہے۔
-
اگر ذیلی نظاموں میں سے ایک بدل جائے تو کیا ہوگا؟ اگواڑا تبدیلی جذب کرتا ہے۔ آپ کو کلائنٹ کوڈ جاننے کی ضرورت نہیں ہے۔ صرف اگواڑا اپ ڈیٹ کیا گیا ہے۔
-
اگر مختلف کلائنٹس کو ایک ہی فلو کی ضرورت ہو تو کیا ہوگا؟ وہ سب ایک ہی Facade طریقہ کہتے ہیں۔ منطق تمام کال کرنے والوں میں نقل کرنے کے بجائے ایک جگہ پر ہے۔
سیدھے الفاظ میں، یہ ایک سادہ، واحد انٹرفیس فراہم کرتا ہے جو ذیلی نظاموں کے سیٹ کی پیچیدگی کو چھپاتا ہے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"Façade پیٹرن (Façade کے نام سے بھی جانا جاتا ہے) ایک سافٹ ویئر ڈیزائن پیٹرن ہے جو عام طور پر آبجیکٹ پر مبنی پروگرامنگ میں استعمال ہوتا ہے۔ فن تعمیر میں اگواڑے کی طرح، ایک اگواڑا ایک ایسی چیز ہے جو سامنے والے انٹرفیس کے طور پر کام کرتی ہے جو زیادہ پیچیدہ بنیادی یا ساختی کوڈ کو چھپاتا ہے۔”(ذریعہ)
پروگرامنگ کی مثال:
ہر ذیلی نظام اپنا کام انجام دیتا ہے۔ کہ OrderFacade ہر چیز کو مربوط کرنے کے لیے ایک داخلی نقطہ۔ کلائنٹ ہمیشہ صرف Facade سے بات کرتے ہیں۔
// Subsystem 1: checks whether the item is available
public class InventoryService
{
public bool CheckStock(string item)
{
Console.WriteLine($"Inventory: Checking stock for {item}.");
return true;
}
}
// Subsystem 2: handles the payment
public class PaymentService
{
public bool ProcessPayment(string cardNumber, double amount)
{
Console.WriteLine($"Payment: Charging £{amount:F2} to card ending {cardNumber[^4..]}.");
return true;
}
}
// Subsystem 3: generates a shipping label
public class ShippingService
{
public string GenerateLabel(string item, string address)
{
Console.WriteLine($"Shipping: Generating label for {item} to {address}.");
return "TRACK-29384";
}
}
// Subsystem 4: sends the confirmation email
public class EmailService
{
public void SendConfirmation(string email, string trackingCode)
{
Console.WriteLine($"Email: Confirmation sent to {email}. Tracking code: {trackingCode}.");
}
}
// The Facade — one method, hides all four subsystems
public class OrderFacade
{
private readonly InventoryService _inventory = new();
private readonly PaymentService _payment = new();
private readonly ShippingService _shipping = new();
private readonly EmailService _email = new();
public void PlaceOrder(string item, string cardNumber, double amount, string address, string email)
{
Console.WriteLine("=== Placing Order ===");
if (!_inventory.CheckStock(item))
{
Console.WriteLine("Order failed: item out of stock.");
return;
}
if (!_payment.ProcessPayment(cardNumber, amount))
{
Console.WriteLine("Order failed: payment declined.");
return;
}
string trackingCode = _shipping.GenerateLabel(item, address);
_email.SendConfirmation(email, trackingCode);
Console.WriteLine($"\nOrder complete. Your tracking code is {trackingCode}.");
}
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
OrderFacade store = new OrderFacade();
store.PlaceOrder(
item: "Wireless Headphones",
cardNumber: "4111111111111234",
amount: 79.99,
address: "42 Maple Street, London",
email: "customer@email.com"
);
حساب کتاب:
=== Placing Order ===
Inventory: Checking stock for Wireless Headphones.
Payment: Charging £79.99 to card ending 1234.
Shipping: Generating label for Wireless Headphones to 42 Maple Street, London.
Email: Confirmation sent to customer@email.com. Tracking code: TRACK-29384.
Order complete. Your tracking code is TRACK-29384.
کلائنٹ نے ایک طریقہ کہا۔ چار ذیلی نظاموں کو درست ترتیب میں عمل میں لایا گیا۔ اس پیچیدگی میں سے کوئی بھی کال کرنے والے کو نظر نہیں آرہا تھا۔ وہ اگواڑا ہے۔
کب استعمال کرنا ہے۔
ایک Facade استعمال کریں جب آپ ایک پیچیدہ سب سسٹم کو ایک سادہ انٹرفیس فراہم کرنا چاہتے ہیں بغیر کال کرنے والے کو اس کے اندرونی حصوں سے مغلوب کئے۔
اگر آپ اپنے سسٹم کی تہہ لگانا چاہتے ہیں تو یہ بھی اچھا کام کرتا ہے تاکہ اعلیٰ سطح کا کوڈ نچلے درجے کے سب سسٹم کے بجائے Facade سے براہ راست بات چیت کرے۔
یہ بھی ایک اچھا انتخاب ہے جب آپ کو ایک سے زیادہ سروسز میں مراحل کی ایک سیریز کو مربوط کرنے کے لیے ایک ہی انٹری پوائنٹ کی ضرورت ہو۔
6. فلائی ویٹ ڈیزائن پیٹرن
حقیقی کیس
ایک ایسے کھیل پر غور کریں جو جنگل کو پیش کرتا ہے۔ جنگل میں دس ہزار درخت ہیں۔ ہر درخت کی ایک قسم کا نام، رنگ اور ساخت ہوتا ہے۔ لیکن ان میں سے زیادہ تر درخت بلوط کے درخت ہیں، اور تمام بلوط کے درخت ایک جیسے نظر آتے ہیں۔
10,000 انفرادی اشیاء بنانا، ہر ایک ایک ہی نام، رنگ، اور ساخت کو ذخیرہ کرتا ہے، میموری کی ایک بڑی مقدار کو ضائع کرتا ہے۔ اس کے بجائے، ہم ایک مشترکہ اوک آبجیکٹ بناتے ہیں جس میں تمام ڈیٹا ہوتا ہے۔ جنگل میں بلوط کے تمام درخت ایک ہی مشترکہ چیز کی طرف اشارہ کرتے ہیں اور نقشے پر صرف اپنا مقام محفوظ کرتے ہیں۔
یہ فلائی ویٹ پیٹرن ہے۔ ایک ہی ڈیٹا کو متعدد مثالوں میں شیئر کیا جاتا ہے۔ ہر مثال کے لیے منفرد ڈیٹا کو الگ سے ذخیرہ کیا جاتا ہے اور صرف ضرورت کے وقت ڈیلیور کیا جاتا ہے۔
مسائل حل ہوئے:
-
کیا ہوگا اگر ہم ہر ایک درخت کے لیے ایک مکمل آبجیکٹ بنائیں؟ 10,000 درخت ایک ہی نام، رنگ اور ساخت کو 10،000 بار محفوظ کرتے ہیں۔ فلائی ویٹ اسٹورز ایک بار ڈیٹا شیئر کرتے ہیں اور اسے کہیں بھی دوبارہ استعمال کرتے ہیں۔
-
کیا ہوگا اگر درخت کی ایک نئی نسل متعارف کرائی جائے؟ فیکٹری اس کے لیے ایک نئی مشترکہ چیز بناتی ہے۔ اس قسم کے تمام درخت بغیر کسی اضافی میموری کے فوری طور پر استعمال ہوتے ہیں۔
-
کیا ہوگا اگر جنگل کو ہر درخت کو اس کے اپنے مقام پر دینا پڑے؟ مقام فی درخت منفرد ہے، لہذا اسے درخت میں ہی ذخیرہ کیا جاتا ہے اور صرف رینڈر کے وقت مشترکہ آبجیکٹ کو منتقل کیا جاتا ہے۔ مشترکہ اشیاء انہیں کبھی نہیں روکتی ہیں۔
سیدھے الفاظ میں، یہ ایک آبجیکٹ کے ڈیٹا کو ڈیٹا میں تقسیم کرتا ہے جو متعدد مثالوں میں شیئر کیا جاتا ہے اور ڈیٹا جو ہر مثال کے لیے منفرد ہوتا ہے۔ ہم کچھ مشترک ہیں۔ صرف جب ضروری ہو تو منفرد حصوں کو پاس کریں۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"ایک فلائی ویٹ ایک ایسی چیز ہے جو دیگر ملتی جلتی اشیاء کے ساتھ زیادہ سے زیادہ ڈیٹا شیئر کرکے میموری کے استعمال کو کم کرتی ہے۔ جب سادہ تکراری نمائندگی میموری کی ناقابل قبول مقدار کا استعمال کرتی ہے تو یہ اشیاء کو بلک کرنے کا طریقہ ہے۔” (ذریعہ)
پروگرامنگ کی مثال L
TreeType یہ فلائی ویٹ ہے۔ ہم نے ڈیٹا شیئر کیا ہے۔ Tree یہ صرف منفرد اور مشترکہ مقامات کے حوالے رکھتا ہے۔ TreeType. فیکٹری ہر ایک کی ضمانت دیتا ہے۔ TreeType یہ صرف ایک بار پیدا ہوتا ہے۔
// The flyweight — holds shared intrinsic state (same for all trees of this type)
public class TreeType
{
public string Name { get; }
public string Colour { get; }
public string Texture { get; }
public TreeType(string name, string colour, string texture)
{
Name = name;
Colour = colour;
Texture = texture;
}
public void Render(int x, int y)
{
Console.WriteLine($"Rendering {Name} tree ({Colour}, {Texture}) at ({x}, {y})");
}
}
// The flyweight factory — creates and caches tree types so they are never duplicated
public class TreeTypeFactory
{
private readonly Dictionary _cache = new();
public TreeType GetTreeType(string name, string colour, string texture)
{
string key = $"{name}_{colour}_{texture}";
if (!_cache.ContainsKey(key))
{
Console.WriteLine($"Factory: Creating new TreeType for '{name}'.");
_cache[key] = new TreeType(name, colour, texture);
}
return _cache[key];
}
public int TotalTypes => _cache.Count;
}
// The context — holds unique extrinsic state (position) and a reference to a shared flyweight
public class Tree
{
private readonly int _x;
private readonly int _y;
private readonly TreeType _type;
public Tree(int x, int y, TreeType type)
{
_x = x;
_y = y;
_type = type;
}
public void Render() => _type.Render(_x, _y);
}
// The forest — plants trees using shared flyweights
public class Forest
{
private readonly List _trees = new();
private readonly TreeTypeFactory _factory = new();
public void PlantTree(int x, int y, string name, string colour, string texture)
{
TreeType type = _factory.GetTreeType(name, colour, texture);
_trees.Add(new Tree(x, y, type));
}
public void Render()
{
foreach (var tree in _trees)
tree.Render();
}
public int TreeCount => _trees.Count;
public int TreeTypeCount => _factory.TotalTypes;
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
Forest forest = new Forest();
// Plant 6 trees — but only 2 unique types
forest.PlantTree(1, 5, "Oak", "Dark Green", "Rough bark");
forest.PlantTree(3, 12, "Oak", "Dark Green", "Rough bark");
forest.PlantTree(7, 2, "Oak", "Dark Green", "Rough bark");
forest.PlantTree(10, 8, "Pine", "Light Green", "Smooth bark");
forest.PlantTree(15, 3, "Pine", "Light Green", "Smooth bark");
forest.PlantTree(20, 14, "Pine", "Light Green", "Smooth bark");
forest.Render();
Console.WriteLine($"\nTrees planted: {forest.TreeCount}");
Console.WriteLine($"Unique tree types in memory: {forest.TreeTypeCount}");
حساب کتاب:
Factory: Creating new TreeType for 'Oak'.
Factory: Creating new TreeType for 'Pine'.
Rendering Oak tree (Dark Green, Rough bark) at (1, 5)
Rendering Oak tree (Dark Green, Rough bark) at (3, 12)
Rendering Oak tree (Dark Green, Rough bark) at (7, 2)
Rendering Pine tree (Light Green, Smooth bark) at (10, 8)
Rendering Pine tree (Light Green, Smooth bark) at (15, 3)
Rendering Pine tree (Light Green, Smooth bark) at (20, 14)
Trees planted: 6
Unique tree types in memory: 2
چھ درخت ہیں، لیکن صرف دو۔ TreeType ایک آبجیکٹ بنایا گیا ہے۔ اگر آپ اسے 10,000 درختوں تک بڑھا دیتے ہیں، تو فیکٹری اب بھی بالکل 2 درخت پیدا کرے گی۔ ہر درخت کا الگ مقام ہوتا ہے، لیکن اس کی ظاہری شکل مشترکہ ہوتی ہے۔ یہ فلائی ویٹ ہے۔
کب استعمال کرنا ہے۔
فلائی ویٹ کا استعمال کریں جب آپ کی ایپلیکیشن کو بہت زیادہ تعداد میں ملتے جلتے اشیاء بنانے کی ضرورت ہو جو بہت زیادہ میموری استعمال کرے۔
یہ اس وقت بھی کارآمد ہے جب کسی چیز کی زیادہ تر حالت کو مثالوں کے درمیان شیئر کیا جاسکتا ہے، صرف ایک چھوٹا سا حصہ فی مثال منفرد ہوتا ہے۔
اور یہ ایک اچھا انتخاب ہے اگر آپ ریاست کے انوکھے حصوں کو ہر چیز کے اندر ذخیرہ کرنے کے بجائے باہر منتقل کر سکتے ہیں۔
7. پراکسی ڈیزائن پیٹرن
حقیقی کیس
دفتر کی عمارت کے دروازے پر ایک حفاظتی گارڈ کے بارے میں سوچئے۔ آپ براہ راست عمارت میں داخل نہیں ہو سکتے۔ آپ کو پہلے گارڈ سے گزرنا ہوگا۔ سیکیورٹی گارڈ آپ کا نام منظور شدہ فہرست میں چیک کرے گا، آپ کے دورے کو ریکارڈ کرے گا، اور پھر آپ کو جانے دے گا۔ اگر آپ فہرست میں شامل نہیں ہیں، تو آپ کو مسترد کر دیا جائے گا۔ عمارت خود اس میں سے کسی سے نمٹتی نہیں ہے۔ ہم صرف لوگوں کو اندر جانے کی اجازت دیتے ہیں۔ عمارت کے شامل ہونے سے پہلے تمام چیک، ریکارڈ اور فیصلے سیکیورٹی گارڈ (نائب) کرتے ہیں۔
یہ پراکسی پیٹرن ہے۔ یہ کال کرنے والے اور اصل آبجیکٹ کے درمیان بیٹھتا ہے، جو آپ کو کنٹرول کرنے کی اجازت دیتا ہے کہ کیا گزرتا ہے اور اصل چیز کو جانے بغیر رسائی کی جانچ پڑتال یا لاگنگ جیسے رویے کو شامل کرتا ہے۔
مسائل حل ہوئے:
-
کیا ہوگا اگر کوئی سیدھا عمارت میں جا سکتا ہے؟ رسائی کا کوئی کنٹرول نہیں ہے۔ پراکسی ہر درخواست کو روکتی ہے اور فیصلہ کرتی ہے کہ اسے اجازت دی جائے یا نہیں۔
-
اگر مجھے عمارت میں کوئی تبدیلی کیے بغیر سب کچھ ریکارڈ کرنے کی ضرورت ہو تو کیا ہوگا؟ پراکسی اس کا خیال رکھتی ہے۔ اصل عمارت سادہ رہتی ہے اور اپنے کام پر مرکوز رہتی ہے۔
-
کیا ہوگا اگر اصل چیز بنانا مہنگا ہے اور آپ اس میں تاخیر کرنا چاہتے ہیں؟ پراکسی ان کو بنانے سے روک سکتے ہیں جب تک کہ کوئی حقیقت میں چیک پاس نہ کر لے اور اسے ان کی ضرورت ہو۔
سیدھے الفاظ میں، آپ کسی چیز کو کسی دوسری چیز کے سامنے رکھ کر اس تک رسائی کو کنٹرول کرتے ہیں۔ کال کرنے والا سوچتا ہے کہ وہ اصل چیز کے ساتھ براہ راست بات کر رہا ہے، لیکن پراکسی اسے پہلے ہینڈل کرتی ہے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"پراکسی کی سب سے عام شکل ایک کلاس ہے جو کسی اور چیز کے لیے انٹرفیس کے طور پر کام کرتی ہے۔ ایک پراکسی نیٹ ورک کنکشن، میموری میں موجود ایک بڑی چیز، فائل، یا کسی دوسرے وسائل کے ساتھ انٹرفیس کر سکتی ہے جسے نقل کرنا مہنگا یا ناممکن ہے۔” (ماخذ)
پروگرامنگ کی مثال:
کلائنٹ گفتگو کر رہا ہے۔ IBuilding. کہ SecurityGuard یہ ایک پراکسی ہے۔ ایک ہی انٹرفیس کو لاگو کریں، رسائی کو کنٹرول کریں، اور صرف مجاز زائرین۔ OfficeBuilding.
// The subject interface — the building and the proxy both implement this
public interface IBuilding
{
void Enter(string visitorName);
}
// The real subject — the actual building, just grants entry
public class OfficeBuilding : IBuilding
{
public void Enter(string visitorName)
{
Console.WriteLine($"Building: {visitorName} has entered.");
}
}
// The proxy — the security guard controls who gets through
public class SecurityGuard : IBuilding
{
private readonly OfficeBuilding _building = new();
private readonly List _authorisedVisitors = new() { "Alice", "Bob", "Carol" };
public void Enter(string visitorName)
{
Console.WriteLine($"Guard: {visitorName} is requesting entry.");
if (_authorisedVisitors.Contains(visitorName))
{
Console.WriteLine("Guard: ID verified. Access granted.");
_building.Enter(visitorName);
}
else
{
Console.WriteLine($"Guard: {visitorName} is not on the list. Access denied.");
}
}
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
IBuilding entrance = new SecurityGuard();
entrance.Enter("Alice");
Console.WriteLine();
entrance.Enter("David");
Console.WriteLine();
entrance.Enter("Bob");
حساب کتاب:
Guard: Alice is requesting entry.
Guard: ID verified. Access granted.
Building: Alice has entered.
Guard: David is requesting entry.
Guard: David is not on the list. Access denied.
Guard: Bob is requesting entry.
Guard: ID verified. Access granted.
Building: Bob has entered.
ایک کلائنٹ نے فون کیا۔ Enter() جس کے بارے میں میں نے سوچا کہ ایک عمارت تھی۔ وہ دراصل ایک سیکورٹی گارڈ تھا۔ گارڈ نے فیصلہ کیا کہ کیا ہوا ہے۔ عمارت صرف ان لوگوں کے لیے دکھائی دے رہی تھی جن کے اندر سے گزرنے کی اجازت تھی۔ یہی پراکسی ہے۔
کب استعمال کرنا ہے۔
اگر آپ کو رسائی کنٹرول کی ضرورت ہے، جیسے کہ صرف مخصوص کال کرنے والوں کو اصل چیز سے گزرنے کی اجازت دینا، پراکسی استعمال کریں۔
یہ مثالی ہے اگر آپ اصل چیز کو تبدیل کیے بغیر لاگنگ، کیشنگ، یا توثیق جیسے رویے کو شامل کرنا چاہتے ہیں۔
اور آپ اس فیچر کو اس وقت استعمال کر سکتے ہیں جب اصل چیز بنانا مہنگا ہو اور آپ اس کی تخلیق میں تاخیر یا حفاظت کرنا چاہتے ہیں جب تک کہ آپ کو درحقیقت اس کی ضرورت نہ ہو۔
طرز عمل ڈیزائن پیٹرن
سیدھے الفاظ میں، رویے کے نمونے سب کچھ ہیں۔ اشیاء کیسے بات چیت کرتی ہیں اور ذمہ داری کا اشتراک کرتی ہیں۔. یہ اشیاء کے درمیان ذمہ داریاں تفویض کرنے پر توجہ مرکوز کرتا ہے اور کاموں کو مکمل کرنے کے لیے وہ کیسے مل کر کام کرتے ہیں۔
ویکیپیڈیا اسے اس طرح بیان کرتا ہے:
"سافٹ ویئر انجینئرنگ میں، رویے کا ڈیزائن پیٹرن ایک ڈیزائن پیٹرن ہے جو اشیاء کے درمیان عام مواصلات کے پیٹرن کی شناخت کرتا ہے. ایسا کرنے سے، یہ پیٹرن اس مواصلات کو انجام دینے میں زیادہ لچک فراہم کرتے ہیں.” (ماخذ)
طرز عمل کے ڈیزائن کے نمونے نہ صرف یہ بیان کرتے ہیں کہ اشیاء کی ساخت کیسے بنتی ہے، بلکہ یہ بھی کہ وہ کس طرح بات چیت کرتے ہیں اور ذمہ داریوں کو تقسیم کرتے ہیں۔ وہ اشیاء کے درمیان رابطے پر توجہ مرکوز کرتے ہیں: کون کس سے بات کر رہا ہے اور ہر فریق دوسرے کے بارے میں کتنا جانتا ہے۔
طرز عمل کے ڈیزائن کے 11 نمونے ہیں:
-
ذمہ داری کا سلسلہ: درخواست کو ہینڈلر چین کے ساتھ پاس کرتا ہے، ہر ہینڈلر کو یہ فیصلہ کرنے دیتا ہے کہ اس پر کارروائی کی جائے یا اسے آگے بڑھایا جائے۔
-
حکم: درخواستوں کو اشیاء میں سمیٹ کر، آپ کلائنٹ کو پیرامیٹرائز کر سکتے ہیں، قطار میں کاموں کو شامل کر سکتے ہیں، اور کالعدم کرنے کی حمایت کر سکتے ہیں۔
-
مترجم: ایک زبان کو دیکھتے ہوئے، جملے کا اندازہ کرنے والے ترجمان کے ساتھ گرائمیکل تاثرات کی وضاحت کریں۔
-
تکرار کرنے والا: مجموعہ کو اندرونی طور پر کیسے منظم کیا جاتا ہے اس کو ظاہر کیے بغیر مجموعہ کے عناصر تک ترتیب وار رسائی کا طریقہ فراہم کرتا ہے۔
-
ثالث: ایسی اشیاء کی وضاحت کرتا ہے جو براہ راست ایک دوسرے کا حوالہ نہیں دیتے ہیں، اس بات کا احاطہ کرتے ہوئے کہ اشیاء کا ایک سیٹ کیسے تعامل کرتا ہے۔
-
یادگار: کسی چیز کی اندرونی حالت کو کیپچر کرتا ہے تاکہ اسے بعد میں انکیپسولیشن کو توڑے بغیر بحال کیا جا سکے۔
-
مبصر: ایک سے کئی انحصار کی وضاحت کرتا ہے تاکہ جب کوئی چیز حالت بدلتی ہے تو اس کے تمام انحصار خود بخود مطلع ہوجاتے ہیں۔
-
صورت حال: آپ کسی چیز کے رویے کو تبدیل کر سکتے ہیں جب اس کی اندرونی حالت بدل جاتی ہے، بالکل اسی طرح جیسے آپ نے اس کی کلاس تبدیل کی ہو۔
-
حکمت عملی: قابل تبادلہ الگورتھم کے خاندان کی وضاحت کرتا ہے اور کلائنٹ کو یہ منتخب کرنے کی اجازت دیتا ہے کہ رن ٹائم کے وقت کون سا الگورتھم استعمال کرنا ہے۔
-
ٹیمپلیٹ کا طریقہ: ایک طریقہ میں الگورتھم کے ڈھانچے کی وضاحت کرتا ہے، ذیلی طبقات کو بھرنے کے لیے چند مراحل چھوڑتا ہے۔
-
وزیٹر: آپ عنصر کی کلاس کو تبدیل کیے بغیر ایک نئے آپریشن کی وضاحت کر سکتے ہیں جس پر آپریشن کیا جاتا ہے۔
1. ذمہ داری ڈیزائن پیٹرن کا سلسلہ
حقیقی کیس
اپنی کمپنی کے اخراجات کی منظوری کے عمل کے بارے میں سوچیں۔ ملازم نے درخواست ڈائریکٹر کو جمع کرائی۔ اگر رقم کم ہے، تو ڈائریکٹر اسے منظور کرتا ہے اور بس۔ اگر کسی ڈائریکٹر کی منظوری کے لیے یہ بہت بڑا ہے تو اسے نائب صدر کو بھیج دیا جاتا ہے۔ اگر یہ بہت بڑا ہو جاتا ہے، تو یہ سی ای او تک جاتا ہے۔
سلسلہ میں شامل ہر فرد کو صرف دو چیزیں جاننے کی ضرورت ہے: آپ کیا منظور کر سکتے ہیں اور اگر آپ نہیں کر سکتے تو آپ کو کس کو دینا چاہیے۔ ملازمین کو یہ جاننے کی ضرورت نہیں ہے کہ کون منظوری دیتا ہے۔
یہ ذمہ داری ڈیزائن پیٹرن کا سلسلہ ہے۔ ایک درخواست ہینڈلرز کی ایک زنجیر کو نیچے لے جاتی ہے جب تک کہ ہینڈلرز میں سے کسی ایک کے ذریعہ اس پر کارروائی نہ کی جائے، ہر ہینڈلر کے ساتھ صرف اس سلسلہ میں اس کے اپنے لنک میں دلچسپی ہوتی ہے۔
مسائل حل ہوئے:
-
کیا ہوگا اگر بھیجنے والے کو یہ جاننے کی ضرورت ہو کہ درخواست کس کو سنبھالنی چاہئے؟ یہ کال کرنے والے کو ایک مخصوص ہینڈلر سے جوڑ دے گا اور اعتراف کے ڈھانچے میں تبدیلی کے لمحے کو توڑ دے گا۔ سلسلہ بندی بھیجنے والوں کو یہ جانے بغیر درخواستیں جمع کرنے کی اجازت دیتی ہے کہ کون ان پر کارروائی کرے گا۔
-
کیا ہوگا اگر ایک ہینڈلر بغیر کسی متبادل کے صرف منظور یا مسترد کر سکتا ہے؟ ان اجازتوں سے باہر کی درخواستیں صرف ناکام ہوجاتی ہیں۔ زنجیریں ہینڈلرز کو ایسے مواد کو منتقل کرنے کی اجازت دیتی ہیں جسے وہ مزید سنبھال نہیں سکتے۔
-
اگر مجھے اپنی منظوری کا ڈھانچہ تبدیل کرنے کی ضرورت ہو تو کیا ہوگا؟ اس کے بعد کون سا ہینڈلر آتا ہے اسے دوبارہ وائر کرنا کافی ہے۔ نہ بھیجنے والے اور نہ ہی کسی دوسرے ہینڈلرز کو تبدیل کرنے کی ضرورت ہے۔
سیدھے الفاظ میں، یہ ہینڈلر چین کے ساتھ درخواست کو پاس کرتا ہے۔ ہر ہینڈلر فیصلہ کرتا ہے کہ آیا اس پر کارروائی کرنی ہے یا اسے اگلے ہینڈلر کو منتقل کرنا ہے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"آبجیکٹ اورینٹڈ ڈیزائن میں، چین آف ریسپانسیبلٹی پیٹرن ایک طرز عمل ڈیزائن پیٹرن ہے جو کمانڈ آبجیکٹ کے ماخذ اور پروسیسنگ آبجیکٹ کے ایک سیٹ پر مشتمل ہوتا ہے۔ ہر پروسیسنگ آبجیکٹ میں منطق ہوتی ہے جو کہ کمانڈ آبجیکٹ کی اقسام کی وضاحت کرتی ہے جس پر یہ عمل کر سکتا ہے، اور باقی کو سلسلہ میں اگلی پروسیسنگ آبجیکٹ پر منتقل کیا جاتا ہے۔”
(ماخذ)
پروگرامنگ کی مثال:
ہر ایک Approver یہ اپنی منظوری کی حدود کو جانتا ہے اور سلسلہ میں اگلے منظور کنندہ کا حوالہ رکھتا ہے۔ کہ ExpenseRequest یہ اس وقت تک منظور ہوتا ہے جب تک کہ کوئی اسے منظور نہ کر لے یا جب تک کوئی اسے منظور نہ کر سکے۔
// The request that travels along the chain
public class ExpenseRequest
{
public string Description { get; }
public decimal Amount { get; }
public ExpenseRequest(string description, decimal amount)
{
Description = description;
Amount = amount;
}
}
// The handler, every link in the chain implements this
public abstract class Approver
{
private Approver? _next;
public void SetNext(Approver next) => _next = next;
public void Approve(ExpenseRequest request)
{
if (CanApprove(request))
{
Console.WriteLine($"{GetType().Name}: Approved '{request.Description}' (${request.Amount}).");
}
else if (_next is not null)
{
Console.WriteLine($"{GetType().Name}: Can't approve '{request.Description}' (${request.Amount}). Passing it up.");
_next.Approve(request);
}
else
{
Console.WriteLine($"{GetType().Name}: No one left to approve '{request.Description}' (${request.Amount}). Request denied.");
}
}
protected abstract bool CanApprove(ExpenseRequest request);
}
// Concrete handlers, each with its own approval limit
public class Director : Approver
{
protected override bool CanApprove(ExpenseRequest request) => request.Amount <= 1000;
}
public class VicePresident : Approver
{
protected override bool CanApprove(ExpenseRequest request) => request.Amount <= 20000;
}
public class Chief : Approver
{
protected override bool CanApprove(ExpenseRequest request) => request.Amount <= 50000;
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
Approver directorApprover = new Director();
Approver vpApprover = new VicePresident();
Approver ceoApprover = new Chief();
directorApprover.SetNext(vpApprover);
vpApprover.SetNext(ceoApprover);
directorApprover.Approve(new ExpenseRequest("Laptop", 800));
Console.WriteLine();
directorApprover.Approve(new ExpenseRequest("Team offsite", 12000));
Console.WriteLine();
directorApprover.Approve(new ExpenseRequest("New office lease", 90000));
حساب کتاب:
Director: Approved 'Laptop' ($800).
Director: Can't approve 'Team offsite' ($12000). Passing it up.
VicePresident: Approved 'Team offsite' ($12000).
Director: Can't approve 'New office lease' ($90000). Passing it up.
VicePresident: Can't approve 'New office lease' ($90000). Passing it up.
Chief: No one left to approve 'New office lease' ($90000). Request denied.
ملازم نے صرف ڈائریکٹر سے بات کی۔ چاہے ایک ڈائریکٹر، نائب صدر یا جنرل مینیجر بالآخر منظوری دیتا ہے یہ سلسلہ خود پر منحصر ہے، ملازمین پر نہیں۔
کب استعمال کرنا ہے۔
ذمہ داری کا سلسلہ اس وقت استعمال کریں جب ایک سے زیادہ ادارے کسی درخواست کو ہینڈل کر سکیں اور ہینڈلر کو پہلے سے معلوم نہ ہو۔
اگر آپ واضح طور پر کسی وصول کنندہ کی وضاحت کیے بغیر درخواست جاری کرنا چاہتے ہیں تو یہ بھی ایک اچھا انتخاب ہے۔
اور یہ اس وقت مفید ہے جب آپ کو ہینڈلرز کے سیٹ اور ان کے آرڈر کو سخت کوڈنگ کرنے کے بجائے ترتیب دینے کے قابل ہونے کی ضرورت ہو۔
2. کمانڈ ڈیزائن پیٹرن
حقیقی کیس
اسے یونیورسل ریموٹ کنٹرول سمجھیں۔ ہر بٹن کو ایک مخصوص کام انجام دینے کے لیے پروگرام کیا جاتا ہے، جیسے لائٹ کو آن یا آف کرنا۔ جب آپ کوئی بٹن دباتے ہیں، تو ریموٹ کو معلوم نہیں ہوتا کہ لائٹس دراصل اندرونی طور پر کیسے کام کرتی ہیں۔ یہ صرف اس عمل کو متحرک کرتا ہے جس کو انجام دینے کے لیے بٹن سیٹ کیا گیا ہے۔ اور چونکہ ہر بٹن کا عمل خود مختار ہے، اس لیے ریموٹ کنٹرول اس کارروائی کو بھی کالعدم کر سکتا ہے جسے اس نے بٹن کو ریورس میں دبانے سے انجام دیا ہے۔
یہ کمانڈ پیٹرن ہے. "لائٹس آن کریں" کی درخواست اس کے اپنے شے میں لپٹی ہوئی ہے۔ اسے متحرک کرنے کے لیے آپ کو اس کے بارے میں کچھ جاننے کی ضرورت نہیں ہے کہ یہ کیسے ہوا ہے۔
مسائل حل ہوئے:
-
کیا ہوگا اگر بٹن کو یہ جاننے کی ضرورت ہو کہ روشنی کیسے کام کرتی ہے؟ اگر روشنی کا اندرونی حصہ بدل جاتا ہے، تو تمام بٹنوں کو دوبارہ لکھنا پڑے گا۔ کسی ایکشن کو کمانڈ میں لپیٹنے کا مطلب ہے کہ ریموٹ ان تفصیلات کو نہیں چھوتا۔
-
اگر میں آخری کارروائی کو منسوخ کرنا چاہتا ہوں تو مجھے کیا کرنا چاہیے؟ کمانڈ آبجیکٹ کے بغیر، کچھ بھی کالعدم نہیں ہوسکتا، صرف ضمنی اثرات مکمل ہوتے ہیں۔ اپنے کام کو سمیٹنے سے آپ کو کچھ آرام ملتا ہے۔
-
میں کسی کام کو قطار میں کیسے شامل کروں، اسے لاگ ان کروں، یا اسے بعد میں کیسے ٹرگر کروں؟ نارمل میتھڈ کالز فوراً ہوتی ہیں اور پیچھے کچھ نہیں چھوڑتی ہیں۔ کمانڈز آبجیکٹ ہیں، لہذا انہیں اسٹور، قطار میں، اور دوبارہ چلایا جا سکتا ہے۔
سیدھے الفاظ میں، یہ درخواست کو ایک آبجیکٹ میں لپیٹ دیتا ہے، اس لیے اسے متحرک کرنے کے لیے یہ جاننے کی ضرورت نہیں ہوتی کہ درخواست کیسے کی جاتی ہے، اور یہ صرف قطار میں لگا سکتا ہے، لاگ ان کر سکتا ہے یا آپریشن کو کالعدم کر سکتا ہے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"کمانڈ پیٹرن ایک طرز عمل ڈیزائن پیٹرن ہے جو کسی کام کو انجام دینے یا بعد میں کسی ایونٹ کو متحرک کرنے کے لیے درکار تمام معلومات کو سمیٹنے کے لیے اشیاء کا استعمال کرتا ہے۔" (ماخذ)
پروگرامنگ کی مثال:
کہ RemoteControl پکارنے والا ایک پکارنے والا ہے جسے صرف وہی جانتا ہے۔ ICommand. LightOnCommand اور LightOffCommand مخصوص احکامات یہ ہیں: Light وصول کنندہ اور اس کے بارے میں کیا کرنا ہے۔
// The command interface, every action implements this
public interface ICommand
{
void Execute();
void Undo();
}
// The receiver, the object that actually does the work
public class Light
{
private readonly string _room;
public Light(string room) => _room = room;
public void On() => Console.WriteLine($"{_room} light: turned on.");
public void Off() => Console.WriteLine($"{_room} light: turned off.");
}
// Concrete commands, each wraps a receiver and an action
public class LightOnCommand : ICommand
{
private readonly Light _light;
public LightOnCommand(Light light) => _light = light;
public void Execute() => _light.On();
public void Undo() => _light.Off();
}
public class LightOffCommand : ICommand
{
private readonly Light _light;
public LightOffCommand(Light light) => _light = light;
public void Execute() => _light.Off();
public void Undo() => _light.On();
}
// The invoker, it holds a command and triggers it without knowing what it does
public class RemoteControl
{
private ICommand? _command;
public void SetCommand(ICommand command) => _command = command;
public void PressButton() => _command?.Execute();
public void PressUndo() => _command?.Undo();
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
var livingRoomLight = new Light("Living Room");
var remote = new RemoteControl();
remote.SetCommand(new LightOnCommand(livingRoomLight));
remote.PressButton();
remote.SetCommand(new LightOffCommand(livingRoomLight));
remote.PressButton();
Console.WriteLine();
Console.WriteLine("Undoing last action...");
remote.PressUndo();
حساب کتاب:
Living Room light: turned on.
Living Room light: turned off.
Undoing last action...
Living Room light: turned on.
ریموٹ کنٹرول نے کال نہیں کی۔ _light.On() یا _light.Off() براہ راست اسے بلایا گیا تھا Execute() اور Undo() آپ جو بھی حکم دیں ۔ یہ کمانڈ پیٹرن ہے. درخواست خود ایک اعتراض بن گئی ہے۔
کب استعمال کرنا ہے۔
کمانڈ پیٹرن کا استعمال کریں جب آپ کسی چیز کو سخت کوڈ کرنے کے بجائے انجام دینے کے لیے کسی شے کو پیرامیٹرائز کرنا چاہتے ہیں۔
اس خصوصیت کا استعمال اس وقت کریں جب آپ کو قطار، لاگ، یا کالعدم درخواستوں کی حمایت کرنے کی ضرورت ہو۔
اور اس پر غور کریں اگر آپ اس آبجیکٹ کو الگ کرنا چاہتے ہیں جو آپریشن کو کال کرتی ہے اس چیز سے جو جانتی ہے کہ آپریشن کیسے کرنا ہے۔
3. ترجمان ڈیزائن پیٹرن
حقیقی کیس
ایک بنیادی کیلکولیٹر پر غور کریں جو درج ذیل اظہار کو پڑھتا ہے: (5 + 3) - 2. کوئی بھی ایک واحد طریقہ کو ہارڈ کوڈ نہیں کرنا چاہتا ہے جو تمام ممکنہ تاثرات کو سنبھالتا ہے۔ اس کے بجائے، تاثرات کو چھوٹے چھوٹے ٹکڑوں میں تقسیم کیا جاتا ہے جسے نمبرز اور ایکشن بٹن کہتے ہیں، جن میں سے ہر ایک خود کو جانچنا اور دوسرے ٹکڑوں کے ساتھ جوڑنا جانتا ہے۔ (5 + 3) - 2 دو چیزوں کو منہا کرنے سے یعنی نمبر 2، 5 اور 3 کو شامل کرنے سے نتیجہ نکلتا ہے۔ ہر ٹکڑے کو صرف یہ جاننے کی ضرورت ہے کہ اپنی تشریح کیسے کی جائے۔
یہ ترجمان کا نمونہ ہے۔ گرامر کو چھوٹی اشیاء کے درخت کے طور پر دکھایا جاتا ہے، اور ہر شے جانتی ہے کہ اپنی چھوٹی اشیاء کا اندازہ کیسے لگانا ہے۔
مسائل حل ہوئے:
-
اگر آپ ایک بڑے طریقے سے پورے اظہار کو جانچنے کی کوشش کریں تو کیا ہوگا؟ جیسے ہی آپ کی گرامر پیچیدہ ہو جاتی ہے، اس کا انتظام کرنا مشکل ہو جاتا ہے۔ گرامر کو چھوٹی کلاسوں میں تقسیم کرنا، ایک اصول کے مطابق، ہر حصے کو آسان بناتا ہے۔
-
اگر گرامر کو بڑھنے کی ضرورت ہو تو کیا ہوگا؟ ضرب جیسے نئے آپریشنز کو شامل کرنا صرف ایک نئی کلاس ہے۔ کسی بھی موجودہ حصوں کو تبدیل کرنے کی ضرورت نہیں ہے۔
-
کیا ہوگا اگر ایک ہی اظہار کو ایک سے زیادہ بار، یا مختلف سیاق و سباق میں جانچنے کی ضرورت ہو؟ چونکہ ہر ایک ٹکڑا صرف ایک شے ہے، اس لیے ایک ہی درخت کو دوبارہ تعمیر کیے بغیر اس کی دوبارہ تشریح کی جا سکتی ہے۔
سیدھے الفاظ میں، ہم گرامر کو چھوٹی اشیاء کے درخت کے طور پر پیش کرتے ہیں۔ یہاں، ہر ایک ہستی اپنی نمائندگی کے اپنے حصے کی تشریح کرنا جانتی ہے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"کمپیوٹر پروگرامنگ میں، مترجم کا پیٹرن ایک ڈیزائن پیٹرن ہے جو یہ بتاتا ہے کہ کسی زبان میں جملے کی جانچ کیسے کی جائے۔ بنیادی خیال یہ ہے کہ کمپیوٹر کی مخصوص زبان کی ہر علامت (ٹرمینل یا غیر ٹرمینل) کے لیے ایک کلاس ہو۔" (ماخذ)
پروگرامنگ کی مثال:
Number ایک ٹرمینل نمائندگی جو ایک سادہ قدر ہے۔ Add اور Subtract ایک غیر ٹرمینل اظہار دو دیگر اظہارات کو جوڑتا ہے۔ ہر نوڈ، ٹرمینل یا نہیں، جانتا ہے کہ کیسے: Interpret() خود
// The abstract expression, every node in the grammar implements this
public abstract class Expression
{
public abstract int Interpret();
}
// A terminal expression, a plain number that needs no further interpretation
public class Number : Expression
{
private readonly int _value;
public Number(int value) => _value = value;
public override int Interpret() => _value;
}
// Non-terminal expressions, each combines other expressions
public class Add : Expression
{
private readonly Expression _left;
private readonly Expression _right;
public Add(Expression left, Expression right)
{
_left = left;
_right = right;
}
public override int Interpret() => _left.Interpret() + _right.Interpret();
}
public class Subtract : Expression
{
private readonly Expression _left;
private readonly Expression _right;
public Subtract(Expression left, Expression right)
{
_left = left;
_right = right;
}
public override int Interpret() => _left.Interpret() - _right.Interpret();
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
// (5 plus 3) minus 2
Expression expression = new Subtract(
new Add(new Number(5), new Number(3)),
new Number(2)
);
Console.WriteLine($"Result: {expression.Interpret()}");
// (10 minus 4) plus (2 plus 2)
Expression another = new Add(
new Subtract(new Number(10), new Number(4)),
new Add(new Number(2), new Number(2))
);
Console.WriteLine($"Result: {another.Interpret()}");
حساب کتاب:
Result: 6
Result: 10
کسی بھی چیز نے کبھی بھی پورے اظہار کا ایک ساتھ جائزہ نہیں لیا ہے۔ Subtract خود سے پوچھا _left اور _right اپنے لیے تشریح کرنے کے لیے، لوگوں نے اپنے بچوں سے عام نمبر بھی مانگے۔ یہ ترجمان کا نمونہ ہے۔ گرامر ایک وقت میں ایک چھوٹے سے ٹکڑے کی ترجمانی کرتا ہے۔
کب استعمال کرنا ہے۔
اگر آپ کے پاس تشخیص کرنے کے لیے ایک سادہ زبان یا گرامر ہے، تو ایک ترجمان کا استعمال کرتے ہوئے اور اسے اظہار کے درخت کے طور پر بیان کرنا اسے مزید قابل انتظام بناتا ہے۔
جب نحو نسبتاً مستحکم ہو تو یہ بھی اچھا کام کرتا ہے۔ مثال کے طور پر، ایک نیا اصول شامل کرنے کا مطلب ہے کہ کسی موجودہ کلاس کو دوبارہ لکھنے کے بجائے ایک نئی کلاس شامل کرنا۔
اور یہ اس وقت مفید ہے جب آپ ایک بڑے طریقہ کے بجائے متعدد چھوٹی، توجہ مرکوز کلاسز رکھنا چاہتے ہیں جہاں آپ ایک ساتھ ہر چیز کا تجزیہ اور جائزہ لینا چاہتے ہیں۔
4. Iterator ڈیزائن پیٹرن
حقیقی کیس
کتابوں کی الماری کے بارے میں سوچئے۔ میں ایک وقت میں ایک کتاب کو بائیں سے دائیں دیکھنا چاہتا ہوں، یہ جانے بغیر کہ کتابیں ترتیب دی گئی ہیں، ڈھیروں اور ڈھیروں میں محفوظ ہیں، یا مکمل طور پر کوئی اور چیز۔ آپ کو بس یہ پوچھنے کا طریقہ درکار ہے، "آگے کیا ہے؟" اور آپ کو پتہ چل جائے گا کہ آپ آخر تک پہنچ گئے ہیں۔ شیلف دراصل کتابوں کو اندرونی طور پر کیسے ذخیرہ کرتی ہیں یہ آپ کی فکر نہیں ہے۔
یہ Iterator پیٹرن ہے۔ یہ ایک وقت میں ایک عنصر کے ذریعے ایک مجموعہ کے ذریعے قدم رکھنے کا ایک مستقل طریقہ فراہم کرتا ہے اس کو ظاہر کیے بغیر کہ اس مجموعہ کو کس طرح منظم کیا گیا ہے۔
مسائل حل ہوئے:
-
کیا ہوگا اگر کسی کلائنٹ کو یہ جاننے کی ضرورت ہو کہ ایک مجموعہ اس پر تکرار کرنے کے لیے اندرونی طور پر کیسے ذخیرہ کیا جاتا ہے؟ اس اندرونی ڈھانچے کو تبدیل کرنے سے کوڈ کا کوئی بھی ٹکڑا ٹوٹ جائے گا جو اس ڈھانچے کو دہراتا ہے۔ تکرار کرنے والے اپنی ساخت کو ایک سادہ "گیٹ اگلا" انٹرفیس کے پیچھے چھپاتے ہیں۔
-
اگر مجھے ایک ہی وقت میں ایک سے زیادہ دورے کی ضرورت ہو تو کیا ہوگا؟ ایک مشترکہ مقام کام نہیں کرے گا۔ ہر تکرار کرنے والا اپنی پوزیشن کو برقرار رکھتا ہے، اس لیے متعدد ٹراورسلز آزادانہ طور پر ہو سکتے ہیں۔
-
کیا ہوگا اگر آپ خود زبان کا استعمال کرتے ہوئے کسی مجموعہ پر اعادہ کرنا چاہتے ہیں؟
foreach? تکرار کرنے والے انٹرفیس کو لاگو کرنے کا مطلب ہے کہ زبان کو توقع ہے کہ اپنی مرضی کے مجموعوں کو مفت میں مدد ملتی ہے۔
سیدھے الفاظ میں، یہ ایک وقت میں ایک عنصر کو دیکھنے کا ایک طریقہ فراہم کرتا ہے اس بات کو ظاہر کیے بغیر کہ مجموعہ اصل میں کس طرح منظم ہے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"آبجیکٹ پر مبنی پروگرامنگ میں، ایٹریٹر پیٹرن ایک ڈیزائن پیٹرن ہے جو کنٹینر کو عبور کرنے اور کنٹینر میں عناصر تک رسائی کے لیے تکرار کرنے والوں کا استعمال کرتا ہے۔" (ذریعہ)
پروگرامنگ کی مثال:
Bookshelf یہ ایک بے نقاب مجموعی ہے۔ IEnumerator یہ ظاہر کیے بغیر کہ کتاب رکھی جارہی ہے۔ List اندرونی طور پر۔ BookshelfIterator یہ ایک تکرار کرنے والا ہے جو ایک وقت میں ایک چیز سے گزرتا ہے۔
// The aggregate, exposes an iterator without revealing how books are stored
public class Bookshelf : IEnumerable
{
private readonly List _books = new();
public void Add(string title) => _books.Add(title);
public IEnumerator GetEnumerator() => new BookshelfIterator(_books);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
// The iterator, walks the collection one book at a time
public class BookshelfIterator : IEnumerator
{
private readonly List _books;
private int _position = -1;
public BookshelfIterator(List books) => _books = books;
public string Current => _books[_position];
object IEnumerator.Current => Current;
public bool MoveNext()
{
_position++;
return _position < _books.Count;
}
public void Reset() => _position = -1;
public void Dispose() { }
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
var bookshelf = new Bookshelf();
bookshelf.Add("Clean Code");
bookshelf.Add("The Pragmatic Programmer");
bookshelf.Add("Design Patterns");
foreach (var book in bookshelf)
{
Console.WriteLine($"On the shelf: {book}");
}
حساب کتاب:
On the shelf: Clean Code
On the shelf: The Pragmatic Programmer
On the shelf: Design Patterns
کہ foreach لوپ کو کبھی نہیں چھوا تھا۔ List اندر Bookshelf براہ راست اسے بلایا گیا تھا MoveNext() اور Current کو BookshelfIteratorایک وقت میں ایک قدم۔ یہ Iterator پیٹرن ہے۔ ٹراورسل منطق خود مجموعہ سے باہر ہے۔
کب استعمال کرنا ہے۔
کسی مجموعے کے اندرونی ڈھانچے کو ظاہر کیے بغیر اسے عبور کرنے کے لیے Iterator کا استعمال کریں۔
یہ بھی ایک اچھا انتخاب ہے اگر آپ کو ایک ہی مجموعے پر متعدد کنکرنٹ ٹراورسلز کو سپورٹ کرنے کی ضرورت ہے۔
اور اگر آپ چاہتے ہیں کہ آپ کا حسب ضرورت مجموعہ زبان کے بلٹ ان تکرار نحو کے ساتھ کام کرے تو اسے آزمائیں: foreach.
5. ثالث ڈیزائن پیٹرن
حقیقی کیس
ہوائی ٹریفک کنٹرول ٹاور کے بارے میں سوچئے۔ ہوائی جہاز براہ راست ایک دوسرے سے بات چیت کرنے کے لیے ریڈیو نہیں کرتے ہیں کہ کون پہلے لینڈ کرے گا۔ یہ الجھا ہوا ہو گا۔ ایک ہی وقت میں درجنوں پائلٹ ایک دوسرے کے ساتھ تعاون کرنے کی کوشش کر رہے ہیں۔
اس کے بجائے، تمام طیارے صرف ٹاور کے ساتھ بات چیت کرتے ہیں۔ ٹاور رن وے کی حالت جانتا ہے اور ہر جہاز کو بتاتا ہے کہ کیا کرنا ہے۔ ہوائی جہاز کو یہ جاننے کی ضرورت نہیں ہے کہ اس کے ارد گرد کتنے دوسرے طیارے ہیں یا وہ کیا کر رہے ہیں۔
یہ ثالثی کا نمونہ ہے۔ اشیاء ایک دوسرے سے براہ راست بات کرنے کے بجائے، وہ ایک مرکزی ہستی سے بات کرتے ہیں جو ان سب کو مربوط کرتی ہے۔
مسائل حل ہوئے:
-
کیا ہوگا اگر ہر ہوائی جہاز کو ہر دوسرے طیارے سے براہ راست بات چیت کرنی پڑے؟ جیسے جیسے مزید طیارے شامل ہوں گے، کنکشنز کی تعداد پھٹ جائے گی، اور ہر طیارے کو ہر دوسرے طیارے کے بارے میں جاننے کی ضرورت ہوگی۔ ثالث کا مطلب ہے کہ ہر طیارے کو صرف ٹاور کے بارے میں جاننے کی ضرورت ہوتی ہے۔
-
کیا ہوگا اگر ہم آہنگی کی منطق کو تمام متعلقہ اشیاء میں تقسیم کیا گیا ہو؟ لینڈنگ کی ترجیح کو تبدیل کرنے کا مطلب ہے تمام طیاروں کو چھونے سے۔ ایک ثالث کے ساتھ، وہ منطق ایک جگہ رہتی ہے۔
-
اگر میں سسٹم میں نیا ہوائی جہاز شامل کرنا چاہتا ہوں تو کیا ہوگا؟ آپ کو صرف ٹاور سے بات کرنے کا طریقہ جاننے کی ضرورت ہے۔ آسمان میں پہلے سے موجود ہر دوسرے طیارے کو اس کو متعارف کرانے کی ضرورت نہیں ہے۔
سیدھے الفاظ میں، اشیاء کو ایک دوسرے کے ساتھ براہ راست بات چیت کرنے کے بجائے، یہ تمام مواصلات کو ایک مرکزی ہستی کے ذریعے روٹ کرتا ہے جو اشیاء کو مربوط کرنا جانتا ہے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"سافٹ ویئر انجینئرنگ میں، ثالثی پیٹرن ایک آبجیکٹ کی وضاحت کرتا ہے جو اس بات کو سمیٹتا ہے کہ اشیاء کا ایک سیٹ کس طرح آپس میں تعامل کرتا ہے۔ اس پیٹرن کو رویے کا نمونہ سمجھا جاتا ہے کیونکہ یہ ان طریقوں کی وجہ سے جن میں یہ کسی پروگرام کے عمل کے رویے کو تبدیل کرسکتا ہے۔" (ماخذ)
پروگرامنگ کی مثال:
ControlTower یہ ثالث ہے۔ یہ صرف ایک چیز ہے Aircraft میں نے پہلے بھی اس کے بارے میں بات کی ہے۔ یہ اس بات کا تعین کرتا ہے کہ آیا کوئی طیارہ اپنے حالات کی بنیاد پر لینڈ کر سکتا ہے، اور کوئی بھی ہوائی جہاز دوسرے طیارے سے براہ راست رابطہ میں نہیں ہے۔
// The mediator interface
public interface IControlTower
{
void RequestLanding(Aircraft requester);
}
// The concrete mediator, coordinates all the aircraft instead of letting them talk to each other
public class ControlTower : IControlTower
{
private readonly List _aircraft = new();
private bool _runwayFree = true;
public void Register(Aircraft aircraft) => _aircraft.Add(aircraft);
public void RequestLanding(Aircraft requester)
{
if (_runwayFree)
{
_runwayFree = false;
Console.WriteLine($"Tower: Runway clear. {requester.Name}, you are cleared to land.");
}
else
{
Console.WriteLine($"Tower: Runway occupied. {requester.Name}, please hold your position.");
}
}
}
// The colleague, only ever talks to the mediator, never to other aircraft directly
public class Aircraft
{
public string Name { get; }
private readonly IControlTower _tower;
public Aircraft(string name, IControlTower tower)
{
Name = name;
_tower = tower;
}
public void RequestLanding()
{
Console.WriteLine($"{Name}: Requesting permission to land.");
_tower.RequestLanding(this);
}
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
var tower = new ControlTower();
var flight101 = new Aircraft("Flight 101", tower);
var flight202 = new Aircraft("Flight 202", tower);
tower.Register(flight101);
tower.Register(flight202);
flight101.RequestLanding();
flight202.RequestLanding();
حساب کتاب:
Flight 101: Requesting permission to land.
Tower: Runway clear. Flight 101, you are cleared to land.
Flight 202: Requesting permission to land.
Tower: Runway occupied. Flight 202, please hold your position.
پروازیں 101 اور 202 نے کبھی ایک دوسرے سے بات نہیں کی۔ کوئی بھی فریق نہیں جانتا کہ دوسرا موجود ہے۔ دونوں نے صرف ٹاپ سے بات کی، جس نے پھر فیصلہ کیا کہ آگے کیا ہوا۔ یہ ثالثی کا نمونہ ہے۔
کب استعمال کرنا ہے۔
ثالث کا استعمال کریں جب اشیاء کا ایک گروپ پیچیدہ، آپس میں جڑے ہوئے طریقے سے بات چیت کرتا ہے اور آپ اس مواصلات کو مرکزی بنانا چاہتے ہیں۔
یہ اس وقت بھی مفید ہے جب آپ اشیاء کو ایک دوسرے کا براہ راست حوالہ دے کر ان کو ایک ساتھ بند کرنے کے بجائے آزادانہ طور پر دوبارہ استعمال کرنا چاہتے ہیں۔
اور اگر آپ کی اشیاء کے تعامل کا طریقہ اکثر بدلتا ہے، تو بہتر ہے کہ تمام متعلقہ اشیاء کو تبدیل کرنے کے بجائے ایک جگہ پر تبدیلیاں کریں۔
6. میمنٹو ڈیزائن پیٹرن
حقیقی کیس
اپنے ٹیکسٹ ایڈیٹر میں انڈو ہسٹری کے بارے میں سوچیں۔ اکثر، ایڈیٹرز خاموشی سے اسنیپ شاٹس لیتے ہیں کہ دستاویز کیسی دکھتی ہے۔ اس کو حاصل کرنے کے لیے، ہم آپ سے اپنی دستاویز میں کسی بھی اندرونی کو ظاہر کرنے کے لیے نہیں کہتے ہیں۔ یہ صرف اس وقت مواد کی ایک کاپی حاصل کرتا ہے۔
جب آپ انڈو کو دباتے ہیں، تو آپ کا ایڈیٹر وہ سنیپ شاٹ واپس کر دیتا ہے اور آپ کا دستاویز اس کی اصل حالت میں بحال ہو جاتا ہے۔ تاریخ ان سنیپ شاٹس کا ذخیرہ رکھتی ہے، لیکن آپ ان کے اندر کچھ بھی نہیں دیکھتے یا تبدیل نہیں کرتے۔ بس بچائیں اور واپس جائیں۔
یہ میمنٹو پیٹرن ہے۔ یہ آپ کو کسی چیز کی حالت کو پکڑنے اور بحال کرنے کی اجازت دیتا ہے اس کو ظاہر کیے بغیر کہ وہ ریاست اندرونی طور پر کیسے تشکیل دی گئی ہے۔
مسائل حل ہوئے:
-
کیا ہوگا اگر آپ کو اپنی دستاویز میں تمام نجی فیلڈز کو Undo کے ذریعے بے نقاب کرنے کی ضرورت ہے؟ اس سے انکیپسولیشن ٹوٹ جاتی ہے اور دستاویز کے اندر کی جانے والی کوئی بھی تبدیلی اس پر پھیل جائے گی جو بھی انڈو کو ہینڈل کرتا ہے۔ ایک یادگار چیز کے اندر ایک ڈھانچہ چھپاتا ہے جسے صرف دستاویز ہی پڑھ سکتا ہے۔
-
کیا ہوگا اگر مجھے پرانے سنیپ شاٹس کا معائنہ کرنے یا درست کرنے کے لیے ریکارڈز کی ضرورت ہو؟ یہ ممکن نہیں ہونا چاہیے۔ محافظ صرف یادداشتوں کو ذخیرہ اور واپس کریں گے اور اس کے کسی بھی مواد کو پڑھ یا تبدیل نہیں کریں گے۔
-
اگر مجھے صرف ایک کے بجائے ایک سے زیادہ بحالی پوائنٹس کی ضرورت ہو تو کیا ہوگا؟ کیونکہ ہر ایک کیپ سیک ایک سادہ چیز ہے، اس لیے آپ اسے اسٹیک کر سکتے ہیں، اس کی فہرست بنا سکتے ہیں یا اسے حذف کر سکتے ہیں، آپ کو جتنے ریسٹور پوائنٹس آپ رکھنا چاہتے ہیں دے سکتے ہیں۔
سیدھے الفاظ میں، یہ ایک اسنیپ شاٹ میں کسی چیز کی حالت کو پکڑتا ہے جسے بعد میں یہ ظاہر کیے بغیر بحال کیا جا سکتا ہے کہ اس حالت کو اندرونی طور پر کیسے ملایا جاتا ہے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"میمنٹو پیٹرن ایک سافٹ ویئر ڈیزائن پیٹرن ہے جو کسی چیز کو اس کی پچھلی حالت میں بحال کرنے کی صلاحیت فراہم کرتا ہے (رول بیک کے ذریعے اسے کالعدم کریں)۔"
(ماخذ)
پروگرامنگ کی مثال:
TextEditor بانی: تخلیق کرتا ہے۔ EditorMemento آپ سنیپ شاٹس بنا سکتے ہیں اور ان میں سے کسی ایک سے بحال کر سکتے ہیں۔ History یہ مینیجر ہے۔ اندر دیکھے بغیر تحائف کو ڈھیروں میں اسٹور کریں۔
// The memento, an immutable snapshot of the editor's state
public class EditorMemento
{
public string Content { get; }
public EditorMemento(string content) => Content = content;
}
// The originator, creates and restores from mementos of its own state
public class TextEditor
{
public string Content { get; private set; } = string.Empty;
public void Write(string text) => Content += text;
public EditorMemento Save() => new(Content);
public void Restore(EditorMemento memento) => Content = memento.Content;
}
// The caretaker, stores mementos without ever looking inside them
public class History
{
private readonly Stack _snapshots = new();
public void Save(EditorMemento memento) => _snapshots.Push(memento);
public EditorMemento Undo() => _snapshots.Pop();
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
var editor = new TextEditor();
var history = new History();
editor.Write("Hello");
history.Save(editor.Save());
editor.Write(", world");
history.Save(editor.Save());
editor.Write("!!!");
Console.WriteLine($"Current: {editor.Content}");
editor.Restore(history.Undo());
Console.WriteLine($"After undo: {editor.Content}");
editor.Restore(history.Undo());
Console.WriteLine($"After undo: {editor.Content}");
حساب کتاب:
Current: Hello, world!!!
After undo: Hello, world
After undo: Hello
History اسنیپ شاٹ کے اندر موجود متن کو نہ پڑھیں اور نہ ہی تبدیل کریں۔ میں نے ابھی یادگار کو آگے بڑھایا اور یہ پھٹ گیا۔ صرف TextEditor میں جانتا تھا کہ اس میں جو کچھ تھا اس کا کیا کرنا ہے۔ یہ میمنٹو پیٹرن ہے۔
کب استعمال کرنا ہے۔
اگر آپ کو فنکشنلٹی کو کالعدم/دوبارہ کرنے کی ضرورت ہے اور کسی شے کے اندرونی حصے کو بے نقاب کیے بغیر ریاست کو حاصل کرنا چاہتے ہیں تو میمنٹو استعمال کریں۔
یہ بھی ایک اچھا انتخاب ہے اگر آپ خود سنیپ شاٹس لے کر نجی فیلڈز کو بے نقاب کرکے انکیپسولیشن کو توڑتے ہیں۔
اور اس سے مدد ملتی ہے جب آپ چاہتے ہیں کہ تاریخ کو ذخیرہ کرنے والی چیز گونگی رہے۔ دوسرے لفظوں میں، یہ جانے یا اس کی پرواہ کیے بغیر اسنیپ شاٹ رکھنے جیسا ہے۔
7. آبزرور ڈیزائن پیٹرن
حقیقی کیس
ہمارے یوٹیوب چینل کو سبسکرائب کرنے پر غور کریں۔ یہ دیکھنے کے لیے صفحہ کو ریفریش کرنے کی ضرورت نہیں ہے کہ آیا کوئی نئی ویڈیو اپ لوڈ کی گئی ہے۔ آپ کے سبسکرائب کرنے کے بعد، آپ کو چینل پر مواد اپ لوڈ ہونے کے وقت خود بخود اطلاعات موصول ہوں گی۔
چینل نہیں جانتا یا اس کی پرواہ نہیں کرتا ہے کہ ہر سبسکرائبر ان اطلاعات کے ساتھ کیا کرتا ہے۔ آپ صرف اتنا جانتے ہیں کہ آپ کے پاس سبسکرائبرز کی فہرست ہے، اور اگر کوئی تبدیلی ہوتی ہے تو آپ کے سبھی سبسکرائبرز کو مطلع کر دیا جائے گا۔
یہ مبصر پیٹرن ہے. ایک آبجیکٹ انحصار کی فہرست رکھتا ہے اور جب بھی اس کی حالت بدلتی ہے تو خود بخود ان سب کو مطلع کر دیتی ہے۔
مسائل حل ہوئے:
-
کیا ہوگا اگر ہر سبسکرائبر کو اپ ڈیٹس کے لیے آپ کا چینل چیک کرتے رہنا پڑے؟ اس سے کوشش ضائع ہوتی ہے اور تاخیر ہوتی ہے۔ آپ کے چینل کو اپنے سبسکرائبرز کو براہ راست مطلع کرنے کا مطلب ہے کہ آپ کو اس وقت پتہ چل جائے گا جب ایسا ہوتا ہے۔
-
کیا ہوگا اگر آپ کے چینل کو یہ جاننے کی ضرورت ہو کہ ہر سبسکرائبر نئی ویڈیو میں کیا چاہتا ہے؟ ایسا نہیں ہونا چاہیے۔ چینلز صرف کال کرتے ہیں۔
Notify()ہر سبسکرائبر خود فیصلہ کرتا ہے کہ اس کا کیا مطلب ہے۔ -
میں رن ٹائم پر سبسکرائبرز کو کیسے شامل یا ہٹاؤں؟ چینلز کو تبدیل کرنے کی ضرورت نہیں ہے۔ ہم صرف ایک فہرست کو برقرار رکھتے ہیں، اور سبسکرائب کرنا یا ان سبسکرائب کرنا صرف اس فہرست کو متاثر کرتا ہے۔
سیدھے الفاظ میں، ایک شے اپنے انحصار کی فہرست کو برقرار رکھتی ہے اور جب بھی اس کی حالت بدلتی ہے تو خود بخود ان سب کو مطلع کر دیتی ہے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"آبزرور پیٹرن ایک سافٹ ویئر ڈیزائن پیٹرن ہے جس میں ایک چیز، جسے سبجیکٹ کہا جاتا ہے، انحصار کی فہرست کو برقرار رکھتا ہے، جسے مبصر کہتے ہیں، اور خود بخود ریاستی تبدیلیوں کے بارے میں مطلع کیا جاتا ہے، عام طور پر اس کے طریقوں میں سے ایک کو کال کرکے۔" (ذریعہ)
پروگرامنگ کی مثال:
YouTubeChannel عنوانات میں شامل ہیں: ISubscriberجب بھی کوئی ویڈیو اپ لوڈ کی جائے گی تو سب کو مطلع کیا جائے گا۔ Subscriber ایک ٹھوس مبصر جو خود فیصلہ کرتا ہے کہ اس اطلاع کے ساتھ کیا کرنا ہے۔
// The observer interface, every subscriber implements this
public interface ISubscriber
{
void Notify(string channelName, string videoTitle);
}
// The concrete observer
public class Subscriber : ISubscriber
{
private readonly string _name;
public Subscriber(string name) => _name = name;
public void Notify(string channelName, string videoTitle)
{
Console.WriteLine($"{_name}: {channelName} just uploaded '{videoTitle}'!");
}
}
// The subject, keeps track of its subscribers and notifies them of changes
public class YouTubeChannel
{
private readonly string _name;
private readonly List _subscribers = new();
public YouTubeChannel(string name) => _name = name;
public void Subscribe(ISubscriber subscriber) => _subscribers.Add(subscriber);
public void Unsubscribe(ISubscriber subscriber) => _subscribers.Remove(subscriber);
public void UploadVideo(string title)
{
Console.WriteLine($"{_name}: Uploaded '{title}'.");
foreach (var subscriber in _subscribers)
{
subscriber.Notify(_name, title);
}
}
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
var channel = new YouTubeChannel("Code With Isaiah");
var alice = new Subscriber("Alice");
var bob = new Subscriber("Bob");
channel.Subscribe(alice);
channel.Subscribe(bob);
channel.UploadVideo("Design Patterns Explained");
channel.Unsubscribe(bob);
channel.UploadVideo("Understanding the Observer Pattern");
حساب کتاب:
Code With Isaiah: Uploaded 'Design Patterns Explained'.
Alice: Code With Isaiah just uploaded 'Design Patterns Explained'!
Bob: Code With Isaiah just uploaded 'Design Patterns Explained'!
Code With Isaiah: Uploaded 'Understanding the Observer Pattern'.
Alice: Code With Isaiah just uploaded 'Understanding the Observer Pattern'!
باب کی جانب سے اپنا سبسکرپشن منسوخ کرنے کے بعد، میں نے کبھی کوئی نیا اپ لوڈ نہیں سنا۔ چینل نے اسے کبھی نمایاں نہیں کیا اور اب اسے اپنی اطلاع کی فہرست میں شامل نہیں کیا۔ یہ مبصر پیٹرن ہے.
کب استعمال کرنا ہے۔
ایک آبزرور کا استعمال کریں جب ایک آبجیکٹ میں تبدیلی کے لیے دیگر اشیاء کی نامعلوم تعداد کو خود بخود اپ ڈیٹ کرنے کی ضرورت ہوتی ہے۔
جب آپ اشیاء کو ڈھیلے طریقے سے جوڑ کر رکھنا چاہتے ہیں تو یہ بھی مفید ہے۔ موضوع صرف مبصر انٹرفیس کے بارے میں جانتا ہے اور کوئی خاص تفصیلات نہیں ہے۔
اور یہ ایک اچھا اختیار ہے جب رن ٹائم پر انحصار کی تعداد بڑھ سکتی ہے یا سکڑ سکتی ہے، جیسے سبسکرائب کرنا اور ان سبسکرائب کرنا۔
8. ریاستی ڈیزائن پیٹرن
حقیقی کیس
ایک آن لائن آرڈر کے بارے میں سوچیں جو اس کے لائف سائیکل سے گزر رہا ہے: ہولڈ، شپ، ڈیلیور، وغیرہ۔ "اگلا قدم اٹھانے" کا اصل مطلب ہر قدم کے لیے مختلف ہے۔ زیر التواء کا مطلب ہے کہ پیکج کورئیر کے حوالے کر دیا گیا ہے۔ ڈیلیور کا مطلب ہے موصول کے بطور نشان زد۔ ایک بار ڈیلیور ہونے کے بعد، جانے کے لیے کہیں اور نہیں ہے۔ ایک بڑے طریقہ کے بجائے if تمام ممکنہ اقدامات کو چیک کرنے سے آپ کو یہ جاننے میں مدد ملے گی کہ ہر قدم کے بعد کیا آتا ہے۔
یہ ریاستی طرز ہے۔ کسی چیز کا رویہ اس کی موجودہ حالت پر منحصر ہوتا ہے، اور ہر ریاست جانتی ہے کہ اگلی حالت میں کیسے منتقل ہونا ہے۔
مسائل حل ہوئے:
-
کیا ہوگا اگر آپ کو کنڈیشنلز کی ایک لمبی زنجیر کا استعمال کرتے ہوئے تمام مراحل کو ایک طریقہ میں ہینڈل کرنا پڑے؟ ہر نئے مرحلے کے اضافے کے ساتھ، اس کی پیروی کرنا مزید مشکل ہو جائے گا۔ ہر قدم کو اس کی اپنی کلاس دینا اس کی منطق کو آزاد رکھتا ہے۔
-
کیا ہوگا اگر ایک نیا مرحلہ شامل کرنے کا مطلب ہے کہ وہی بہت بڑا طریقہ ترمیم کرنا؟ ایسا کرنے کے دوران، غیر متعلقہ مراحل پر کیڑے متعارف کروانا آسان ہے۔ ایک نئی ریاست دوسری کلاسوں کے ساتھ شامل کردہ ایک نئی کلاس ہے۔
-
کیا ہوگا اگر کسی چیز کو مکمل طور پر مختلف طریقے سے برتاؤ کرنے کی ضرورت ہے اس پر منحصر ہے کہ وہ اس کے لائف سائیکل میں کہاں ہے؟ کسی موجودہ اسٹیٹ آبجیکٹ کو سونپنے کا مطلب ہے کہ سیاق و سباق کو تفصیلات جاننے کی ضرورت نہیں ہے۔ یہ صرف یہ پوچھ رہا ہے کہ موجودہ حالت کا کیا کرنا ہے۔
سیدھے الفاظ میں، یہ کسی چیز کے طرز عمل کو تبدیل کرنے کا سبب بنتا ہے جو اس کے پاس موجود ریاستی شے کو تبدیل کرتا ہے، اس سے ایسا ظاہر ہوتا ہے جیسے شے تبدیل ہو رہی ہے کہ یہ ریاست کی تبدیلی کی بنیاد پر کیسے برتاؤ کرتی ہے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"ایک اسٹیٹ پیٹرن ایک طرز عمل سافٹ ویئر ڈیزائن پیٹرن ہے جو کسی چیز کے رویے کو تبدیل کرنے کی اجازت دیتا ہے جب اس کی اندرونی حالت تبدیل ہوتی ہے. یہ پیٹرن ایک محدود ریاستی مشین کے تصور کے قریب ہے." (ماخذ)
پروگرامنگ کی مثال:
Order سیاق و سباق میں کچھ بھی ہوتا ہے۔ IOrderState یہ موجود ہے اور تفویض کیا گیا ہے۔ ہر مخصوص ریاست، PendingState, ShippedState, DeliveredStateمیں جانتا ہوں کہ اگلی حالت کیا ہوگی۔
// The state interface, every state implements this
public interface IOrderState
{
void Next(Order order);
string Name { get; }
}
// The context, delegates behaviour to whatever state it currently holds
public class Order
{
public IOrderState State { get; set; } = new PendingState();
public void Next()
{
Console.WriteLine($"Order is currently: {State.Name}");
State.Next(this);
}
}
// Concrete states, each knows what comes after it
public class PendingState : IOrderState
{
public string Name => "Pending";
public void Next(Order order) => order.State = new ShippedState();
}
public class ShippedState : IOrderState
{
public string Name => "Shipped";
public void Next(Order order) => order.State = new DeliveredState();
}
public class DeliveredState : IOrderState
{
public string Name => "Delivered";
public void Next(Order order)
{
Console.WriteLine("Order has already been delivered. Nothing left to do.");
}
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
var order = new Order();
order.Next();
order.Next();
order.Next();
order.Next();
حساب کتاب:
Order is currently: Pending
Order is currently: Shipped
Order is currently: Delivered
Order has already been delivered. Nothing left to do.
Order میں نے کبھی چیک نہیں کیا کہ "اگر یہ زیر التواء ہے تو یہ کریں، اور اگر ڈیلیور ہو جائے تو یہ کریں۔" اس نے صرف جمود سے پوچھا کہ آگے کیا کرنا ہے، اور ملک نے خود فیصلہ کیا کہ اس کے بعد کیا کرنا ہے۔ یہ ریاستی طرز ہے۔
کب استعمال کرنا ہے۔
ریاست کا استعمال کریں جب کسی چیز کا طرز عمل اس کی حالت پر منحصر ہوتا ہے، اور اس رویے کو رن ٹائم کے وقت تبدیل ہونا چاہیے کیونکہ اس کی حالت بدل جاتی ہے۔
یہ اس وقت بھی مفید ہے جب آپ کے پاس بڑے مشروط بلاکس ہوں جو موجودہ حالت یا آبجیکٹ کی قسم کی بنیاد پر برانچ کرتے ہیں۔
اور اس کا انتخاب اس وقت کریں جب ریاستوں کے درمیان منتقلی کو ایک بڑے طریقہ میں پھیلانے کے بجائے واضح اور خود ساختہ ہونے کی ضرورت ہو۔
9. حکمت عملی ڈیزائن پیٹرن
حقیقی کیس
آن لائن اسٹور پر چیک آؤٹ کرنے کے بارے میں سوچیں۔ آپ کریڈٹ کارڈ یا پے پال کے ذریعے ادائیگی کر سکتے ہیں۔ خریداری کی ٹوکری کو پرواہ نہیں ہے کہ آپ کیا منتخب کرتے ہیں۔ ہم کل رقم جان لیں گے، اسے آپ کے منتخب کردہ ادائیگی کے طریقہ کار تک پہنچائیں گے، اور اس کی تفصیلات کو ہینڈل کریں گے کہ اس طریقہ سے اصل میں کس طرح چارج کیا جائے گا۔ آپ کی ادائیگی کا طریقہ تبدیل کرنے سے آپ کے شاپنگ کارٹ میں موجود کوڈ میں کوئی تبدیلی نہیں آئے گی۔
یہ حکمت عملی کا نمونہ ہے۔ الگورتھم (اس معاملے میں "ادائیگی کا طریقہ") اس کی اپنی قابل تبادلہ آبجیکٹ میں نکالا جاتا ہے، اور کلائنٹ آسانی سے منتخب کرتا ہے کہ کون سا اعتراض استعمال کرنا ہے۔
مسائل حل ہوئے:
-
اگر آپ کی ٹوکری بڑی ہے تو کیا ہوگا؟
if/elseادائیگی کے تمام طریقوں کے لیے؟ کچھ نیا شامل کرنے کا مطلب ہر بار اس طریقہ میں ترمیم کرنا ہے۔ ادائیگی کے ہر طریقہ کو اس کی اپنی کلاس میں درآمد کرکے، آپ کو اپنا کارٹ تبدیل کرنے کی ضرورت نہیں ہے۔ -
اگر آپ رن ٹائم پر الگورتھم کو تبدیل کرنا چاہتے ہیں تو کیا ہوگا؟ ہارڈ کوڈ شدہ طریقوں کو تبدیل نہیں کیا جا سکتا۔ ایک حکمت عملی آبجیکٹ کو آسانی سے کسی اور شے سے تبدیل کیا جاسکتا ہے جو ایک ہی انٹرفیس کو نافذ کرتا ہے۔
-
کیا ہوگا اگر آپ کو ایک مشترکہ انٹرفیس کا اشتراک کرنے کے لیے ادائیگی کے دو مختلف طریقوں کی ضرورت ہے، لیکن کچھ نہیں؟ ہر ایک حکمت عملی کا انٹرفیس نافذ کرتا ہے، لیکن اندرونی تفصیلات (کارڈ نمبر، ای میل) کو نجی رکھا جاتا ہے۔
سیدھے الفاظ میں، یہ الگورتھم کو اپنے قابل تبادلہ آبجیکٹ کے طور پر درآمد کرتا ہے، لہذا جو کلاسز اسے استعمال کرتی ہیں انہیں یہ جاننے یا اس کی پرواہ کرنے کی ضرورت نہیں ہے کہ کون سا مخصوص ورژن چل رہا ہے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"حکمت عملی کے نمونے طرز عمل کے سافٹ ویئر ڈیزائن کے نمونے ہیں جو رن ٹائم پر الگورتھمک انتخاب کو قابل بناتے ہیں۔" (ذریعہ)
پروگرامنگ کی مثال:
ShoppingCart سیاق و سباق اس طرح ہے: IPaymentStrategy اصل ادائیگیوں کو تفویض کریں۔ CreditCardPayment اور PayPalPayment مخصوص حکمت عملی ہیں اور ہر ایک کی ادائیگی کے مختلف طریقے ہیں۔
// The strategy interface, every payment method implements this
public interface IPaymentStrategy
{
void Pay(decimal amount);
}
// Concrete strategies, each a different way to pay
public class CreditCardPayment : IPaymentStrategy
{
private readonly string _cardNumber;
public CreditCardPayment(string cardNumber) => _cardNumber = cardNumber;
public void Pay(decimal amount)
{
Console.WriteLine($"Charged ${amount} to credit card ending in {_cardNumber[^4..]}.");
}
}
public class PayPalPayment : IPaymentStrategy
{
private readonly string _email;
public PayPalPayment(string email) => _email = email;
public void Pay(decimal amount)
{
Console.WriteLine($"Charged ${amount} via PayPal account {_email}.");
}
}
// The context, holds a strategy and delegates the actual payment work to it
public class ShoppingCart
{
private readonly decimal _total;
private IPaymentStrategy? _paymentMethod;
public ShoppingCart(decimal total) => _total = total;
public void SetPaymentMethod(IPaymentStrategy method) => _paymentMethod = method;
public void Checkout()
{
if (_paymentMethod is null)
{
Console.WriteLine("No payment method selected.");
return;
}
_paymentMethod.Pay(_total);
}
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
var cart = new ShoppingCart(59.99m);
cart.SetPaymentMethod(new CreditCardPayment("4111 1111 1111 1111"));
cart.Checkout();
cart.SetPaymentMethod(new PayPalPayment("isaiah@example.com"));
cart.Checkout();
حساب کتاب:
Charged $59.99 to credit card ending in 1111.
Charged $59.99 via PayPal account isaiah@example.com.
ShoppingCart مجھے اس بات کا کوئی اندازہ نہیں تھا کہ میری ادائیگی اصل میں کیسے عمل میں آئی۔ میں نے ابھی فون کیا۔ Pay() اس وقت آپ کے پاس جو بھی حکمت عملی تھی۔ یہ حکمت عملی کا نمونہ ہے۔ الگورتھم کو تبدیل کر دیا گیا ہے اور جو کلاسز اسے استعمال کرتی ہیں وہ بالکل وہی رہتی ہیں۔
کب استعمال کرنا ہے۔
جب آپ کے پاس الگورتھم کی متعدد قسمیں ہوں اور رن ٹائم پر ان کے درمیان سوئچ کرنا چاہتے ہوں تو حکمت عملی استعمال کریں۔
یہ بھی مفید ہے اگر آپ شرائط سے بھری کلاسوں سے بچنا چاہتے ہیں جو قسم یا جھنڈے کی بنیاد پر طرز عمل کا انتخاب کرتے ہیں۔
اور اگر اس میں شامل کلاسوں میں صرف مختلف طرز عمل ہیں جو وہ استعمال کرتے ہیں، اور وہ طرز عمل قابل تبادلہ ہونا چاہیے، یہ ایک ٹھوس انتخاب ہے۔
10. ٹیمپلیٹ طریقہ ڈیزائن پیٹرن
حقیقی کیس
گرم مشروبات، چائے یا کافی بنانے کا سوچیں۔ دونوں ایک ہی بنیادی مراحل کی پیروی کرتے ہیں۔ یعنی پانی کو ابالیں، ابالیں، کپ میں ڈالیں اور حسب ذائقہ کچھ شامل کریں۔ فرق صرف دو قدموں کا ہے۔ یہ چائے بنانے اور کافی کو پیسنے اور ابالنے کے اقدامات ہیں۔ چائے کو لیموں، کافی کو چینی اور دودھ ملتا ہے۔ پوری ترکیب کبھی نہیں بدلتی، صرف چند مراحل کی مخصوص تفصیلات۔
یہ ٹیمپلیٹ طریقہ پیٹرن ہے. بیس کلاس الگورتھم کے فکسڈ کنکال کی وضاحت کرتی ہے، اور ذیلی کلاسیں صرف ان مراحل کو بھرتی ہیں جن کو حقیقت میں تبدیل کرنے کی اجازت ہے۔
مسائل حل ہوئے:
-
کیا ہوگا اگر ہر ڈرنک شروع سے پوری ترکیب کو دہرائے؟ پانی کو ابال کر ایک کپ میں ڈالنا ہر کلاس کو دہرایا جاتا ہے۔ ٹیمپلیٹ کے طریقہ کار کا مطلب ہے کہ آپ اقدامات کو ایک بار لکھیں اور انہیں ایک جگہ رکھیں۔
-
کیا ہوگا اگر ذیلی طبقات اقدامات کو دوبارہ ترتیب دے سکیں یا قدموں کو مکمل طور پر چھوڑ دیں؟ پھر ہر مشروب پوری ترکیب کو توڑ سکتا ہے۔ الگورتھم کا ڈھانچہ بنیادی طبقے میں ایک واحد طریقہ کے طور پر موجود ہے، اس لیے اس کی ترتیب اور ساخت مستقل رہتی ہے۔
-
اگر میں نیا مشروب شامل کرنا چاہوں تو کیا ہوگا؟ آپ کو بس دیگر مراحل کو پُر کرنا ہے، بشمول پکنے اور پکانا۔ باقی سب کچھ پہلے ہی بیس کلاس کے ذریعہ سنبھالا جاتا ہے۔
سیدھے الفاظ میں، اپنی بیس کلاس میں الگورتھم کے فکسڈ اسکیلیٹن کی وضاحت کریں اور ذیلی کلاسوں میں صرف ان مراحل کو بھریں جو حقیقت میں مختلف ہو سکتے ہیں۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"آبجیکٹ اورینٹڈ پروگرامنگ میں، ٹیمپلیٹ کا طریقہ ایک طرز عمل کے ڈیزائن کے نمونوں میں سے ایک ہے جسے Gamma et al. نے کتاب ڈیزائن پیٹرنز میں شناخت کیا ہے۔ ٹیمپلیٹ کا طریقہ ایک سپر کلاس کا طریقہ ہے، عام طور پر ایک تجریدی سپر کلاس، اور کئی اعلی درجے کے مراحل کے لحاظ سے آپریشن کے کنکال کی وضاحت کرتا ہے۔" (ماخذ)
پروگرامنگ کی مثال:
Beverage وضاحت کریں Prepare() سانچہ کا طریقہ: اقدامات کی ایک مقررہ ترتیب۔ Tea اور Coffee صرف اوور رائڈ کریں۔ Brew() اور AddCondiments()یہ وہ دو مراحل ہیں جہاں تبدیلیوں کی اصل میں اجازت ہے۔
// The abstract class, defines the skeleton of the algorithm
public abstract class Beverage
{
// The template method, the steps and their order never change
public void Prepare()
{
BoilWater();
Brew();
PourInCup();
AddCondiments();
}
private void BoilWater() => Console.WriteLine("Boiling water.");
private void PourInCup() => Console.WriteLine("Pouring into cup.");
// Steps left for subclasses to fill in
protected abstract void Brew();
protected abstract void AddCondiments();
}
// A concrete class, fills in the steps specific to tea
public class Tea : Beverage
{
protected override void Brew() => Console.WriteLine("Steeping the tea bag.");
protected override void AddCondiments() => Console.WriteLine("Adding lemon.");
}
// Another concrete class, fills in the steps specific to coffee
public class Coffee : Beverage
{
protected override void Brew() => Console.WriteLine("Brewing the coffee grounds.");
protected override void AddCondiments() => Console.WriteLine("Adding sugar and milk.");
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
Beverage tea = new Tea();
Beverage coffee = new Coffee();
tea.Prepare();
Console.WriteLine();
coffee.Prepare();
حساب کتاب:
Boiling water.
Steeping the tea bag.
Pouring into cup.
Adding lemon.
Boiling water.
Brewing the coffee grounds.
Pouring into cup.
Adding sugar and milk.
دونوں نے ابلا ہوا پانی پیا اور اسی طرح اپنے پیالوں میں ڈالا۔ Prepare() ہم نے اسے اپنی بیس کلاس میں سنبھالا۔ صرف پکنے اور پکانے میں تبدیلی آئی ہے۔ کیونکہ یہ وہ قدم تھا جس کے لیے ہر ذیلی طبقہ دراصل ذمہ دار تھا۔ یہ ٹیمپلیٹ طریقہ پیٹرن ہے.
کب استعمال کرنا ہے۔
ٹیمپلیٹ کا طریقہ استعمال کریں جب متعدد کلاسز ایک ہی مجموعی الگورتھم کا اشتراک کریں لیکن چند مخصوص مراحل میں مختلف ہوں۔
یہ ایک اچھا انتخاب ہے اگر آپ ایک مقررہ مرحلہ آرڈر کو نافذ کرنا چاہتے ہیں لیکن ذیلی طبقات کو اس کے کچھ حصوں کو حسب ضرورت بنانے کی اجازت دیتے ہیں۔
اور یہ مفید ہے اگر آپ الگورتھم کے ان حصوں کو نقل کرنے سے بچنا چاہتے ہیں جو تمام ذیلی طبقات میں کبھی تبدیل نہیں ہوتے ہیں۔
11. وزیٹر ڈیزائن پیٹرن
حقیقی کیس
ایک خریداری کی ٹوکری پر غور کریں جس میں متعدد اشیاء، جیسے کتابیں اور الیکٹرانکس شامل ہوں۔ ہر ادائیگی پر ٹیکس مختلف طریقے سے وصول کیے جاتے ہیں۔ قیمتوں کا تعین کرنے کی منطق Book اور Electronic خاص طور پر، اگر آپ کو دوسرے کاموں کی ضرورت ہو، جیسے شپنگ لیبل بنانا یا وارنٹی کے خلاصے، آپ بعد میں خود بھی کلاس استعمال کر سکتے ہیں۔ اس کے بجائے، ہر آئٹم وزیٹر کو قبول کرتا ہے اور خود کو حوالے کرتا ہے۔ زائرین وہ ہوتے ہیں جو حقیقت میں کتابوں کی قیمت ای کتابوں سے مختلف طریقے سے لینا جانتے ہیں۔
یہ وزیٹر پیٹرن ہے. اعمال ان اشیاء کے باہر موجود ہیں جن پر وہ انجام پاتے ہیں، اور ہر شے دیکھنے والے کو بتاتی ہے کہ انہیں کیا جاننے کی ضرورت ہے: یہ اصل میں کس قسم کا ہے۔
مسائل حل ہوئے:
-
اگر قیمتوں کی منطق براہ راست اندرون خانہ لکھی جائے تو کیا ہوگا؟
BookاورElectronic? ہر نیا کام (ٹیکس، شپنگ، وارنٹی) کا مطلب ہے دونوں کلاسوں میں بار بار ترمیم کرنا۔ اس کے بجائے، وزیٹر ہر نئے کام کو اپنی کلاس میں رکھتا ہے۔ -
اگر آپ کو موجودہ آئٹم کلاس کو چھوئے بغیر نیا آپریشن شامل کرنے کی ضرورت ہو تو کیا ہوگا؟ عام طور پر اس کا مطلب ہے ان تمام کلاسوں میں ترمیم کرنا جن پر آپریشن لاگو ہوتا ہے۔ ایک نیا مہمان ایک نئی کلاس ہے،
BookاورElectronicکبھی نہ بدلیں۔ -
کیا ہوگا اگر ریگولر لوپ کو ہر آئٹم کی مخصوص قسم کا اندازہ لگانا ہو؟ اس کا مطلب عام طور پر قسم کی جانچ کی ایک سیریز ہے۔
Accept()کال کرناVisit(this)مرتب کرنے والے کو ایک اوورلوڈ کی ضرورت کے بغیر خود بخود درست اوورلوڈ کو منتخب کرنے کی اجازت دیتا ہے۔ifیا چیک ٹائپ کریں۔
سیدھے الفاظ میں، یہ اس چیز سے کارروائیوں کو منتقل کرتا ہے جس پر وہ اس کی اپنی کلاس میں انجام دیتے ہیں۔ ہر آبجیکٹ وزیٹر کو قبول کرتا ہے اور اسے بتاتا ہے کہ یہ کس مخصوص قسم کا ہے۔
ویکیپیڈیا اس کی وضاحت اس طرح کرتا ہے:
"وزیٹر ڈیزائن پیٹرن الگورتھم کو آبجیکٹ ڈھانچے سے الگ کرنے کا ایک طریقہ ہے جس پر وہ کام کرتے ہیں۔" (ماخذ)
پروگرامنگ کی مثال:
Book اور Electronic دونوں کو لاگو کریں IItem اور بس مجھے ایک کال دو visitor.Visit(this). PricingVisitor لاگو IVisitor ہر ٹھوس قسم کے لیے اوورلوڈ ہوتے ہیں، اس لیے قیمتوں کا درست منطق خود بخود عمل میں آتا ہے۔
// The element interface, every item in the cart implements this
public interface IItem
{
void Accept(IVisitor visitor);
}
// Concrete elements, each accepts a visitor and hands itself over
public class Book : IItem
{
public string Title { get; }
public decimal Price { get; }
public Book(string title, decimal price)
{
Title = title;
Price = price;
}
public void Accept(IVisitor visitor) => visitor.Visit(this);
}
public class Electronic : IItem
{
public string Name { get; }
public decimal Price { get; }
public Electronic(string name, decimal price)
{
Name = name;
Price = price;
}
public void Accept(IVisitor visitor) => visitor.Visit(this);
}
// The visitor interface, one Visit overload per concrete element
public interface IVisitor
{
void Visit(Book book);
void Visit(Electronic electronic);
}
// A concrete visitor, adds a new operation without touching Book or Electronic
public class PricingVisitor : IVisitor
{
public decimal Total { get; private set; }
public void Visit(Book book)
{
Console.WriteLine($"Book: {book.Title} — ${book.Price:F2} (no tax).");
Total += book.Price;
}
public void Visit(Electronic electronic)
{
var priceWithTax = electronic.Price * 1.15m;
Console.WriteLine($"Electronic: {electronic.Name} — ${priceWithTax:F2} (with 15% tax).");
Total += priceWithTax;
}
}
اب آئیے اسے عملی طور پر دیکھتے ہیں۔
var cart = new List
{
new Book("Design Patterns", 45.00m),
new Electronic("Headphones", 120.00m)
};
var pricingVisitor = new PricingVisitor();
foreach (var item in cart)
{
item.Accept(pricingVisitor);
}
Console.WriteLine($"Total: ${pricingVisitor.Total:F2}");
حساب کتاب:
Book: Design Patterns — $45.00 (no tax).
Electronic: Headphones — $138.00 (with 15% tax).
Total: $183.00
ان میں سے کوئی بھی نہیں Book نہ ہی Electronic قیمتوں کی منطق کی ایک لائن پر مشتمل ہے۔ ہر ایک صرف جانتا تھا کہ کس طرح Accept() مہمان PricingVisitor یہ دراصل وہی تھا جس نے فیصلہ کیا کہ ہر قسم کی قیمت کیسے لگائی جاتی ہے۔ یہ وزیٹر پیٹرن ہے. آپریشن آبجیکٹ کے ڈھانچے سے باہر ہیں، اس کے اندر نہیں۔
کب استعمال کرنا ہے۔
جب آپ کو ہر کلاس کو اس کی اپنی منطق سے آلودہ کیے بغیر غیر متعلقہ کلاسوں کے گروپ میں آپریشن کرنے کی ضرورت ہو تو وزٹرز کا استعمال کریں۔
اگر آپ نئے کاموں کو کثرت سے شامل کرنا چاہتے ہیں تو یہ بھی مفید ہے، لیکن آبجیکٹ کی ساخت خود شاذ و نادر ہی تبدیل ہوتی ہے۔
اور یہ ایک اچھا انتخاب ہے جب آپ کو یہ معلوم کرنے کے لیے ٹائپ چیکنگ یا کاسٹ کرنے کی ضرورت ہو کہ مجموعہ میں موجود ہر چیز کے ساتھ کیا کرنا ہے۔
نتیجہ
اس میں تین خاندانوں کے تمام 23 کلاسک ڈیزائن پیٹرن شامل ہیں: تخلیق، ساخت، اور طرز عمل۔
ان میں سے کسی کے لیے بھی کوئی اصول نہیں ہیں۔ یہ اس مسئلے کا جواب ہے جو سافٹ ویئر میں ظاہر ہوتا رہتا ہے۔ اشیاء کو ان کی صحیح اقسام کو سختی سے کوڈ کیے بغیر کیسے بنایا جائے، چھوٹے ڈھانچے سے بڑے ڈھانچے کیسے بنائے جائیں، اور کس طرح اشیاء کو مضبوطی سے باندھے بغیر بات چیت کرنے کی اجازت دی جائے۔
یاد رکھنے کے لیے چند باتیں:
-
زیادہ تر معاملات میں، ان میں سے زیادہ تر نمونے استعمال نہیں کیے جائیں گے۔ پہچان جب آپ کو کسی مسئلے کی ضرورت ہو۔ یہ اصل ہنر ہے۔ کسی ایسے مسئلے پر پیٹرن کو مجبور کرنا جس کی ضرورت نہیں ہے عام طور پر کوڈ کو پیروی کرنا زیادہ مشکل بنا دیتا ہے، آسان نہیں۔
-
حقیقی دنیا کے استعارے وجدان کی تعمیر کے لیے موجود ہیں، نہ کہ لفظی طور پر۔ ایک بار جب کسی کہانی میں پیٹرن کے ظاہر ہونے پر آپ کلکس کو سمجھتے ہیں، تو اسے اصل کوڈ میں تلاش کرنا بہت آسان ہو جاتا ہے۔
-
ایک نمونہ بنتا ہے۔ فیکٹری کے طریقے ایسی چیزیں بنا سکتے ہیں جو خود ڈیکوریٹر ہیں۔ بلڈر کا استعمال کرتے ہوئے جامع درخت بنائے جا سکتے ہیں۔ حقیقی دنیا کے نظام ان کو تنہائی میں استعمال کرنے کے بجائے نمونوں کو مکس اور پرت دیتے ہیں۔
-
زبان سے کوئی فرق نہیں پڑتا۔ یہاں کی تمام مثالیں C# میں ہیں، لیکن اگر آپ سمجھتے ہیں تو Python، Java، TypeScript، Go، Rust وغیرہ میں وہی شکل موجود ہے۔ مسئلہ ایک بار جب پیٹرن حل ہوجاتا ہے، تو اسے کسی بھی زبان میں ترجمہ کرنا آسان ہے۔
مقصد 23 ناموں کو حفظ کرنا نہیں ہے۔ اس کے نیچے، آپ بار بار آنے والی دشواریوں کو پہچانیں گے، اس لیے جب وہ آپ کے کوڈ میں ظاہر ہوں گے، تو آپ ان کو ٹھیک کرنے کی ایک ثابت شدہ شکل کو پہلے ہی جانتے ہیں۔
"ہر پیٹرن ایک ایسے مسئلے کی وضاحت کرتا ہے جو ہمارے ماحول میں بار بار ہوتا ہے اور اس مسئلے کے حل کے جوہر کو اس انداز میں بیان کرتا ہے جو آپ کو اس حل کو ایک ملین بار استعمال کرنے کی اجازت دیتا ہے اور کبھی بھی دو بار ایسا کیے بغیر۔"
ماخذ: کرسٹوفر الیگزینڈر، پیٹرن کی زبان - آرکیٹیکچرل کام جس نے سافٹ ویئر ڈیزائن کے اصل نمونوں کو متاثر کیا۔
اگر آپ کو یہ ہینڈ بک مفید معلوم ہوتی ہے، تو ماخذ github.com/Clifftech123/design-patterns-handbook پر ہے۔ ان نمونوں کے لیے ستارہ لگائیں، کانٹا لگائیں یا PR کھولیں جو آپ کے خیال میں غائب ہیں۔