四畳半の秘密基地

さあ、今日はどんな実験をしよう

MENU

Kotlinとスプラトゥーンで学ぶデザインパターン【5. Singleton】

最近チャージャーに限界を感じてきました。特にガチホコだと思わぬ奇襲を受けやすいです。
奇襲に対処できるチャージャーになりたい。


Singleton パターンとは

Singletonとは対象のクラスが絶対に1つしか存在しないことを保証するデザインパターンです。
今回は、ダウニーの上位互換ことスパイキーをSingletonパターンで表現してみました。
スパイキーはゲソタウンで指定したギアの受け取りをしてくれます。
当たり前ですが、スパイキーは一人(匹?)しかいないです。
複数いたらどのスパイキーからギアを受け取ればいいかわからなくなりますね。


実装

Spiky.kt

class Spiky {

    private constructor() {}

    var delivery: Delivery? = null

    companion object {
        val spiky = Spiky()
        fun getInstance(): Spiky {
            return spiky
        }
    }

    fun receiveDelivery(delivery: Delivery) {
        this.delivery = delivery
    }

    fun requestReceiveOrder() {
        delivery?.let { println(it.name + "が届いてるぜ!") }
                ?: println("そんなものはない")
    }
}

まずはスパイキークラス。コンストラクタをプライベートにして外部からインスタンス化できないようにして、
インスタンスはクラス内で生成しgetInstanceで渡すようにしてます。
あとは配達物が届いてるか判定して文字を出力します。セリフは雑です。


Delivery.kt

data class Delivery(val id: Int, val name: String)

配達物のデータクラスです。良い英語が思いつかなかった・・・


Main.kt

val spiky = Spiky.getInstance()
spiky.requestReceiveOrder()

spiky.receiveDelivery(Delivery(1, "ガチガサネ"))

val spiky2 = Spiky.getInstance()
spiky2.requestReceiveOrder()

スパイキーを2回生成するようにして同じインスタンスを参照できているか確認します。
インスタンスであればspikey2には配達物が届いてないことになります。


実行結果

そんなものはない
ガチガサネが届いてるぜ!

2回目のリクエストで配達物が届いていたので中のインスタンスは同じようです。


感想

配達物の判定を最初、null判定で表示してたのですが、letを使用したら括弧のdeliveryがnullでないことを保証できたので
letを使いました。赤べこ本で知識として知ってはいたものの使いどころがわかってないですね。
IDEパイセンが気づかせてくれました。ありがとうIntelliJ IDEA!

Kotlinとスプラトゥーンで学ぶデザインパターン【6. Prototype】

久しぶりの更新になってしまいました。
スプラトゥーン2が発売されたし仕方ないよね。ちまちま空いてる時間にプレイしてます。
↓今の状況です。

今回はPrototypeパターンです。Singletonパターンをすでにやってるつもりになってとばしてしまった・・・

Prototype パターンとは

インスタンスを作る時、通常は作りたいクラスをnewして作成しますが、
そうではなく既存のインスタンスをコピーして作る。それをPrototypeパターンと呼ぶらしいです。
なぜそのような実装をするかというと

などの理由が挙げられます。

実装

スプラトゥーンからは離れてしまいますが、セーブデータを題材にしてみました。
セーブデータの中にはいろいろな情報が入っており、クラスからインスタンス化するには難しいというか面倒くさいです。
そこでprototypeパターンを使って実装してみます。

SaveData.kt

class SaveData(val user: User, val games: Array<String>) : Cloneable {
    fun createClone(): SaveData {
        return clone() as SaveData
    }

    fun print() {
        println("id:" + user.id)
        println("name:" + user.name)
        println("registered game datas:")
        games.forEach { println(it) }
    }
}

SaveDataManager.kt

class SaveDataManager {
    fun create(user: User, games: Array<String>): SaveData {
        return SaveData(user, games)
    }

    fun copy(data: SaveData):SaveData {
        return data.createClone()
    }
}

SaveDataにCloneableインターフェースを実装してクローンできるようにしてます。これが今回のキモですね。
cloneは外部に見えないので別途メソッドを作って対応しました。

Main.kt

fun main(args: Array<String>) {
    val user = User(123, "seito")
    var games:Array<String> = arrayOf("Splatoon2", "Mario Kart")
    val manager: SaveDataManager = SaveDataManager()

    val newSaveData = manager.create(user, games)
    newSaveData.print()

    val copiedSaveData = manager.copy(newSaveData)
    copiedSaveData.print()
}

一度セーブデータを作ってそれをコピーして同じ出力結果になるよねーって確認をしています。

実行結果

id:123
name:seito
registered game datas:
Splatoon2
Mario Kart
id:123
name:seito
registered game datas:
Splatoon2
Mario Kart

感想

デザパタ本を読みながら作ったらManagerクラスができたんですが、今回の場合Manager内で扱うクラスは1種類なのでManagerクラスを作らないでMainから直接createCloneを呼んだほうがすっきりするだろうなーと思ったり。
スケーラビリティを考えるとManagerクラスがあってもいいけどスケールする可能性あるのか。

Kotlinとスプラトゥーンで学ぶデザインパターン【4. Factory Method】

昨日前夜祭で久しぶりのスプラトゥーンを満喫してました。
エイムも操作もひどいことになってました。
しばらくはリハビリ期間になりそうです・・・

Factory Methodパターンとは

Factory Methodパターンは、インスタンスの作り方をスーパークラスで定め、実際にインスタンス化する処理はサブクラスに分けて実装するパターンです。
今回は、サーモンランの敵を題材にしました。登場から攻撃までの処理を実装します。

実装

まず枠組みとなるスーパークラスを2つ定義します。
Factory.kt

abstract class Factory {
    fun create() : Enemy {
        var enemy = createEnemy()
        deployToMap(enemy)
        return enemy
    }

    abstract fun createEnemy() : Enemy
    abstract fun deployToMap(enemy: Enemy)
}

Enemy.kt

abstract class Enemy {
    abstract fun attack()
    abstract fun move()
    abstract fun appear()
}

Factoryでは生成してからマップに配置するまでの処理を行なっています。
次に具象クラスです。バクダンという敵をインタンス化します。

BakudanFactory.kt

class BakudanFactory : Factory() {
    override fun createEnemy(): Enemy {
        return Bakudan()
    }

    override fun deployToMap(enemy: Enemy) {
        enemy.appear()
    }

}

Bakudan.kt

class Bakudan : Enemy() {
    override fun attack() {
        println("爆弾を発射!")
    }

    override fun move() {
        println("ちょっと移動")
    }

    override fun appear() {
        println("バクダンが現れた!")
    }

}

実行結果

バクダンが現れた!
ちょっと移動
爆弾を発射!

感想

参考書の例では1製品1工場で作られていたのですが、複数製品があったらどうするのか疑問です。スプラトゥーンのサーモンランではオオモノシャケという敵が複数種類います。今回はバクダン専用のFactoryクラスを作りましたが、コウモリやモグラなどを追加する場合は別でFactoryクラスを作った方がいいのかなぁ。個人的にはEnemyFactoryを作ってそこに全ての敵のインスタンス化を押し込んでしまいたい気持ちになります。謎は深まるばかり・・・

Kotlinとスプラトゥーンで学ぶデザインパターン【3. Template Method】

スプラトゥーン2発売日まで1ヶ月を切りました。
E3での大会、日本チームは惜しくも2位でしたが熱い試合でした。
今回はTemplate Methodパターンです。

Template Methodパターンとは

Template Methodパターンは、親クラスで処理の枠組みを決め、子クラスで具体的な処理を記述するデザインパターンです。
スプラトゥーンでこのデザインパターンが適用できるところがなかなか見つからなかったのですが
試合結果画面がナワバリとガチで違うことに気づき今回は試合結果画面を題材にすることにしました。(前提として勝った時しか考慮していません)

実装

まずナワバリバトルとガチバトルの違いを見極めます。

ナワバリバトル

  • 左上のポイントは塗った面積、
  • チームのリザルトにはランク、武器、名前、塗った面積

<ガチバトル>

  • 左上のポイントはランクに応じてランクに応じて取得できるポイント
  • チームのリザルトにはランク、武器、名前、腕前

が各々表示されます。
これらのことを考慮して実装するとイカのようになりました。

GameResult.kt

abstract class GameResult {
    abstract fun showScore(player: Player)
    abstract fun showTeamResult(players: Array<Player>)

    fun show(players: Array<Player>) {

        //一人目のプレイヤーが自分自身
        showScore(players[0])

        showTeamResult(players)
    }
}

スコア表示、チーム表示メソッドを抽象化して具象クラスに任せています。

Player.kt

data class Player(val name: String, val rank: Int, val udemae: String, val score:Int, val wepon:String)

データクラス。

NawabariBattleResult.kt

class NawabariBattleResult: GameResult() {
    override fun showScore(player: Player) {
        println("" + player.score + "ポイント獲得しました!")
    }

    override fun showTeamResult(players: Array<Player>) {
        for (player in players) {
            println("" + player.rank + ", "  + player.wepon + ", " + player.name + ", " + player.score)
        }
    }

}

GachiBattleResult.kt

class GachiBattleResult : GameResult() {
    override fun showScore(player: Player) {
        println("" + getScoreByUdemae(player.udemae) + "ポイント獲得しました!")
    }

    override fun showTeamResult(players: Array<Player>) {
        for (player in players) {
            println(player.wepon + ", " + player.name + ", " + player.rank)
        }
    }

    //ノックアウトは考慮しない
    private fun getScoreByUdemae(udemae: String): Int {
        when (udemae) {
            "C-" -> return 1000
            "C" -> return 1300
            "C+" -> return 1600
            "B-" -> return 2000
            "B" -> return 2300
            "B+" -> return 2600
            "A-" -> return 3000
            "A" -> return 3300
            "A+" -> return 3600
            "S" -> return 4000
            else -> return 5000
        }
    }
}


具象クラスの実装は上記のようになりました。

呼び出すときはイカのように。

println("ナワバリバトルの結果")
val nawabariResult: NawabariBattleResult = NawabariBattleResult()
nawabariResult.show(players)

println("\n\nガチバトルの結果")
val gachiResult: GachiBattleResult = GachiBattleResult()
gachiResult.show(players)

ソースはこちら
github.com

感想

Javaの入門書を読んでる頃、抽象クラスとかインターフェースとか何に使うのだろう?とずっと疑問で、
業務でもあまり使わないで実装してましたがデザインパターンを学ぶと使い所が見えてくる気がします。

Kotlinとスプラトゥーンで学ぶデザインパターン【2. Visitor】

なんとか第二回もできました!
今回はAdapterパターンです。

Adapterパターンとは

デザパタ本には

「すでに提供されているもの」と「必要なもの」の間の「ずれ」を埋めるデザインパターン

とあります。この通りなんですが、じゃあスプラトゥーンでこのパターンが使えそうな所はあるかなぁと考えました中々思いつかず。

やっと思いついたのがスプラトゥーン2のローラーの攻撃方法の変更でした。
1では横振りのみでしたが、2ではタテ振りという概念が追加されました。
この差をAdapterパターンで埋めてみることにしました。

実装

まず、1のときの実装は以下のようになっています。

val roller : SplaRoller = SplaRoller()
println(roller.swing())

ローラークラスの詳細は以下

open class SplaRoller {
    fun swing():String {
        return "Swing!"
    }
}

出力結果

Swing!

ただ振るのみ。
次に2のローラーの実装は以下のようにメソッドを呼び出せる想定で実装します。

val newRoller : SplaRoller2 = NewSplaRoller()
println(newRoller.swingHorizontal())
println(newRoller.swingVertical())

そのために必要なものが、まずは2仕様のインターフェース

SplaRoller2.kt

interface SplaRoller2 {
    fun swingHorizontal(): String
    fun swingVertical(): String
}

旧仕様と新仕様を繋ぐクラス
NewSplaRoller.kt

class NewSplaRoller : SplaRoller(), SplaRoller2 {
    override fun swingHorizontal(): String {
        return "Horizontal " + swing()
    }

    override fun swingVertical(): String {
        return "Vertical " + swing()
    }

}

出力結果

Horizontal Swing!
Vertical Swing!

という感じです。ちなみに縦振り・横振りは振り始めにジャンプしているか否かで判定されるようです!

実装してみて

実装パターンをパッと見た所、クラス・インターフェースが冗長な感じがしました。
元の実装をそのとき必要な実装に合わせるだけならSplaRollerクラスを継承してSplaRoller2を作るという方が簡単ではないかと。
でもよくよく考えると、単純に継承するだけだとSplaRollerクラスが変更された時に影響を受けやすい気がします。できるだけ疎結合にしておくという意味でやはり間に継承or移譲するクラスを挟んだ方が良さそうという結論に至りました。

蛇足

気になっていたダイナモの縦振りやはり存在するようですね。
ローラーですら相当な飛距離なのでダイナモならどんだけ飛ぶんだよ!とか楽しみにしてます。
想像では、ダイナモの縦振り=Fateアーサー王エクスカリバーです。

Kotlinとスプラトゥーンで学ぶデザインパターン【1. Iterator】

頭の中がごちゃごちゃ

最近自分の中で考えてることをごちゃ混ぜにしてアウトプットしたらタイトルのような結果になりました。
イカ、最近の自分の思考

  • 「サーモンランとか激アツ!これはローカルでできてほしい」
  • 「あかべこ本一通りやったけど次は何しよう」
  • スプラトゥーン2発売まで2ヶ月きった!」
  • WiiU売ってしまってスプラトゥーンできない。やりたいやりたいYoutubeで我慢」
  • 「KotlinがGoogle公式の言語に!これはプロダクト採用ワンチャンある。勉強しよう」
  • 「2はとりあえずチャージャーから入ってエイムリハビリしていこう」
  • アーキテクチャ意識して制約加えてコーディングするようになってから悩む時間が増えた」
  • 「制約の中でもスムーズにコーディングするためにはデザインパターン的な技が必要では?」
  • 「赤木も山王工業戦に備えてスピンムーブとか練習してたし、やはり新しい技(デザインパターン)を習得しよう」

ベースになっているのは結城浩さんのデザインパターン本です。
それをKotlinで書き直してみました。第一回目はIteratorパターンです。
ソースは
DesignPatternInKotlin/src/iterator at master · kseito/DesignPatternInKotlin · GitHub
にまとまっています。

Iteratorパターン

Iteratorパターンとは、何かの集合から要素を順番に取り出して処理していくものです。
今回は、スプラトゥーンのイカの画面を意識して作りました。

f:id:k-seito:20170603141846j:plain

バトルで使用する武器を選択する画面です。
武器(Wepon)があり、それがリスト化(WeponList)されそれが一覧表示(WeponIterator+println)されます。


Wepon.kt

data class Wepon(val name: String, val range: Int, val power: Int, val quickness: Int)

WeponList.kt

class WeponList(count: Int): Aggreagte {

    override fun iterator(): Iterator {
        return WeponIterator(this)
    }

    private var wepons: Array<Wepon?> = arrayOfNulls(count)
    private var count: Int = 0

    fun add(wepon: Wepon) {
        wepons[count] = wepon
        count++
    }

    fun getWeponAt(index: Int): Wepon? {
        return wepons[index]
    }

    fun getCount(): Int {
        return count
    }
}

WeponIterator.kt

class WeponIterator(private val weponList: WeponList) : Iterator {

    private var index: Int = 0

    override fun next(): Any {
        val wepon: Wepon? = weponList.getWeponAt(index)
        index++
        return wepon ?: Wepon("未開放", 0, 0, 0)
    }

    override fun hasNext(): Boolean {
        return weponList.getCount() > index
    }

}

デザインパターンとして作成するクラスはここまで(Interfaceは省略します)
あとは動かすだけ。

Main.kt

fun main(args: Array<String>) {
    val list: WeponList = WeponList(5)
    list.add(Wepon("わかばシューター", 32, 32, 75))
    list.add(Wepon("スプラシューターコラボ", 50, 45, 55))
    list.add(Wepon(".96ガロンデコ", 68, 85, 15))
    list.add(Wepon("カーボンローラー", 20, 65, 70))
    list.add(Wepon("ダイナモローラー", 72, 30, 20))

    val weponSelect: Iterator = list.iterator()
    while (weponSelect.hasNext()) {
        val wepon: Wepon = weponSelect.next() as Wepon
        System.out.println("武器名:" + wepon.name + "、射程:" + wepon.range + "、攻撃力:" + wepon.power +
                "、連射力:" + wepon.quickness)
    }
}

出力結果は

武器名:わかばシューター、射程:32、攻撃力:32、連射力:75
武器名:スプラシューターコラボ、射程:50、攻撃力:45、連射力:55
武器名:.96ガロンデコ、射程:68、攻撃力:85、連射力:15
武器名:カーボンローラー、射程:20、攻撃力:65、連射力:70
武器名:ダイナモローラー、射程:72、攻撃力:30、連射力:20

となります。

Kotlinを使ってみて

今回実装したときにArrayの初期化ではまりました。指定した数の要素を確保するためにarrayOfNullsを使用しましたが、
要素がNullableになってしまい何とかNotNullな要素にしたい!と思いいろいろ考えてみましたがだめでした。。。
このへんはまだまだKotlin力が足りないと思うので精進します。


デザパタ本見たら全部で23種類デザインパターンがあった・・・全てスプラトゥーンネタにできるかわかりませんが完走目指して頑張ります。

RxJava2.0の基本クラスを学ぶ

今さらですがRxJava2.0について学びたいと思います。

RxJava2.0ではObservableに似たクラスとしてFlowable, Maybeが追加されています。
今回は復習の意味も込めて、Observable, Single, Completable, Maybeの違いについて調べてみたいと思います。 Flowableはだいぶ特殊でObservableで扱うストリームでバッファを考慮するとき等に使うようで、(自分にとっては)現状あまり用途がないので割愛します。

Observable

0~N個のオブジェクトの流れ、完了、エラーを扱います。

プログラム

Observable.range(1, 10)
        .subscribe(System.out::println);

出力結果

1
2
3
4
5
6
7
8
9
10

Single

1つのオブジェクトの流れ、エラーを扱います。 onCompleteは呼ばれないです。

プログラム

Single.just(1)
        .subscribe(System.out::println);
                

出力結果

1

Completable

オブジェクトの流れは扱わず、完了かエラーの判定のみ扱います。

完了

プログラム

Observable<Integer> observable = Observable.range(1, 10);

Completable.fromObservable(observable)
        .subscribe(
                () -> System.out.println("Complete!"),
                e -> System.out.println(e.getMessage()));

出力結果

Complete!

エラー

プログラム

Completable.fromObservable(observable)
        .subscribe(
                () -> System.out.println("Complete!"),
                e -> System.out.println(e.getMessage()));

出力結果

damepo

Maybe

0~1個のオブジェクト、エラーを扱います。 onCompleteは呼ばれます。

プログラム

Maybe.just(1)
        .filter(integer -> integer == 2)
        .defaultIfEmpty(3)
        .subscribe(System.out::println);

出力結果

3

感想

SingleとMaybeの違いが分かりづらいです。 Singleに0個のオブジェクトの時の処理を追加したのがMaybeなのかな・・・。もっと勉強しないと!

参考URL

GitHub - ReactiveX/RxJava: RxJava – Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM. RxJava2.0 Observable, Single, Maybe, Completableの使い分けメモ - Qiita