デザインパターンを学習しよう【1. Iteratorパターン-2】

株式会社オズビジョンのユッコ (@terra_yucco) です。
デザインパターンの 2 記事目の続きになります。実際のデザインパターンの説明を見ていきます。

# 教材
[変わらず](https://www.techscore.com/tech/DesignPattern/index.html/)です。

Iterator パターンの前回は[こちらの記事](https://qiita.com/terra_yucco/items/cd430cecaa0f03577b58)で。

## [1. Iteratorパターン](https://www.techscore.com/tech/DesignPattern/Iterator/Iterator1.html/)
### 1.1 Itaratorパターンとは
### 1.2 サンプルケース
### [1.2a （実習課題1）](https://www.techscore.com/tech/DesignPattern/Iterator/Iterator2.html/)

>「Teacher」抽象クラスを継承する MyTeacher クラスと、「StudentList」クラスを拡張した MyStudentList クラスを設計しクラス図を作成しなさい。また、これらのクラスを実装し、 下記のMain クラスを実行しなさい。この２つのクラス以外のクラスやインタフェースを自由に追加しても問題ありません。 MyTeacher クラスの callStudents メソッドでは、あなたのクラスの生徒を順番に標準出力に出力するようにしなさい。

(略)

#### 追加課題

>このメソッドを利用して、 毎朝出席を取ることにも慣れてきました。ところが学校側から「名簿が古くなったので、 新しい形式のものに変えようと思います。新しい名簿は、java.util.Vector クラスを利用したものです。」 との通達があり、新しい名簿が渡されました。新しい名簿は、以下のように与えられます。

おっと java.util...。読み替えはできるのだろうか。

```java

import java.util.Vector;
 
public class NewStudentList{
    protected Vector<Student> students;
    public void add(Student student){
        students.add(student);
    }
    public Student getStudentAd(int index){
        return students.get(index);
    }
}
```

Vector と思ったら、PHP にも実はあるらしいです。
[PHP マニュアル > 関数リファレンス > その他の基本モジュール > Data Structures > The Vector class](https://www.php.net/manual/ja/class.ds-vector.php)
ただし `PECL` なので今回は見送ります。

PHP で実装するならこんな感じでしょうか。
StudentList とあまり変わらない :expressionless: (今度 Vector 使ってみよう)
と、とりあえず、大事なのは、元と違うクラスになっている、ということです。

```php:NewStudentList.php
class NewStudentList
{
    protected $students;

    /**
     * @param Student $student
     */
    public function add($student)
    {
        $this->students[] = $student;
    }

    /**
     * @param int $index
     * @return Student
     */
    public function getStudentAt($index)
    {
        return $this->students[$index];
    }
}
```

こういう風に、リストのクラスがどんどん変わってしまう場合に使うべきなのが Iterator パターン。

##### クラス図

クラス図は描けないので、教材からお借りしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95030/1ff96356-5a8e-6f96-73b1-17a8cf0a0552.png)

##### Interfaces

- [PHP マニュアル > 言語リファレンス > クラスとオブジェクト > オブジェクト インターフェイス](https://php.net/manual/ja/language.oop5.interfaces.php)

```php:Aggregate.php
<?php
interface Aggregate {
    public Iterator iterator();
}
```

PHP には組み込みで `Iterator` が存在していました。

- [PHP マニュアル > 言語リファレンス > 定義済みのインターフェイスとクラス > Iterator インターフェイス](https://www.php.net/manual/ja/class.iterator.php)

※教材通りに進めるとそれと名前が重複してしまうため、クラス図の `Iterator` にあたる部分は `MyIterator` としています。

```php:MyIterator.php
<?php
interface MyIterator {
  public function hasNext();
  public function next();
}
```

##### 今回のケースに基づいて具体的な構成を検討

このクラス図も教材に載っていたものです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/95030/a2429b01-a03a-ae47-8819-ec03cf3ac3ee.png)

上記の Interface を実装して、今回のケースに必要な　ConcreteAggregate と ConcreteIterator を実装する必要がある。

- ConcreteAggregate は StudentList (を拡張した MyStudentList) が該当
- ConcreteIterator は今までの中に登場しないが、対応させたクラスを作る必要がある

### [1.2b 実習課題 2](https://www.techscore.com/tech/DesignPattern/Iterator/Iterator3.html/)

>前ページの最後のクラス図にあわせて、 MyStudentList クラスと、MyStudentListIterator クラスを実装しなさい。 ただし、MyStudentList クラスは、以前の古い名簿 StudentList を拡張したものにすること。

### [1.2c 実習課題 3](https://www.techscore.com/tech/DesignPattern/Iterator/Iterator4.html/)

>java.util.Vector クラスを利用した新しい名簿に変更後に必要となる、NewVeteranStudentList クラスと NewVeteranStudentListIterator クラスを実装し、クラス図を作成しなさい。また、これに伴い VeteranTeacher クラスを変更しなさい。

ああでもないこうでもないとやっていたら、新しく作った Interface 類を使って修正したコードで MyTeacher が仕事をできるようになったので、実習課題の 2 と 3 をまとめてやった形になりました。
課題と言いつつ途中まではヘルプも入るので、どこまでが課題かわかりにくい。

とりあえず、最終的なコードは以下のようになっています。

```php:Main.php
<?php
require_once './StudentList.php';
require_once './Aggregate.php';
require_once './MyStudentIterator.php';
class MyStudentList extends StudentList implements Aggregate
{
    public function __construct()
    {
        parent::__construct();
    }

    public function iterator($list)
    {
        return new MyStudentIterator($list);
    }
}
[root@localhost ~]# cat Main.php
<?php
require_once './MyTeacher.php';

class Main
{
    public function work1()
    {
        $you = new MyTeacher();
        $you->createStudentList();
        $you->callStudents();
    }
}

$main = new Main();
$main->work1();
```

```php:MyTeacher.php
<?php
require_once './Teacher.php';
require_once './Student.php';
require_once './MyStudentList.php';

class MyTeacher extends Teacher
{
    public function createStudentList()
    {
        $this->studentList = new MyStudentList();
        $this->studentList->addStudent(new Student('赤井亮太', '男'));
        $this->studentList->addStudent(new Student('赤羽里美', '女'));
        $this->studentList->addStudent(new Student('岡田美央', '女'));
        $this->studentList->addStudent(new Student('西森俊介', '男'));
        $this->studentList->addStudent(new Student('中ノ森玲菜', '女'));
    }

    public function callStudents()
    {
        $itr = $this->studentList->iterator($this->studentList);
        while ($itr->hasNext()) {
            $student = $itr->next();
            echo $student->getName() . $this->getTitle($student->getSex()) . PHP_EOL;
        }
    }

    private function getTitle($sex)
    {
        return $sex === '男' ? 'くん' : 'さん';
    }
}
```

```php:Teacher.php
<?php
abstract class Teacher
{
    protected $studentList;

    abstract public function createStudentList();
    abstract public function callStudents();
}
```

```php:MyIterator.php
<?php
interface MyIterator
{
  public function hasNext();
  public function next();
}
```

```php:MyStudentIterator.php
{
    private $myStudentList;

    private $index;

    public function __construct($list)
    {
        $this->myStudentList = $list;
        $this->index = 0;
    }

    public function hasNext()
    {
        return ($this->myStudentList->getLastNum() > $this->index);
    }

    public function next()
    {
        $student = $this->myStudentList->getStudentAt($this->index);
        $this->index++;
        return $student;
    }
}
```

```php:Aggregate.php
<?php
interface Aggregate {
    public function iterator($list);
}
```

```php:MyStudentList.php
<?php
require_once './StudentList.php';
require_once './Aggregate.php';
require_once './MyStudentIterator.php';
class MyStudentList extends StudentList implements Aggregate
{
    public function __construct()
    {
        parent::__construct();
    }

    public function iterator($list)
    {
        return new MyStudentIterator($list);
    }
}
```

```php:StudentList.php
<?php
class StudentList
{
    /**
     * @var array
     */
    protected $students;

    /**
     * @var int
     */
    private $last = 0;

    /**
     * @param int
     */
    public function __construct() {
        $this->students = array();
    }

    /**
     * @param Student $student
     */
    public function addStudent($student)
    {
        $this->students[$this->last] = $student;
        $this->last++;
    }

    /**
     * @param int $index
     * @return Student
     */
    public function getStudentAt($index)
    {
        return $this->students[$index];
    }

    /**
     * @return int
     */
    public function getLastNum()
    {
        return $this->last;
    }
}
```

```php:Student.php
<?php
class Student
{
    /**
     * @var string
     */
    private $name;

    /**
     * @var int
     */
    private $sex; // 0:男, 1:女

    /**
     * @param string $name
     * @param int    $sex
     */
    public function __construct($name, $sex)
    {
        $this->name = $name;
        $this->sex = $sex;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @return int
     */
    public function getSex()
    {
        return $this->sex;
    }
}
```

## Conclusion

サイトの内容自体をまだ理解できていないところもあり、本当にこれで　Iterator になっているのはまだ自信がありません。次回、このコードを元に、集約体が変わっても大丈夫かを確認するような取り組みをして、自分の Iterator パターンの学習を一度終わろうと思います。