الگوی استخر اشیاء، جزو الگوهای تکوینی است و کار آن جلوگیری از ایجاد اشیاء تکراری و محافظت از به هدر رفتن حافظه است. نحوه کار این الگو بدین شکل است که وقتی کاربر درخواست نمونهای از یک شیء را میدهد، بعد از اتمام کار، شیء نابود نمیشود؛ بلکه به استخر بازگشت داده میشود تا در درخواستهای آینده، مجددا مورد استفاده قرار گیرد. این کار موجب عدم هدر رفتن حافظه و همچنین بالا رفتن کارآیی برنامه میگردد. این الگو به خصوص برای اشیایی که مدت کوتاهی مورد استفاده قرار میگیرند و آماده سازی آنها هزینه بر است بسیار کارآمد میباشد. از آنجا که هزینهی ساخت و نابود سازی در حد بالایی قرار دارد و آن شیء به طور مکرر مورد استفاده قرار میگیرد، زمان زیادی صرفه جویی میشود.
در این الگو ابتدا شیء از استخر درخواست میشود. اگر قبلا ایجاد شده باشد، مجددا مورد استفاده قرار میگیرد. در غیر این صورت نمونه جدیدی ایجاد میشود و وقتی که کار آن پایان یافت به استخر بازگشت داده شده و نگهداری میشود، تا زمانی که مجددا درخواست استفاده از آن برسد.
یکی از نکات مهم و کلیدی این است که وقتی کار شیء مربوطه تمام شد، باید اطلاعات شیء قبلی و دادههای آن نیز پاکسازی شوند؛ در غیر این صورت احتمال آسیب و خطا در برنامه بالا میرود و این الگو را به یک ضد الگو تبدیل خواهد کرد.
نمونهای از پیاده سازی این الگو را در دات نت، میتوانید در data providerهای مخصوص SQL Server نیز ببینید. از آنجا که ایجاد اتصال به دیتابیس، هزینه بر بوده و دستورات در کوتاهترین زمان ممکن اجرا میشوند، این کانکشنها یا اتصالات بعد از بسته شدن از بین نمیروند، بلکه در استخر مانده تا زمانیکه مجددا نمونه مشابهی از آنها درخواست شود تا دوباره مورد استفاده قرار بگیرند تا از هزینه اولیه برای ایجاد آنها پرهیز شود. استفاده از این الگو در برنامه نویسی با سوکت ها، تردها و کار با اشیاء گرافیکی بزرگ مثل فونتها و تصاویر Bitmap کمک شایانی میکند. ولی در عوض برای اشیای ساده میتواند کارآیی را به شدت کاهش دهد.
الگوی بالا را در سی شارپ بررسی میکنیم:
ابتدا کلاس PooledObject را به عنوان یک شیء بزرگ و پر استفاده ایجاد میکنیم:
tempdata ویژگی است که باید برای هر شیء، مجزا باشد. پس باید دقت داشت برای استفادههای بعدی نیاز است پاک سازی شود.
بعد از آن کلاس پول را پیاده سازی میکنیم:
در کلاس بالا، کاربر با متد GetObject، درخواست شیءایی را میکند و متد نگاه میکند که اگر لیست موجودی، پر باشد، اولین نمونهی در دسترس را ارسال میکند. ولی در صورتیکه نمونهای از آن نباشد، یک نمونهی جدید را ایجاد کرده و آن را در لیست مورد استفادهها قرار میدهد و به کاربر باز میگرداند.
متد Release Object وظیفهی بازگرداندن شیء را به استخر، دارد که از لیست در حال استفادهها آن را حذف کرده و به لیست موجودی اضافه میکند. متد Cleanup در این بین وظیفه ریست کردن شیء را دارد تا مشکلی که در بالا بیان کردیم رخ ندهد.
کد زیر را اجرا میکنیم:
نتیجه به شکل زیر است:
ابتدا شیء obj1 ایجاد میشود و سپس obj2 و بعد از آن obj1 به لیست موجودیها بازگشته و برای obj3 همان شیء را بازگشت میهد که برای obj1 ساخته بود. توقف تردها در این مثال برای مشاهدهی بهتر زمان است.
در این الگو ابتدا شیء از استخر درخواست میشود. اگر قبلا ایجاد شده باشد، مجددا مورد استفاده قرار میگیرد. در غیر این صورت نمونه جدیدی ایجاد میشود و وقتی که کار آن پایان یافت به استخر بازگشت داده شده و نگهداری میشود، تا زمانی که مجددا درخواست استفاده از آن برسد.
یکی از نکات مهم و کلیدی این است که وقتی کار شیء مربوطه تمام شد، باید اطلاعات شیء قبلی و دادههای آن نیز پاکسازی شوند؛ در غیر این صورت احتمال آسیب و خطا در برنامه بالا میرود و این الگو را به یک ضد الگو تبدیل خواهد کرد.
نمونهای از پیاده سازی این الگو را در دات نت، میتوانید در data providerهای مخصوص SQL Server نیز ببینید. از آنجا که ایجاد اتصال به دیتابیس، هزینه بر بوده و دستورات در کوتاهترین زمان ممکن اجرا میشوند، این کانکشنها یا اتصالات بعد از بسته شدن از بین نمیروند، بلکه در استخر مانده تا زمانیکه مجددا نمونه مشابهی از آنها درخواست شود تا دوباره مورد استفاده قرار بگیرند تا از هزینه اولیه برای ایجاد آنها پرهیز شود. استفاده از این الگو در برنامه نویسی با سوکت ها، تردها و کار با اشیاء گرافیکی بزرگ مثل فونتها و تصاویر Bitmap کمک شایانی میکند. ولی در عوض برای اشیای ساده میتواند کارآیی را به شدت کاهش دهد.
الگوی بالا را در سی شارپ بررسی میکنیم:
ابتدا کلاس PooledObject را به عنوان یک شیء بزرگ و پر استفاده ایجاد میکنیم:
public class PooledObject { DateTime _createdAt = DateTime.Now; public DateTime CreatedAt { get { return _createdAt; } } public string TempData { get; set; } public void DoSomething(string name) { Console.WriteLine($"{name} : {TempData} is written on {CreatedAt}"); } }
بعد از آن کلاس پول را پیاده سازی میکنیم:
public static class Pool { private static List<PooledObject> _available = new List<PooledObject>(); private static List<PooledObject> _inUse = new List<PooledObject>(); public static PooledObject GetObject() { lock(_available) { if (_available.Count != 0) { PooledObject po = _available[0]; _inUse.Add(po); _available.RemoveAt(0); return po; } else { PooledObject po = new PooledObject(); _inUse.Add(po); return po; } } } public static void ReleaseObject(PooledObject po) { CleanUp(po); lock (_available) { _available.Add(po); _inUse.Remove(po); } } private static void CleanUp(PooledObject po) { po.TempData = null; } }
متد Release Object وظیفهی بازگرداندن شیء را به استخر، دارد که از لیست در حال استفادهها آن را حذف کرده و به لیست موجودی اضافه میکند. متد Cleanup در این بین وظیفه ریست کردن شیء را دارد تا مشکلی که در بالا بیان کردیم رخ ندهد.
کد زیر را اجرا میکنیم:
var obj1 = Pool.GetObject(); obj1.DoSomething("obj1"); Thread.Sleep(2000); var obj2 = Pool.GetObject(); obj2.DoSomething("obj2"); Pool.ReleaseObject(obj1); Thread.Sleep(2000); var obj3 = Pool.GetObject(); obj3.DoSomething("obj3");
obj1 : is written on 4/21/2016 11:25:26 AM obj2 : is written on 4/21/2016 11:25:28 AM obj3 : is written on 4/21/2016 11:25:26 AM