オブジェクト指向で大切になるInterfaceの考え方やオブジェクトの再利用性を学ぶために「Java言語で学ぶデザインパターン入門」について学び、Javaとついでにkotlinで書いてみることにしました。
最初はIteratorについてまとめます。
※また、今回コメント欄にレビューをいただきました@sdkeiさんありがとうございます。
レビューを元に修正しました内容を反映させましたので、その点も踏まえて書いていこうと思います。
##Iteratorとは
List等の集合の要素を最初から順番にスキャンするパターンで、日本語だと「反復子」という意味。
##Aggregateインターフェース
数え上げる事のできる集合体という役割をAggreagateインターフェースで持たせます。
インターフェースは継承ではなく、役割を持たせて強制的にメソッドを実装させる役割があります。
interface Aggregate {
public abstract Iterator iterator();
}
interface Aggregate {
fun iterator(): Iterator
}
##Iteratorインターフェース
要素を数え上げる役割を果たすので、要素操作するメソッドを持たせるインターフェース
ここでnextメソッドの動きが大切で、要素を取得する、次の要素を取得できるようにindexを進める処理が必要。
また、こちらのIteratorインターフェースについてレビューをいただきましてJavaでいうObject型はKotlinでいうAny?に相当するとのことでした。
AnyとAny?の違いはNullを許容するかどうかの違いになり、APIでは様々な実装ができるようNullを許容するAny?に修正しました。
interface Iterator {
public abstract boolean hasNext();
public abstract Object next();
}
interface Iterator {
fun hasNext(): Boolean
fun next(): Any?
}
##Memberクラス
要素として取得されるクラスを作成。
kotlinだとgetter/setterを実装しなくてもmember.nameで取得できます。
class Member {
private String name;
public Member(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Member(val name: String)
##Staffクラス
Aggregateの実装としてStaffクラスを作成し、StaffクラスをMemberの集合体とします。
インターフェースで定義したIteratorメソッドを実装します。
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);
}
}
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を戻り値としました。
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 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の実装が干渉しない事がメリット。
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());
}
}
}
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
##所感
- 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());
}
}
}
##参考
下記を参考にさせて頂き、大変読みやすく、理解しやすかったです。
KotlinのAnyについては下記を参考にいたしました。
JavaプログラマがKotlinでつまづきがちなところ