الگوی Flyweight

الگوی Flyweight در دسته الگوهای ساختاری قرار دارد. زمانی که تعداد زیادی از اشیا را دارید و مدام از آنها نمونه‌سازی می کنید بهتر است که آنها رابه اشتراک بگذارید و از اشغال حافظه توسط نمونه های تکراری جلوگیری کنید.به عبارت دیگر فاکتور گیری از اشیا تکراری و صرفه جویی در مصرف حافظه .UML class diagram شركت كنندگان کلاس ها و اشیاء شرکت کننده در این الگو عبارتند از:سبک وزن Flyweight ( کاراکتر در مثال دنیای واقعی Character) :تعریف یک Interface (رابط) flyweight ها از طریق آن میتواند در وضعیت بیرونی دریافت و عمل کند. به عبارت دیگر واسطی است که شکل کلی تمام اشیای برنامه (سبک وزن/ اشتراکی و غیر سبک وزن/ غیر اشتراکی) را تعریف می کند در این واسط وضعیت بیرونی مورد نیاز اشیاء سبک وزن به شکل پارامتر در متدها در نظر گرفته میشود. از این رو اشیا سبک وزن از طریق آنها می‌توانند اطلاعات مربوط به وضعیت بیرونی را دریافت کرده و استفاده کنند. سبک وزن سفت ConcreteFlyweight ( در مثال دنیای واقعی CharacterA, CharacterB, ..., CharacterZ) :پیاده سازی (Interface) رابط Flyweight و اضافه می کند ذخیره سازی برای وضعیت درونی(intrinsic state)، اگر وجود داشته باشد. یک شی ConcreteFlyweight باید قابل اشتراک گذاری باشد.هر وضعیت که آن را ذخیره می کند، باید درونی باشد به این معنا که، این باید مستقل از زمینه ( context ) شی ConcreteFlyweight باشد. به عبارت دیگر کلاس یک شیء سبک وزن است که یک وضعیت درونی (intrsicState) و یک وضعیت بیرونی (extrinsicState) تشکیل می شود. در حالت عادی از هر ConcreteFlyweight تنها یک نمونه ساخته می شود. سبک وزن سفت غیر اشتراکی UnsharedConcreteFlyweight ( در مثال دنیای واقعی استفاده نشده است not used ) :همه subclass (زیرکلاس ها) Flyweight نیازی به اشترک گذاری ندارند. Flyweight interface امکان اشتراک گذاری را فراهم می کند. اما آن را اجباری نمی کند. برای اشیاء UnsharedConcreteFlyweight برای داشتن اشیای ConcreteFlyweight به عنوان فرزند در برخی از سطوح در ساختار شی flyweight (به عنوان کلاس های ردیف و ستون) رایج است. به عبارت دیگر این کلاس به دلیل اجرای الگوی کامپوزیت (Composite - مرکب/مخلوط) طراحی شده است و معادل یک مولفه گروهی است که مجموعه ای از اشیاء سبک وزن را در نگهداری می کند،اما خودش غیر سبک وزن است. یعنی نمونه های آن به صورت اشتراکی استفاده نشده و هنگام نیاز، هر بار یک شی مجزا از آن ایجاد می شود( در واقع واسطی که کلاس Flyweight برای اشیا سبک وزن تعریف کرده اجبار نمی کند زیر کلاس (SubClass) ها سبک وزن باشند). برای نمونه کلاس های Row و Column در برنامه واژه پرداز چنین کلاسی هستند.کارخانه سبک وزن FlyweightFactory (کارخانه کارکتر در مثال دنیای واقعی CharacterFactory) :اشیاء flyweight را ایجاد و مدیریت می کند.تضمین می کند که flyweight به درستی به اشتراک گذاشته می شود.هنگامی که یک client درخواست یک flyweight می کند،اشیاء FlyweightFactory یک نمونه موجود موجود را ایجاد می کنند، اگر وجود نداشته باشد یکی را ایجاد می کنند.به عبارت دیگر کارخانه تولید اشیاء سبک وزن است و کلاسی است که ایجاد اشیا سبک و از را مدیریت می کند. کلاینت ای که به نمونه ای از ConcreteFlyweight نیاز دارد نباید مستقیماً از آن نمونه سازی کند، بلکه باید درخواست خود را به FlyweightFactoryبدهد. این کلاس نیز دقت می کند اشیا سبک وزن تنها یک بار ایجاد شوند و در درخواست های بعدی از شیء از پیش ایجاد شده استفاده می کند. کلاینت Client ( در مثال واقعی FlyweightApp) : ارجاع به سبک وزن (ها) را حفظ می کند.محاسبه یا ذخیره وضعیت بیرونی سبک وزن (ها).به عبارت دیگر کلاینتی است که با اشیا مختلف برنامه اعم از سبک وزن یا غیر سبک وزن کار میکند. این کلاس هنگام فراخوانی متد Operation() برای اشیا Flyweight موظف است وضعیت بیرونی آنها را محاسبه کرده و به آنها تحویل بدهد. این مسئله بسیار مهم است. لذا شی سبک وزن خودش وضعیت بیرونی اش را حفظ نمی کند. این کار بر عهده کلاینت است. ساختار کلی کد using System; using System.Collections; namespace DoFactory.GangOfFour.Flyweight.Structural { /// <summary> /// MainApp startup class for Structural /// Flyweight Design Pattern. /// </summary> class MainApp { /// <summary> /// Entry point into console application. /// </summary> static void Main () { // Arbitrary extrinsic state int extrinsicstate = 22; FlyweightFactory factory = new FlyweightFactory (); // Work with different flyweight instances Flyweight fx = factory.GetFlyweight ("X"); fx.Operation (--extrinsicstate); Flyweight fy = factory.GetFlyweight ("Y"); fy.Operation (--extrinsicstate); Flyweight fz = factory.GetFlyweight ("Z"); fz.Operation (--extrinsicstate); UnsharedConcreteFlyweight fu = new UnsharedConcreteFlyweight (); fu.Operation (--extrinsicstate); // Wait for user Console.ReadKey (); } } /// <summary> /// The 'FlyweightFactory' class /// </summary> class FlyweightFactory { private Hashtable flyweights = new Hashtable (); // Constructor public FlyweightFactory () { flyweights.Add ("X", new ConcreteFlyweight ()); flyweights.Add ("Y", new ConcreteFlyweight ()); flyweights.Add ("Z", new ConcreteFlyweight ()); } public Flyweight GetFlyweight (string key) { return ((Flyweight) flyweights[key]); } } /// <summary> /// The 'Flyweight' abstract class /// </summary> abstract class Flyweight { public abstract void Operation (int extrinsicstate); } /// <summary> /// The 'ConcreteFlyweight' class /// </summary> class ConcreteFlyweight : Flyweight { public override void Operation (int extrinsicstate) { Console.WriteLine ("ConcreteFlyweight: " + extrinsicstate); } } /// <summary> /// The 'UnsharedConcreteFlyweight' class /// </summary> class UnsharedConcreteFlyweight : Flyweight { public override void Operation (int extrinsicstate) { Console.WriteLine ("UnsharedConcreteFlyweight: " + extrinsicstate); } } } OutputConcreteFlyweight : 21ConcreteFlyweight : 20ConcreteFlyweight : 19UnsharedConcreteFlyweight : 18یا public class FlyweightFactory { private Dictionary<char, Flyweight> flyweights = new Dictionary<char, Flyweight>(); public FlyweightFactory() { flyweights.Add('a', new ConcreteFlyweight('a')); flyweights.Add('b', new ConcreteFlyweight('b')); flyweights.Add('c', new ConcreteFlyweight('c')); } public Flyweight GetFlyweight(char key) { return flyweights[key]; } } public abstract class Flyweight { public abstract void Operation(string extrinsicstate); } internal class ConcreteFlyweight : Flyweight { private char intrinsicState; public ConcreteFlyweight(char intrinsicState) { this.intrinsicState = intrinsicState; } public override void Operation(string extrinsicstate) { Console.WriteLine("ConcreteFlyweight('" + intrinsicState + "'): \"" + extrinsicstate + "\""); } } public class UnsharedConcreteFlyweight : Flyweight { private IEnumerable<Flyweight> flyweights; public UnsharedConcreteFlyweight(IEnumerable<Flyweight> flyweights) { this.flyweights = flyweights; } public override void Operation(string extrinsicstate) { Console.WriteLine("UnsharedConcreteFlyweight: "); foreach (var flyweight in flyweights) { flyweight.Operation(extrinsicstate); } Console.WriteLine(); } } public class Test { public static void Main() { string extrinsicstate = "THIS IS A BIG EXTRINSIC STATE"; FlyweightFactory factory = new FlyweightFactory(); UnsharedConcreteFlyweight flyweights1 = new UnsharedConcreteFlyweight(new Flyweight[] { factory.GetFlyweight('a'), factory.GetFlyweight('a'), factory.GetFlyweight('b'), factory.GetFlyweight('c'), factory.GetFlyweight('a'), factory.GetFlyweight('c') }); UnsharedConcreteFlyweight flyweights2 = new UnsharedConcreteFlyweight(new Flyweight[] { factory.GetFlyweight('c'), factory.GetFlyweight('b'), factory.GetFlyweight('b'), factory.GetFlyweight('a'), factory.GetFlyweight('c'), factory.GetFlyweight('b') }); flyweights1.Operation(extrinsicstate); flyweights2.Operation(extrinsicstate); Console.ReadKey(); } } /* Output UnsharedConcreteFlyweight: ConcreteFlyweight('a'): "THIS IS A BIG EXTRINSIC STATE" ConcreteFlyweight('a'): "THIS IS A BIG EXTRINSIC STATE" ConcreteFlyweight('b'): "THIS IS A BIG EXTRINSIC STATE" ConcreteFlyweight('c'): "THIS IS A BIG EXTRINSIC STATE" ConcreteFlyweight('a'): "THIS IS A BIG EXTRINSIC STATE" ConcreteFlyweight('c'): "THIS IS A BIG EXTRINSIC STATE" UnsharedConcreteFlyweight: ConcreteFlyweight('c'): "THIS IS A BIG EXTRINSIC STATE" ConcreteFlyweight('b'): "THIS IS A BIG EXTRINSIC STATE" ConcreteFlyweight('b'): "THIS IS A BIG EXTRINSIC STATE" ConcreteFlyweight('a'): "THIS IS A BIG EXTRINSIC STATE" ConcreteFlyweight('c'): "THIS IS A BIG EXTRINSIC STATE" ConcreteFlyweight('b'): "THIS IS A BIG EXTRINSIC STATE" */ کد دنیای واقعی این کد دنیای واقعی نشان می دهد که الگوی Flyweight، که در آن تعداد نسبتا کمی از اشیاء شخصیت( Character ) بارها توسط یک سند که دارای شخصیت های بالقوه زیادی است، به اشتراک گذاشته می شود. using System; using System.Collections.Generic; namespace DoFactory.GangOfFour.Flyweight.RealWorld { /// <summary> /// MainApp startup class for Real-World /// Flyweight Design Pattern. /// </summary> class MainApp { /// <summary> /// Entry point into console application. /// </summary> static void Main () { // Build a document with text string document = "AAZZBBZB"; char[] chars = document.ToCharArray (); CharacterFactory factory = new CharacterFactory (); // extrinsic state int pointSize = 10; // For each character use a flyweight object foreach (char c in chars) { pointSize++; Character character = factory.GetCharacter (c); character.Display (pointSize); } // Wait for user Console.ReadKey (); } } /// <summary> /// The 'FlyweightFactory' class /// </summary> class CharacterFactory { private Dictionary<char, Character> _characters = new Dictionary<char, Character> (); public Character GetCharacter (char key) { // Uses "lazy initialization" Character character = null; if (_characters.ContainsKey (key)) { character = _characters[key]; } else { switch (key) { case 'A': character = new CharacterA (); break; case 'B': character = new CharacterB (); break; //... case 'Z': character = new CharacterZ (); break; } _characters.Add (key, character); } return character; } } /// <summary> /// The 'Flyweight' abstract class /// </summary> abstract class Character { protected char symbol; protected int width; protected int height; protected int ascent; protected int descent; protected int pointSize; public abstract void Display (int pointSize); } /// <summary> /// A 'ConcreteFlyweight' class /// </summary> class CharacterA : Character { // Constructor public CharacterA () { this.symbol = 'A'; this.height = 100; this.width = 120; this.ascent = 70; this.descent = 0; } public override void Display (int pointSize) { this.pointSize = pointSize; Console.WriteLine (this.symbol + " (pointsize " + this.pointSize + ")"); } } /// <summary> /// A 'ConcreteFlyweight' class /// </summary> class CharacterB : Character { // Constructor public CharacterB () { this.symbol = 'B'; this.height = 100; this.width = 140; this.ascent = 72; this.descent = 0; } public override void Display (int pointSize) { this.pointSize = pointSize; Console.WriteLine (this.symbol + " (pointsize " + this.pointSize + ")"); } } // ... C, D, E, etc. /// <summary> /// A 'ConcreteFlyweight' class /// </summary> class CharacterZ : Character { // Constructor public CharacterZ () { this.symbol = 'Z'; this.height = 100; this.width = 100; this.ascent = 68; this.descent = 0; } public override void Display (int pointSize) { this.pointSize = pointSize; Console.WriteLine (this.symbol + " (pointsize " + this.pointSize + ")"); } } } OutputA (pointsize 11)A (pointsize 12)Z (pointsize 13)Z (pointsize 14)B (pointsize 15)B (pointsize 16)Z (pointsize 17)B (pointsize 18)منبع