3.3. Iterator¶
3.3.1. تعریف¶
دیزاین پترن Iterator یک الگوی رفتاری محسوب میشه که به شما اجازه میده به المان های یک مجموعه به صورت متوالی دسترسی پیدا کنید و روی اون ها پیمایش انجام بدید بدون اینکه جزئیات لایه های زیرین نحوه ی پیاده سازی این ساختار برای لایه های سطح بالای برنامه آشکار بشه.
همچنین به راحتی میشه بدون دست زدن به ساختار کد، المان هایی رو به مجموعه اضافه کرد.
3.3.2. چه زمانی استفاده میشه؟¶
این دیزاین پترن معمولا زمانی استفاده میشه که مجموعه یا Collection پیچیده ای داریم که قصد داریم این پیچیدگی رو از منطق اصلی برنامه جدا کنیم.
این الگو به نحوی پیاده سازی میشه که ما امکان پیمایش روی انواع ساختار های داده رو داشته باشیم و نوع این ساختار برای سطوح بالا اهمیتی نداره.
3.3.3. اجزاء¶
الگوی طراحی Iterator از چند بخش اصلی تشکیل میشه:
اولین بخشی که تعریف می کنیم Iterator Interface هست که ساختار iterator های پیمایش کننده ساختار های داده ای رو مشخص می کنه.
بخش بعد هم پیاده سازی های مختلف Iterator ها هست که از ساختار Interface پیروی می کنن و برای پیمایش روی داده ها به کار میرن.
بعد از این Aggregate رو داریم که Interface مربوط به ساختار داده ای که قرار هست روی اون پیمایش انجام بشه رو مشخص می کنه.
و بعد پیاده سازی های Aggregate رو داریم که قرار هست روشون پیمایش انجام بشه و یک متد هم برای دریافت iterator دارن.
در نهایت هم که مثل همیشه Client رو داریم که قراره کلاس های ما رو صدا بزنه و از اون استفاده کنه.
Kostyantyn Kolesnichenko, Public domain, via Wikimedia Commons
Caution
✅ مزایای استفاده
امکان مدیریت روی ترتیب request handling
رعایت اصل Open/Closed از اصول Solid: امکان اضافه کردن انواع جدید Collection ها و iterator ها بدون تغییر Client
رعایت اصلی تک مسئولیتی از اصول SOLID طبق توضیحاتی که داده شد
امکان پیمایش موازی یک Collection به صورت جداگانه
امکان توقف پیمایش و سپس ادامه آن در زمان دلخواه
Warning
❌ معایب استفاده
اعمال این الگو در مواردی که واقعا بهش نیاز نیست و Collection های پیچیده ای وجود نداره بسیار اشتباه هست
3.3.4. کاربرد عملی¶
این الگو یکی از پر کاربرد ترین الگوهای مورد استفاده در PHP و انواع فریمورک های اون هست و در ساختارهای زیادی برای پیمایش و انجام عملیات روی انواع Collection ها مورد استفاده قرار میگیره.
ما یک نمونه Iteration رو در PHP به عنوان مثال در ادامه پیاده سازی می کنیم.
3.3.5. پیاده سازی¶
برای Interface ما از ساختارهای موجود در PHP یعنی Countable و Iterator استفاده می کنیم و نیاز به تعریف Interface اضافه ای نیست.
پس مستقیم میریم سراغ کلاس User
1<?php
2
3class User
4{
5 public function __construct(
6 private readonly string $name,
7 private readonly int $age
8 ) {
9 }
10
11 public function getName(): string
12 {
13 return $this->name;
14 }
15
16 public function getAge(): int
17 {
18 return $this->age;
19 }
20
21 public function getUserInfo(): string
22 {
23 return $this->getName() . ' (' . $this->getAge() . ')';
24 }
25}
بعد هم Collection یا List مربوط به User ها رو داریم که قراره روش پیمایش انجام بشه:
1<?php
2
3class UserList implements Countable, Iterator
4{
5 /**
6 * @var User[]
7 */
8 private array $users = [];
9 private int $currentIndex = 0;
10
11 public function addUser(User $user)
12 {
13 $this->users[] = $user;
14 }
15
16 public function removeUser(User $userToRemove)
17 {
18 foreach ($this->users as $key => $user) {
19 if ($user->getUserInfo() === $userToRemove->getUserInfo()) {
20 unset($this->users[$key]);
21 }
22 }
23
24 $this->users = array_values($this->users);
25 }
26
27 public function count(): int
28 {
29 return count($this->users);
30 }
31
32 public function current(): User
33 {
34 return $this->users[$this->currentIndex];
35 }
36
37 public function key(): int
38 {
39 return $this->currentIndex;
40 }
41
42 public function next()
43 {
44 $this->currentIndex++;
45 }
46
47 public function rewind()
48 {
49 $this->currentIndex = 0;
50 }
51
52 public function valid(): bool
53 {
54 return isset($this->users[$this->currentIndex]);
55 }
56}
3.3.6. نحوه فراخوانی¶
1<?php
2
3// create some users
4$user1 = new User('Saleh', 31);
5$user2 = new User('Ahmad', 28);
6$user3 = new User('Mina', 31);
7
8// create a user list and add the users to it
9$userList = new UserList();
10$userList->addUser($user1);
11$userList->addUser($user2);
12$userList->addUser($user3);
13
14// iterate over the user list and output each user's name
15foreach ($userList as $user) {
16 echo $user->getName() . PHP_EOL;
17}
18
19// remove a user from the list
20$userList->removeUser($user2);
21
22// iterate over the user list again, this time only outputting the age
23foreach ($userList as $user) {
24 echo $user->getAge() . PHP_EOL;
25}
26
27// output the total number of users in the list
28echo 'Total number of users: ' . count($userList) . PHP_EOL;
به همین زیبایی!