Идеята на този design pattern е да даде начин ефективно откъм памет да нямаме огромен брой близки обекти, на които само част от информацията им е специфичнасто казано, трябва ти обект, подобен на някой вече съществуващ но с някои разлики, вместо да създаваш нов – вземаш съществуващият и му задаваш специфичните за случая неща и си го използваш.
Например, ако си представим уеб страница с хиляди картинки (<img> тагове), които обаче са със един и същ файл (src атрибута) или са няколко с един си същ src, и само други техни свойства (width, height …) се различават. Не е ефективно за всеки да имаме по един обект, при условие, че можем да имаме само по един на база src атрибут (файла), а останалите да са само същият този обект но с различни пропъртита.
Или текстов редактор с много символни знака, които можем да избираме, и всеки е отделен обект но се различават само по самият символ.
Един начин би бил естествено да създаваме нов обект всеки път когато логиката го изисква. Но какво ще стане с паметта, която приложението използва ако например имаме документ с стотици хиляди символа и всеки символ е един обект?
Първо нека логически да разделим общите от частните свойства на тези много, близки обекти, тоест да видим по какво се повтарят.
Ще наричаме общите свойства intrinsic (или invariant) – вродени (непроменящи се), а специфичните – extrinsic (или variant) – тоест привнесени отвън, динамични.
В нашите примери intrinsic ще е src атрибутът на img обектите, защото URL-ът/файлът ще използваме като уникален ключ в data структура, от която ще го вземаме наготово за да го „напаснем“ според случая, вместо да създаваме нов.
А extrinsic ще са например отделните свойства на img обекта, които ще подаваме на вече съществуващият, вместо да създаваме нов.
В примера с текстовият редактор, ще имаме по един обект за всеки символ, intrinsic ще е кодът му, а extrinsic ще са такива като ред, колона, шрифт, цвят и т.н…, неща свързани с конкретната позиция на символа в текста, и които ще подаваме отвън.
Ще имаме един абсклас Flyweight, в който ще са intrinsic свойствата, и два наследяващи го класа – ConcreteFlyweight и UnsharedFlyweight.
Клиентите използват инстанция на ConcreteFlyweight и само подават extrinsic свойствата динамично (run time) според конкретната логика, а не ги използват за да създават нови обекти.
Тоест, ще имаме един обект, просто ще го променяме според нуждите, вместо да създаваме нови.
Друг добър пример би бил ако например искаме да имаме обект за всеки запис от база данни. Ако са милиони? Милиони обекти от дадения клас?
Или просто един обект, на който променяме стойностите на пропъртитата, които кореспондират с полетата на резултата на SQL заявката.
Няма да минем и без „factory“, което ще следи дали вече има такъв създаден или не, и ако не – създава го и го добавя по даден уникален ключ в някаква data структура, която ще ни е container от обекти.
To ensure that Flyweight objects are shared properly, clients must obtain flyweights solely from the flyweight factory (getFlyweight(key)) that maintains a pool of shared Flyweight objects. This greatly reduces the number of physically created objects.
В това отношение Flyweight design pattern силно прилича на Singleton design pattern.
Като всяка design pattern и Flyweight трябва да се използва разумно, в смисъл такъв, в конкретни случаи. Прилагайте Flyweight само когато всички тези условия са налице:
- приложението използва огромен брой близки като стуктура обекти, на които само някои от пропъртитата се променят, от там и storage cost (паметта например) скача силно;
- Most object state can be made extrinsic;
- имаме много обекти, които са много близки и имат свойства и могат да бъдат заменени с един, съдържащ общите такива, а различните – да му се подават отвън;
- The application doesn’t depend on object identity. Since flyweight objects may be shared, identity tests will return true for conceptually distinct objects.
Литература: