2.7. Flyweight¶
2.7.1. تعریف¶
دیزاین پترن Flyweight که یک الگوی ساختاری محسوب می شود در شرایطی استفاده می شود که قصد داشته باشیم میزان مصرف حافظه رم را بهینه سازی کنیم.
اما چطور؟ به جای نگهداری تمام داده ها در هر آبجکت، بخش های مشترک آبجکت ها را بین آبجکت های مورد نظر به اشتراک میذاریم.
2.7.2. اجزاء¶
یک دیزاین پترن Flyweight از چند بخشی اصلی تشکیل میشه.
ابتدا بررسی می کنیم که قسمت های مشترک چه مواردی هستن و با توجه به این موضوع خود Flyweight مشخص میشه که شامل بخشی هست که مشخص می کنه متدها و پراپرتی های مشترک چه مواردی هستن. و در نهایت بخش Client، از اون برای دسترسی به قسمت های مشترک استفاده میکنه.
بعد از اون پیاده سازی های Flyweight رو داریم که در واقع همون آبجکت پیاده سازی شده ی بخش های مشترک هست و اینترفیس Flyweight رو پیاده سازی می کنه.
بخش بعد Flyweight Factory هست که کارش مدیریت ایجاد و به اشتراک گذاری آبجکت های Flyweight هست. یک pool شامل آبجکت های Flyweight موجود رو نگهداری می کنه و آبجکت رو اگر موجود باشه برمیگردونه و اگر نباشه ایجاد می کنه.
و در نهایت هم بخش Client رو داریم که در مثال عملی خواهید دید کارش چیه.
Vanderjoe, CC BY-SA 4.0, via Wikimedia Commons
2.7.3. چه زمانی استفاده میشه؟¶
این الگو معمولا در طراحی بازی های کامپیوتری برای کاهش رم مصرفی بسیار استفاده میشه و در نهایت کاری که انجام میده همیشه کاهش میزان رم مصرفی هست.
توصیه میشه که این الگو رو فقط زمانی استفاده کنید که برنامه شما مشکلات مربوط به مصرف بالای رم رو داشته باشه، برنامه هایی که تعداد بسیار زیادی آبجکت با اطلاعات داده ای مشترک دارن و هر کدوم بخشی از رم رو اشغال می کنن.
Caution
✅ مزایای استفاده
بهینه سازی بالای مصرف رم برنامه زمانی که تعداد آبجکت های مشابه زیادی در برنامه وجود داره.
Warning
❌ معایب استفاده
ممکنه با کاهش رم مصرفی به علت اجبار در محاسبه ی مجدد برخی موارد، مصرف CPU افزایش پیدا کنه!
کد بسیار پیچیده میشه و معمولا خواناییش کاهش پیدا می کنه!
2.7.4. کاربرد عملی¶
این الگو در برنامه نویسی با PHP بسیار بسیار کم کاربرد هست اما برای اینکه ساختارش رو درک کنید سعی می کنم یه مثال براش پیدا کنم!
قطعا کسی با PHP بازی طراحی نمی کنه ولی خب این ملموس ترین مثالیه که میشه داشت.
فرض کنید در حال کدنویسی یک بازی کامپیوتری هستیم که تعداد خیلی زیادی کاراکتر داره که هر کدوم هم توانایی ها و ظاهر مخصوص به خودشون رو دارن.
با این حال جنبه های ظاهری زیادی از این کاراکتر ها مشابه هست و میشه اون ها رو به صورت مشترک نگهداری کرد.
2.7.5. پیاده سازی¶
خب ابتدا خود Flyweight رو داریم:
1<?php
2
3/**
4 * The Flyweight interface declares a method for accepting extrinsic state.
5 */
6interface Character
7{
8 public function render(string $name): void;
9}
بعد نوبت به Factory میرسه که به این صورت تعریف میشه:
1<?php
2
3/**
4 * The Flyweight Factory manages a pool of shared flyweight objects. It
5 * ensures that flyweights are shared correctly. When the client requests a
6 * flyweight, the factory either returns an existing instance or creates a new
7 * one, if it doesn't exist yet.
8 */
9class CharacterFactory
10{
11 private array $characters = [];
12
13 public function getSharedCharacter($height, $weight, $hairColor)
14 {
15 $key = "$height-$weight-$hairColor";
16
17 if (!isset($this->characters[$key])) {
18 echo "Creating shared character '$key'\n";
19 $this->characters[$key] = new SharedCharacter($height, $weight, $hairColor);
20 }
21
22 return $this->characters[$key];
23 }
24}
که بالاتر در مورد نحوه ی عملکردش توضیح دادیم.
و حالا CharacterClient رو داریم:
1<?php
2
3/**
4 * The Client represents a character in the game.
5 */
6class CharacterClient
7{
8 private string $name;
9 private $sharedCharacter;
10
11 public function __construct(string $name, CharacterFactory $factory, $height, $weight, $hairColor)
12 {
13 $this->name = $name;
14 $this->sharedCharacter = $factory->getSharedCharacter($height, $weight, $hairColor);
15 }
16
17 public function render(): void
18 {
19 $this->sharedCharacter->render($this->name);
20 }
21}
2.7.6. نحوه فراخوانی¶
1<?php
2
3// Client code
4$factory = new CharacterFactory();
5
6$player1 = new CharacterClient('Player 1', $factory, 180, 75, 'blonde');
7$player1->render(); // Drawing character 'Player 1' with height '180', weight '75', and hair color 'blonde'
8
9$player2 = new CharacterClient('Player 2', $factory, 180, 75, 'blonde');
10$player2->render(); // Drawing character 'Player 2' with height '180', weight '75', and hair color 'blonde'
11
12$player3 = new CharacterClient('Player 3', $factory, 190, 80, 'red');
13$player3->render(); // Drawing character 'Player 3' with height '190', weight '80', and hair color 'red'
اینجا factory برای ما فقط دو آبجکت کاراکتر میسازه و موارد مشترک رو مجدد نمیسازه.