LoginSignup
3
1

More than 3 years have passed since last update.

デザインパターンをkotlinで書いてみた Iterator編

Last updated at Posted at 2019-08-17

オブジェクト指向で大切になるInterfaceの考え方やオブジェクトの再利用性を学ぶために「Java言語で学ぶデザインパターン入門」について学び、Javaとついでにkotlinで書いてみることにしました。
最初はIteratorについてまとめます。

※また、今回コメント欄にレビューをいただきました@sdkeiさんありがとうございます。
レビューを元に修正しました内容を反映させましたので、その点も踏まえて書いていこうと思います。

Iteratorとは

List等の集合の要素を最初から順番にスキャンするパターンで、日本語だと「反復子」という意味。

Aggregateインターフェース

数え上げる事のできる集合体という役割をAggreagateインターフェースで持たせます。
インターフェースは継承ではなく、役割を持たせて強制的にメソッドを実装させる役割があります。

Aggregate.java
interface Aggregate {
    public abstract Iterator iterator(); 
}
Aggregate.kt
interface Aggregate {
    fun iterator(): Iterator
}

Iteratorインターフェース

要素を数え上げる役割を果たすので、要素操作するメソッドを持たせるインターフェース
ここでnextメソッドの動きが大切で、要素を取得する、次の要素を取得できるようにindexを進める処理が必要。
また、こちらのIteratorインターフェースについてレビューをいただきましてJavaでいうObject型はKotlinでいうAny?に相当するとのことでした。
AnyとAny?の違いはNullを許容するかどうかの違いになり、APIでは様々な実装ができるようNullを許容するAny?に修正しました。

Iterator.java
interface Iterator {
    public abstract boolean hasNext();
    public abstract Object next();
}
Iterator.kt
interface Iterator {
    fun hasNext(): Boolean
    fun next(): Any?
}

Memberクラス

要素として取得されるクラスを作成。
kotlinだとgetter/setterを実装しなくてもmember.nameで取得できます。

Member.java
class Member {
    private String name;
    public Member(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}
Member.kt
class Member(val name: String)

Staffクラス

Aggregateの実装としてStaffクラスを作成し、StaffクラスをMemberの集合体とします。
インターフェースで定義したIteratorメソッドを実装します。

Staff.java
class Staff implements Aggregate{
    private Member[] members;
    private int last;

    public Staff(int maxsize) {
        this.members = new Member[maxsize];
    }

    public Member getMemberAt(int index) {
        return members[index];
    }
    public void appendMember(Member member) {
        this.members[last] = member;
        last++;
    }
    public int getLength() {
        return last;
    }
    public Iterator iterator() {
        return new StaffIterator(this);
    }
}
Staff.kt
class Staff internal constructor(private val maxSize: Int): Aggregate {
    private val members: Array<Member?> = arrayOfNulls(maxSize)
    private var last: Int = 0

    fun getMemberAt(index: Int): Member? = members[index]

    fun appendMember(member: Member) {
        members[this.last] = member
        this.last++
    }

    fun getLength():Int = this.last

    override fun iterator(): Iterator = StaffIterator(this)
}

StaffIteratorクラス

Iteraltorの実装としてStaffIteratorクラスを作成し、Staffをスキャンできるようにメソッドを実装します。
nextメソッドでは要素の取得と、indexを次に進める実装をしており、For構文でいうi++の役目を持たせます。
また、IteratorインターフェースのnextメソッドハAny?を使用してNullを許容しているのですが、memberオブジェクトをnullで返すのは意図していないのでNon-NullであるAnyを戻り値としました。

StaffIterator.java
class StaffIterator implements Iterator{
    private Staff staff;
    private int index;
    public StaffIterator(Staff staff) {
        this.staff = staff;
        this.index = 0;
    }
    public boolean hasNext() {
        if(index < staff.getLength()) {
            return true;
        }else {
            return false;           
        }
    }
    public Object next() {
        Member member = staff.getMemberAt(index);
        index++;
        return member;
    }
}
StaffIterator.kt
class StaffIterator(private val staff: Staff): Iterator {
    private var index: Int = 0
    override fun hasNext(): Boolean = index < staff.getLength()

    override fun next(): Any {
        val member: Member = staff.getMemberAt(index) as Member
        index++
        return member
    }
}

Mainクラス

上記クラスを実際に動作させるMainクラスです。
ここでのポイントはwhile文にStaffの実装が干渉しない事がメリット。

IteratorSample.java
public class IteratorSample {
    public static void main(String[] args) {
        Staff staff = new Staff(3);
        staff.appendMember(new Member("sato"));
        staff.appendMember(new Member("suzuki"));
        staff.appendMember(new Member("saito"));
        Iterator it = staff.iterator();
        while(it.hasNext()) {
            Member member = (Member)it.next();
            System.out.println(member.getName());
        }
    }
}
IteratorSample.kt
fun main(args: Array<String>){
    val staff = Staff(3).apply {
        appendMember(Member("sato"))
        appendMember(Member("suzuki"))
        appendMember(Member("saito"))
    }
    val it: Iterator = staff.iterator()
    while (it.hasNext()) {
        val member: Member = it.next() as Member
        println(member.name)
    }
}

実行結果
sato
suzuki
saito

クラス図

image.png

所感

  • while文の処理ではサブクラスの実装に依存しないので、実装したメソッド内で変更があった場合でもwhile文の処理に影響を与えない事を学んだ。
  • kotlinだと、コードの量をかなり抑えられて見やすい
  • Iteratorを継承したサブクラスをInterfaceのインスタンスにオブジェクトをつめれるのが何気に初めて知りました。
  • kotlinだとnullとnullableの扱いがまだまだできていなさそうなので、コメントやご指摘をいただけると助かります。

問題1-1

Staffクラスの配列をVectorに変更。
実際に実装してみた結果、変更による影響範囲がStaffクラス内に収まることを確認でき、依存性の低さの大切さを学んだ。

package practice.design_pattern_java;

import java.util.Vector;

interface Aggregate {
    public abstract Iterator iterator(); 
}

interface Iterator {
    public abstract boolean hasNext();
    public abstract Object next();
}

class Member {
    private String name;
    public Member(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

class StaffIterator implements Iterator{
    private Staff staff;
    private int index;
    public StaffIterator(Staff staff) {
        this.staff = staff;
        this.index = 0;
    }
    public boolean hasNext() {
        if(index < staff.getLength()) {
            return true;
        }else {
            return false;           
        }
    }
    public Object next() {
        Member member = staff.getMemberAt(index);
        index++;
        return member;
    }
}

class Staff implements Aggregate{
    private Vector<Member> members = new Vector<Member>();
    private int last;

    public Member getMemberAt(int index) {
        return members.get(index);
    }
    public void appendMember(Member member) {
        this.members.addElement(member);;
        last++;
    }
    public int getLength() {
        return last;
    }
    public Iterator iterator() {
        return new StaffIterator(this);
    }
}

public class IteratorVectorSample {
    public static void main(String[] args) {
        Staff staff = new Staff();
        staff.appendMember(new Member("sato"));
        staff.appendMember(new Member("suzuki"));
        staff.appendMember(new Member("saito"));
        Iterator it = staff.iterator();
        while(it.hasNext()) {
            Member member = (Member)it.next();
            System.out.println(member.getName());
        }
    }
}

参考

下記を参考にさせて頂き、大変読みやすく、理解しやすかったです。

JavaプログラマのためのKotlin入門

KotlinのAnyについては下記を参考にいたしました。
JavaプログラマがKotlinでつまづきがちなところ

3
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1