20190318のJavaに関する記事は23件です。

【Gang of Four】デザインパターン学習 - Interpreter

通訳

目次
このパターンは構造を理解するのに時間がかかりました…

構文木を生成するのが目的のパターンでしょうか。
本パターンは開始タグから終了タグまでのひとまとまりを、一つのオブジェクトで表現します。
Compositeパターンを用いて解析し、まとまりごとに処理を行う感じですね。

目的

言語に対して、文法表現と、それを使用して文を解釈するインタプリタを一緒に定義する。

構成要素

・AbstractExpression すべてのノードの抽象クラス
・TerminalExpression 終端を表現するクラス
・NonterminalExpression 終端以外を表現するクラス
・Context 文脈・状況
・Client 利用者

実装

階層構造になっている四則演算用データを処理するサンプルプログラムを実装します。
<+> ~ </+> 足し算
<-> ~ </-> 引き算
<*> ~ </*> 掛け算
</> ~ <//> 割り算

対象サンプルデータ.xml
<+>
    1 
    <-> 
        2 
        3 
        4 
        5 
        6 
        <*> 
            7 
            8 
        </*> 
        9 
    </->
    10 
    11 
    </> 
        12 
        4 
    <//> 
</+>

上記サンプルデータを見慣れた形へ整形すると1 + (2 - 3 - 4 - 5 - 6 - (7 * 8) - 9) + 10 + 11 + (12 / 4)となり、解-56が期待値です。

AbstractExpression 命令・抽象的な表現

Expression.kt
package interpreter

interface Expression {
    enum class Operator(val startTag: String, val endTag: String) {
        Plus("<+>", "</+>"),
        Minus("<->", "</->"),
        Multiplication("<*>", "</*>"),
        Divide("</>", "<//>"),
    }
    fun interpret(context: Context): Int

    companion object {
        fun getExpression(token: String?): Expression? {
            return when (token) {
                Operator.Plus.startTag -> {
                    PlusNonterminalExpression()
                }
                Operator.Minus.startTag -> {
                    MinusNonterminalExpression()
                }
                Operator.Multiplication.startTag -> {
                    MultiplicationExpression()
                }
                Operator.Divide.startTag -> {
                    DivideExpression()
                }
                Operator.Plus.endTag,
                Operator.Minus.endTag,
                Operator.Multiplication.endTag,
                Operator.Divide.endTag -> {
                    null
                }
                else -> {
                    TerminalExpression()
                }
            }
        }
    }
}

TerminalExpression 終端となる表現
木の末端となる要素です。本サンプルデータでは数字がそれにあたります。

TerminalExpression.kt
package interpreter

class TerminalExpression: Expression {
    private var saveToken: String? = null

    override fun interpret(context: Context): Int {
        saveToken = context.token
        context.nextToken()
        return Integer.parseInt(saveToken ?: "0")
    }

    override fun toString(): String {
        return saveToken ?: "0"
    }
}

NonterminalExpression 終端以外の表現
木が分岐する要素です。本サンプルデータでは<+>や<->がそれにあたります。

演算クラス

CalcExpression.kt
package interpreter

import java.util.ArrayList

abstract class CalcExpression: Expression {
    protected val numList = ArrayList<Int>()
    protected val list = ArrayList<Expression>()

    override fun interpret(context: Context): Int {
        context.nextToken()
        loop@ while (!context.isEnd) {
            val childExpressions = Expression.getExpression(context.token)
            if (childExpressions == null) {
                context.nextToken()
                break@loop
            } else {
                numList.add(childExpressions.interpret(context))
                list.add(childExpressions)
            }
        }
        return calc()
    }

    abstract fun calc(): Int
    abstract override fun toString(): String
}

足し算クラス

PlusNonterminalExpression.kt
package interpreter

/**
 * 足し算(<+> ~ </+>)
 */
class PlusNonterminalExpression: CalcExpression() {
    override fun calc(): Int {
        var result = numList.first().toInt()
        for (i in 1 until numList.size) {
            result += numList[i]
        }
        return result
    }

    override fun toString(): String {
        return "+$list"
    }
}

引き算クラス

MinusNonterminalExpression.kt
package interpreter

/**
 * 引き算(<-> ~ </->)
 */
class MinusNonterminalExpression: CalcExpression() {
    override fun calc(): Int {
        var result = numList.first().toInt()
        for (i in 1 until numList.size) {
            result -= numList[i]
        }
        return result
    }

    override fun toString(): String {
        return "-$list"
    }
}

掛け算クラス

MultiplicationExpression.kt
package interpreter

/**
 * 掛け算(<*> ~ </"*>)
 */
class MultiplicationExpression: CalcExpression() {
    override fun calc(): Int {
        var result = numList.first().toInt()
        for (i in 1 until numList.size) {
            result *= numList[i]
        }
        return result
    }

    override fun toString(): String {
        return "*$list"
    }
}

割り算クラス

DivideExpression.kt
package interpreter

class DivideExpression: CalcExpression() {
    override fun calc(): Int {
        var result = numList.first().toInt()
        for (i in 1 until numList.size) {
            result /= numList[i]
        }
        return result
    }

    override fun toString(): String {
        return "/$list"
    }
}

Context 文脈・状況

Context.kt
package interpreter

import java.util.*

class Context(source: String) {
    private val tokens: StringTokenizer = StringTokenizer(source)
    var token: String? = null
        private set

    val isEnd: Boolean
        get() = !tokens.hasMoreElements()

    init {
        nextToken()
    }

    fun nextToken() {
        var token: String? = null
        if (!isEnd) {
            token = tokens.nextToken() // 標準の .nextToken() を呼び出す
        }
        this.token = token
    }
}

Client 利用者

Main.kt
package interpreter

import interpreter.Expression.Companion.getExpression

fun main(args: Array<String>) {
    val source = "<+> 1 <-> 2 3 4 5 6 <*> 7 8 </*> 9 </-> 10 11 </> 12 4 <//> </+>"
    val context = Context(source)
    val expression = getExpression(context.token)

    println(expression?.interpret(context))
    println(expression.toString())
}
[out-put]
-56
+[1, -[2, 3, 4, 5, 6, *[7, 8], 9], 10, 11, /[12, 4]]

期待通りの式、解となりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Encoding and Decoding example in Java

Until Java 8, there was no standard way to Base64 encode a String in Java or decode a base64 encoded String. Java Programmers either use Apache Commons library and it's Base64 class to encode or decode binary data into base 64 encoding or rely on internal Sun classes e.g. sun.misc.BASE64Encoder and sun.misc.BASE64Decoder(), which were not officially part of JDK and can be removed without notification. Java 8 solves this problem by providing standard support for base64 encoding and decoding by providing a java.util.Base64 class. This class contains methods like getEncoder() and getDecoder() to provide Base64 encoder and decoder to carry out base 64 encoding of String or binary data. In this article, I'll show you some example of how to base64 encode String in Java 8.

Java 8 provides three types of base64 encoding and corresponding built-in encoder and decoder, as shown below.

Basic Base64 Encoder
In this type of encoder, only characters A-Z a-z0-9+/ are used for base64 encoding. The encoder does not add any line feed in the output, and the decoder rejects any character other than A-Za-z0-9+/. That's why you see a big line of alphanumeric characters. This is the one you will use most likely and we'll see examples of this Base64 encoder in this tutorial.

You can use the static getEncoder() and getDecoder() method of Base64 class to obtain the basic base64 encoder and decoder and use their encode() and decode() for base64 encoding and decoding.

URL Base64 Encoder
This is similar to basic type but instead of "\", underscore ("") is used i.e. only characters A-Za-z0-9+ are supported. The key point about this encoder is that output is also URL and filename safe because there is no "\" character which acts as the path separator in many operating systems e.g. Windows.

You can use the getUrlEncoder() and getUrlDecoder() method to get the encoder and decoder who can perform this type of base64 encoding.

MIME Base64 Encoder
This is the third type of base64 encoding supported by Java 8. In this case, the output is mapped to MIME-friendly format. The output is represented in lines of no more than 76 characters each and uses a carriage return '\r' followed by a linefeed '\n' as the line separator. No line separator is present to the end of the encoded output.

You can use the static method getMimeEncoder() and getMimeDecoder() to get the MIME type base64 encoder and decoder with specified line length and line separators.

Base64 Encoding Example in Java 8
Now, let's an example of how to encode String into base64 encoding in Java 8. Here is our sample Java program which demonstrates base64 encoding and decoding in basic, URL and MIME types.

For those who are not very familiar with base64 encoding in general, here is good slide to recap the fundamentals:

Java program for base64 encoding and decoding String
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;

/*
* Java Program to Base64 encode a String in JDK 8.
*
*/
public class PascalTriangleInJava {

public static void main(String[] args) {

    // Encoding String using basic base64 encoder
    Encoder theEncoder = Base64.getEncoder();
    String original = "Minecraft";
    byte[] theArray = original.getBytes(StandardCharsets.UTF_8);
    String base64encodedString = theEncoder.encodeToString(theArray);
    System.out.println("Original String: " + original);
    System.out.println("Base64 Encoded String :" + base64encodedString);

    // Decoding a base64 encoded String in Java 8
    Decoder theDecoder = Base64.getDecoder();
    byte[] byteArray = theDecoder.decode(base64encodedString);
    String decoded = new String(byteArray, StandardCharsets.UTF_8);
    System.out.println("decoded String: " + decoded);

}

}

Output
Original String: Minecraft
Base64 Encoded String :TWluZWNyYWZ0
decoded String: Minecraft

That's all about how to encode String in base64 in Java 8. Now, there is no need to use the Apache commons Code library (an additional dependency on the third-party library) or non-standard sun.misc.BASE64Encoder class for base64 encoding. You should only use the new java.util.Base64 class for encoding and decoding String or byte array into Base64. In the next part of this article, I'll teach you how to decode a Base64 encoded String in Java 8.

Related blog:

Spring rest interview questions

Spring boot interview questions

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Gang of Four】デザインパターン学習 - Command

コマンド

目次
本パターンの主旨は、要求をオブジェクト化することで動的に要求を編集できることと、一連の処理を一つのオブジェクトに纏め共通のインターフェースを用意することで保守管理を簡単にすることを実現する、でしょうか。

よくこんなメソッドを見かけます。

Receiver.kt
    fun run(mode: ModeType) {
        when(mode) {
            ModeType.Begin -> {
                // 準備処理
                loadProperty()
                startDriver()
            }
            ModeType.Running -> {
                // メイン処理
                running()
            }
            ModeType.After -> {
                // 終了処理
                saveState()
                stopDriver()
            }
        }
    }

    private fun loadProperty(){}
    private fun startDriver(){}
    private fun running(){}
    private fun saveState(){}
    private fun stopDriver(){}

中断処理が必要になったため、saveState()のみ呼びだすモードがほしくなったのでモードと実装を追加します。

Receiver.kt
        ModeType.Interruption -> {
            // 中断処理
            saveState()
        }

また更に別のモードを…となってくるとReceiverクラスを延々と修正しなければならず、Modeもどんどん増えていきます。これを本パターンを適用することでReceiverクラスを修正することなく、柔軟な振る舞いをさせることができるようになります。

目的

要求をオブジェクトとしてカプセル化することによって、異なる要求や、要求からなるキューやログにより、クライアントをパラメータ化する。また、取り消し可能なオペレーションをサポートする。

構成要素

・Command Receiverクラスによって実行されるメソッドを定義する抽象クラス
・ConcreteCommand Commandクラスの具象クラス
・Client 使う人
・Invoker
・Receiver 命令の出し方を知っているクラス

実装

Receiver 命令の出し方を知っているクラス
インターフェースと

Receiver.kt
package command

interface Receiver {
    fun getName(): String
}

具象クラス

Car.kt
package command

class Car(private val name: String): Receiver {

    override fun getName(): String {
        return "レシーバは${name}オブジェクトです"
    }

    fun openDoor() {
        println("ドアを開ける")
    }

    fun engineStart() {
        println("エンジンスタート")
    }

    fun engineStop() {
        println("エンジンストップ")
    }

    fun lock() {
        println("ロックする")
    }

    fun unlock() {
        println("ロックを解除する")
    }

    fun pushAxelPedal() {
        println("アクセルを踏む")
    }

    fun pushBreakePedal() {
        println("ブレーキペダルを踏む")
    }
}

Command Receiverクラスによって実行されるメソッドを定義する抽象クラス

Command.kt
package command

interface Command {
    fun execute()
}

ConcreteCommand Commandクラスの具象クラス

SimpleCommand.kt
package command

class SimpleCommand(private val receiver: Receiver, private val method: String): Command {
    override fun execute() {
        receiver.javaClass.getDeclaredMethod(method).invoke(receiver)
    }
}
MacroCommand.kt
package command

import kotlin.collections.ArrayList

class MacroCommand: Command {
    private val commandList = ArrayList<Command>()

    override fun execute() {
        commandList.forEach {
            it.execute()
        }
    }

    fun addCommand(command: Command) {
        commandList.add(command)
    }

    fun removeCommand(command: Command) {
        commandList.remove(command)
    }
}

Client
使う人
車のエンジンをかけるコマンドを作成します。

Client.kt
package command

class Client {

    init {
        val receiver = Car("プリウス")
        createStartCarCommand(receiver).execute()
    }

    private fun createStartCarCommand(receiver: Car): Command {
        val startCarCommand = MacroCommand()
        startCarCommand.addCommand(SimpleCommand(receiver, "unlock"))
        startCarCommand.addCommand(SimpleCommand(receiver, "openDoor"))
        startCarCommand.addCommand(SimpleCommand(receiver, "engineStart"))
        return startCarCommand
    }
}

実行結果

[out-put]
ロックを解除する
ドアを開ける
エンジンスタート

車のエンジンをとめるコマンドが欲しくなりました。

Client.kt
package command

class Client {

    init {
        val receiver = Car("プリウス")
        createStopCarCommand(receiver).execute()
    }

    private fun createStopCarCommand(receiver: Car): Command {
        val stopCarCommand = MacroCommand()
        stopCarCommand.addCommand(SimpleCommand(receiver, "engineStop"))
        stopCarCommand.addCommand(SimpleCommand(receiver, "openDoor"))
        stopCarCommand.addCommand(SimpleCommand(receiver, "lock"))
        return stopCarCommand
    }
}

実行結果

[out-put]
エンジンストップ
ドアを開ける
ロックする

車を走らせるコマンドを作成したくなりました。

Client.kt
package command

class Client {

    init {
        val receiver = Car("プリウス")
        createCarRunCommand(receiver).execute()
    }

    private fun createCarRunCommand(receiver: Car): Command {
        return SimpleCommand(receiver, "pushAxelPedal")
    }
}

実行結果

[out-put]
アクセルを踏む

車のエンジンをかけ、しばらく走ったあとエンジンをとめるコマンドが必要になりました。

Client.kt
package command

class Client {

    init {
        val receiver = Car("プリウス")
        createStartAndRunAndStopCarCommand(receiver).execute()
    }

    private fun createStartAndRunAndStopCarCommand(receiver: Car): Command {
        val startAndRunAndStopCarCommand = MacroCommand()
        startAndRunAndStopCarCommand.addCommand(createStartCarCommand(receiver))

        val runCommand = createCarRunCommand(receiver)
        startAndRunAndStopCarCommand.addCommand(runCommand)
        startAndRunAndStopCarCommand.addCommand(runCommand)
        startAndRunAndStopCarCommand.addCommand(runCommand)
        startAndRunAndStopCarCommand.addCommand(runCommand)

        startAndRunAndStopCarCommand.addCommand(createStopCarCommand(receiver))

        return startAndRunAndStopCarCommand
    }

    private fun createStartCarCommand(receiver: Car): Command {
        val startCarCommand = MacroCommand()
        startCarCommand.addCommand(SimpleCommand(receiver, "unlock"))
        startCarCommand.addCommand(SimpleCommand(receiver, "openDoor"))
        startCarCommand.addCommand(SimpleCommand(receiver, "engineStart"))
        return startCarCommand
    }

    private fun createCarRunCommand(receiver: Car): Command {
        return SimpleCommand(receiver, "pushAxelPedal")
    }

    private fun createStopCarCommand(receiver: Car): Command {
        val stopCarCommand = MacroCommand()
        stopCarCommand.addCommand(SimpleCommand(receiver, "engineStop"))
        stopCarCommand.addCommand(SimpleCommand(receiver, "openDoor"))
        stopCarCommand.addCommand(SimpleCommand(receiver, "lock"))
        return stopCarCommand
    }
}

実行結果

[out-put]
ロックを解除する
ドアを開ける
エンジンスタート
アクセルを踏む
アクセルを踏む
アクセルを踏む
アクセルを踏む
エンジンストップ
ドアを開ける
ロックする

GoF本の適用可能性を読むと、このパターンの肝は要求をオブジェクト化することによって任意の要求を取り消しできること、所謂MacroCommandクラスのremoveCommandメソッドを呼び出すことらしいのですが、イマイチ必要になるシチュエーションが思い浮かびません。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Gang of Four】デザインパターン学習 - Chain of Responsibility

責任の連鎖

目次
親子関係のあるオブジェクトにおいて、処理を行うオブジェクトを親子間で柔軟に決定することができるようにするのが本パターンか?

目的

1つ以上のオブジェクトに要求を処理する機会を与えることにより、要求を送信するオブジェクトと受信するオブジェクトの結合を避ける。受信する複数のオブジェクトをチェーン状につなぎ、あるオブジェクトがその要求を処理するまで、そのチェーンに沿って要求を渡していく。

構成要素

・Handler 親子クラスの抽象クラス
・ConcreteHandler Handlerの具象クラス
・Client 使用者

実装

画面を構成する部品で例えると下記のような親子関係があると思います。

ウィンドウ
----ウインドウの中に表示するダイアログ
--------ダイアログの中にあるボタン
----ウインドウの中にあるボタン

どれかのオブジェクトを操作して不具合が発生した場合に、対応処理を行うオブジェクトを自由に決められるサンプルコードを実装します。

Handler 親子クラスの抽象クラス
共通クラス
全部のオブジェクトのスーパークラスです。
Viewをインスタンス化した際にmessageTypeへNormal以外を設定したものは、当該オブジェクト内で不具合に対する処理を実行します。
Normalを設定したViewは親Viewへ処理を任せます。そのまた親Viewが...繰り返し

View.kt
package chainofresponsibility

abstract class View(private val parent: View?, private val messageType: MessageType) {
    enum class MessageType {
        Normal,
        Warning,
        Danger
    }

    protected fun handleHelp() {
        if (hasMessage()) {
            helpLogic()
        } else {
            parent?.handleHelp()
        }
    }

    abstract fun helpLogic()

    private fun hasMessage(): Boolean {
        val ret = MessageType.Normal != messageType

        if (ret) {
            createDialog()
        }
        return ret
    }

    private fun createDialog() {
        when (messageType) {
            MessageType.Warning -> {
                print("警告ダイアログ:")
            }
            MessageType.Danger -> {
                print("エラーダイアログ:")
            }
        }
    }

}

ConcreteHandler Handlerの具象クラス

ウインドウ

Window.kt
package chainofresponsibility

class Window(parent: View?, messageType: View.MessageType): View(parent, messageType) {

    override fun helpLogic() {
        println("ウインドウに起因する不具合!")
    }
}

ダイアログ

Dialog.kt
package chainofresponsibility

class Dialog(parent: View?, messageType: View.MessageType): View(parent, messageType) {

    override fun helpLogic() {
        println("ダイアログに起因する不具合!")
    }
}

ボタン
actionメソッドに0以外を渡すと不具合が発生します。

Button.kt
package chainofresponsibility

class Button(parent: View?, messageType: View.MessageType): View(parent, messageType) {

    fun action(arg: Int) {
        if (arg == 0) {
            println("正常終了")
        } else {
            handleHelp()
        }
    }

    override fun helpLogic() {
        println("ボタンに起因する不具合!")
    }
}

Client 使用者
action(1)で不具合が発生します。下記のサンプルコードではbutton1で発生した不具合はwindowがキャッチし、button2で発生した不具合はdialogがキャッチするようになっています。
dialogのメッセージタイプをNormalへ変更すればwindowがキャッチするようになります。

Client.kt
package chainofresponsibility

class Client {
    init {
        // ウインドウ
        val window = Window(null, View.MessageType.Danger)
        // ウインドウ直下に配置されたボタン
        val button1 = Button(window, View.MessageType.Normal)
        // ウインドウ直下に配置されたダイアログ
        val dialog = Dialog(window, View.MessageType.Warning)
        // ダイアログに配置されたボタン
        val button2 = Button(dialog, View.MessageType.Normal)

        button1.action(0)
        button1.action(1)
        button1.action(0)

        button2.action(0)
        button2.action(1)
        button2.action(0)
    }
}
[out-put]
正常終了
エラーダイアログ:ウインドウに起因する不具合!
正常終了
正常終了
警告ダイアログ:ダイアログに起因する不具合!
正常終了

このパターンはどうやろう…

パッと見ではそのうちどの親オブジェクトがハンドリングするのか管理しきれなくなって結局バグを生み出しそうですが、そもそもの主旨がメッセージを受信するオブジェクトを知る必要がないようにするパターンなので、それで良いんでしょうか。
うーん。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Gang of Four】デザインパターン学習 - Proxy

代理

目次
動機の一つとして、生成と初期化にコストのかかるオブジェクトを使用する時(GUIアプリでいう画面に表示する時)に初めてインスタンス化する、といった場合に本パターンを適用します。

ある画面内には1000枚画像を表示する必要がありますが、有効描画領域には10枚までしか表示することができません。にも関わらず初期表示時にGraphicsクラスのオブジェクトを1000個分インスタンス化し、画像を1000枚ロードしていては、初期表示にあまり関係のない処理にリソースを大きく割かなければならなくなります。

そういった課題を解消するために本パターンを用いるようです。本来Graphicsオブジェクトを割り当てておく領域にProxyを割り当てて置き、表示するタイミングになって初めて画像をロードします。

目的

あるオブジェクトへのアクセスを制御するために、そのオブジェクトの代理、または入れ物を提供する。

構成要素

・Proxy 影武者クラス
・Subject ProxyクラスとRealSubjectクラスの抽象クラス
・RealSubject 本物クラス

実装

ではサンプルコードとして画像描画プログラムを実装します。実際に1000個分インスタンス化するのもめんどくさいので初期表示時の描画数を3とします。

Subject ProxyクラスとRealSubjectクラスの抽象クラス

Subject.kt
package proxy

interface Subject {
    fun draw()
}

RealSubject 本物クラス
イメージクラス
インスタンス化した瞬間画像を読み込むクラス

Image.kt
class Image(private val filePath: String): Subject {

    init {
        loadImage(filePath)
    }

    override fun draw() {
        println("${filePath}を描画")
    }

    private fun loadImage(filePath: String) {
        println("${filePath}を読み込み")
    }
}

ではこのImageクラスを使用して画面を初期表示してみます。

クライアントクラス

Client.kt
package proxy

class Client {
    private val initDrawNum = 3

    init {
        val imageList = getNonProxyImageList()
        for (i in 0 until initDrawNum) {
            imageList[i].draw()
        }
    }

    private fun getNonProxyImageList(): ArrayList<Subject> {
        val imageList = ArrayList<Subject>()
        imageList.add(Image("./image/りんご.png"))
        imageList.add(Image("./image/みかん.png"))
        imageList.add(Image("./image/もも.png"))
        imageList.add(Image("./image/ばなな.png"))
        imageList.add(Image("./image/ぱいなっぷる.png"))
        imageList.add(Image("./image/いちご.png"))

        return imageList
    }
}
[out-put]
./image/りんご.pngを読み込み
./image/みかん.pngを読み込み
./image/もも.pngを読み込み
./image/ばなな.pngを読み込み
./image/ぱいなっぷる.pngを読み込み
./image/いちご.pngを読み込み
./image/りんご.pngを描画
./image/みかん.pngを描画
./image/もも.pngを描画

3つ表示するだけで良いものの全ての画像を読み込んでしまっています。Proxyクラスを利用してみます。

Proxy 影武者クラス
イメージProxyクラス
一度読み込んだものは永久に保持するようになっていますが、有効描画領域外になった場合、ロードした画像を解放するメソッドを実装したほうがより良いですね。

しかし、めんどくさいので今回は省略します。

ImageProxy.kt
package proxy

class ImageProxy(private val filePath: String): Subject {
    var image: Image? = null

    override fun draw() {
        image?.let { unwrapImage ->
            unwrapImage.draw()
        } ?: run {
            val tmpImage = Image(filePath)
            tmpImage.draw()
            image = tmpImage
        }
    }
}

再びクライアントクラス。今回はProxyクラスを利用します。

Client.kt
package proxy

class Client {
    private val initDrawNum = 3

    init {
        val imageList = getProxyImageList()
        for (i in 0 until initDrawNum) {
            imageList[i].draw()
        }
    }

    private fun getProxyImageList(): ArrayList<Subject> {
        val proxyImageList = ArrayList<Subject>()
        proxyImageList.add(ImageProxy("./image/りんご.png"))
        proxyImageList.add(ImageProxy("./image/みかん.png"))
        proxyImageList.add(ImageProxy("./image/もも.png"))
        proxyImageList.add(ImageProxy("./image/ばなな.png"))
        proxyImageList.add(ImageProxy("./image/ぱいなっぷる.png"))
        proxyImageList.add(ImageProxy("./image/いちご.png"))

        return proxyImageList
    }

    private fun getNonProxyImageList(): ArrayList<Subject> {
        val imageList = ArrayList<Subject>()
        imageList.add(Image("./image/りんご.png"))
        imageList.add(Image("./image/みかん.png"))
        imageList.add(Image("./image/もも.png"))
        imageList.add(Image("./image/ばなな.png"))
        imageList.add(Image("./image/ぱいなっぷる.png"))
        imageList.add(Image("./image/いちご.png"))

        return imageList
    }
}
[out-put]
./image/りんご.pngを読み込み
./image/りんご.pngを描画
./image/みかん.pngを読み込み
./image/みかん.pngを描画
./image/もも.pngを読み込み
./image/もも.pngを描画

今度は表示する分だけロードできるようになりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Gang of Four】デザインパターン学習 - Fly Weight

軽い物

目次
書中では何やら難しいことが色々と書いてありますが、要するに同じオブジェクトを複数回作成していると色々な面で高コストになるので、Factoryクラスを作成しインスタンスを生成するのは一度きりとすることでそれを回避しましょうね。というのが本パターンの主旨です。

目的

多数の細かいオブジェクトを効率よくサポートするために共有を利用する。

構成要素

・Flyweight 同一インスタンスを大量に生成する必要があるオブジェクトの抽象クラス
・ConcreteFlyweight Flyweightの具象クラス
・UnsharedConcreteFlyweight Flyweightクラスを管理するクラス 行、列など子としてFlyweightクラスを保持するクラス
・FlyweightFactory Flyweightクラスを生成するクラス
・Client 使用者

実装

※UnsharedConcreteFlyweightに相当するクラスはありません。
文字オブジェクト管理するサンプルコードを書きます。書中のサンプルコードも文字オブジェクトが例になっているので同じようなものになってしまいますが。

Flyweight 同一インスタンスを大量に生成する必要があるオブジェクトの抽象クラス
ConcreteFlyweight Flyweightの具象クラス
文字クラス
文字を1文字管理するクラス

Character.kt
package flyweight

class Character(private val character: Char) {
    fun print() {
        print(character)
    }
}

FlyweightFactory Flyweightクラスを生成するクラス
文字製造クラス
文字クラスのインスタンスを生成、プールするファクトリークラスを作成します。
当該クラスにて一度生成したオブジェクトをプールすることにより、以前生成したことのあるオブジェクトを再度生成しない。

CharacterFactory.kt
package flyweight

// シングルトンオブジェクトにする
object CharacterFactory {

    val characterPool = HashMap<AllCharacter, Character>()

    enum class AllCharacter(val value: Char) {
        A('A'),
        B('B'),
        C('C'),
        D('D'),
        E('E'),
        F('F'),
        G('G'),
        H('H'),
        I('I'),
        J('J'),
        K('K'),
        L('L'),
        M('M'),
        N('N'),
        O('O'),
        P('P'),
        Q('Q'),
        R('R'),
        S('S'),
        T('T'),
        U('U'),
        V('V'),
        W('W'),
        X('X'),
        Y('Y'),
        Z('Z')
    }

    fun getCharacterObject(characterType: AllCharacter): Character {
        // 未生成であれば、当該インスタンスをプールする。
        characterPool[characterType]?.let {
            return it
        } ?: run {
            val char = Character(characterType.value)
            characterPool[characterType] = char
            return char
        }
    }
}

Client 使用者
文字を使う人
BANANAを生成します。

Client.kt
package flyweight

class Client {
    init {
        val characters = ArrayList<Character>()
        // "BANANA"を生成します
        characters.add(CharacterFactory.getCharacterObject(CharacterFactory.AllCharacter.B))
        characters.add(CharacterFactory.getCharacterObject(CharacterFactory.AllCharacter.A))
        characters.add(CharacterFactory.getCharacterObject(CharacterFactory.AllCharacter.N))
        characters.add(CharacterFactory.getCharacterObject(CharacterFactory.AllCharacter.A))
        characters.add(CharacterFactory.getCharacterObject(CharacterFactory.AllCharacter.N))
        characters.add(CharacterFactory.getCharacterObject(CharacterFactory.AllCharacter.A))

        characters.forEach {
            it.print()
        }
    }
}
[output]
BANANA
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Gang of Four】デザインパターン学習 - Facade

外見

目次
クライアントがあるサブシステムを利用する場合、そのままでは複雑なインタフェースを最低限必要なインターフェースのみにまとめるようなパターンです。
当該サブシステムをカスタマイズするようなことがない場合、Facadeパターンを適用したインターフェースのみ注視することによりサブシステムを利用しやすくさせます。

要はクライアントとサブシステムの結合度を弱めるためですね。サブシステムを変更する際も結合度が弱いためし易くなります。

目的

サブシステム内に存在する複数のインタフェースに1つの統一インタフェースを与える。Facade パターンはサブシステムの利用を容易にするための高レベルインタフェースを定義する。

構成要素

・Facade クライアントがサブシステムを利用するにあたって必要最低限のインターフェースを定義するクラス
・サブシステム内のクラス 利用するサブシステム

構成要素のうちサブシステム内のクラスだけ何故ちゃんとした名前がついていないんでしょう…

実装

窓口を新たに作成するだけですがサンプルコードを書きます。

サブシステム内のクラス 利用するサブシステム
プリンタークラス インターフェース色々

Printer.kt
package facade

class Printer {
    fun startUp() { print("起動") }
    fun shutDown() { print("終了") }
    fun setToner(color: String) { print("トナーを${color}色に設定します。") }
    fun addPaper(num: Int) { print("紙を${num}枚追加します。") }
    fun login(name: String) { print("ユーザ${name}がログインしました。") }
    fun printOut(num: Int) { print("${num}枚印刷します。") }
}

クライアントがプリンターを操作する上で必要なのは印刷することだけなので、それのみの窓口を用意します。

Facade クライアントがサブシステムを利用するにあたって必要最低限のインターフェースを定義するクラス
インターフェースを限定

FacadePrinter.kt
package facade

class FacadePrinter {
    private val printer = Printer()

    fun printOut(num: Int) {
        printer.printOut(num)
    }
}

クライアント

Client.kt
package facade

class Client {
    init {
        val printer = FacadePrinter()
        printer.printOut(15)

        // 他のメソッドは使用できない
        // printer.startUp()
        // pritner.shutDown()
    }
}

結果といっても大したものはないですが

[output]
15枚印刷します。

なんかこれは自分の認識が間違っちゅう気がする。コメントにてご指摘ください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Gang of Four】デザインパターン学習 - Decorator

装飾者

目次
いきなり話が脇道に逸れますが、デザインパターンやオブジェクト指向に関する書籍を読んでいるとよくオブジェクトに対する責務責任といった言葉がでてきます。
これは当該オブジェクトにて出来なければならないことを指しています。
また単一責務の原則というのがありますが、これはクラスを変更する理由は1つであるべき、というものです。

例えば以下のようなクラスは修正する理由が2つあります。
- 画面遷移処理を追加したい→画面に関する変更理由
- 引き算処理を追加した→計算に関する変更理由

画面計算.kt
interface 画面計算 {
    fun 描画(): Boolean
    fun 足し算(num1: Int, num2: Int): Int
}

単一責務の原則に則ると以下のように分離すべきです。

画面.kt
interface 画面 {
    fun 描画(): Boolean
    fun 画面遷移(): Boolean
}
計算.kt
interface 計算 {
    fun 足し算(num1: Int, num2: Int): Int
    fun 引き算(num2: Int, num2: Int): Int
}

この単一責務の原則というのも、やはり変更に対して柔軟に対応するための考えだと理解しています。脇道話は以上です。

では本題、目的に責任を動的に追加するとあるので、継承を用いずにオブジェクト(インスタンス)に任意のタイミングで機能を追加していくようなパターンですね。

目的

オブジェクトに責任を動的に追加する。Decorator パターンは、サブクラス化よりも柔軟な機能拡張方法を提供する。

構成要素

・Component 責任を動的に追加できるインターフェースを定義した抽象クラス
・ConcreteComponent Componentクラスの具象クラス
・Decorator Componentクラスに責任を追加するインターフェースを定義した抽象クラス
・ConcreteDecorator Decoratorクラスの具象クラス

実装

テキストビューインスタンスを生成するタイミングで、いろいろな機能を追加できるプログラムを実装します。

Component 責任を動的に追加できるインターフェースを定義した抽象クラス
Decorator Componentクラスに責任を追加するインターフェースを定義した抽象クラス
Viewコンポーネントインターフェース

ViewComponent.kt
package decorator

interface ViewComponent {
    fun draw()
}

ConcreteComponent Componentクラスの具象クラス
テキストビュー

TextView.kt
package decorator

class TextView: ViewComponent {
    override fun draw() {
        print("【テキストビュー】")
    }
}

ConcreteDecorator Decoratorクラスの具象クラス
影をつけるデコレータ

ShadowDecorator.kt
package decorator

class ShadowDecorator(viewComponent: ViewComponent): ViewComponent {
    val viewComponent = viewComponent

    override fun draw() {
        viewComponent.draw()
        addShadow()
    }

    private fun addShadow() {
        print(":影付き")
    }
}

ConcreteDecorator Decoratorクラスの具象クラス
スクロールさせるデコレータ

ScrollDecorator.kt
package decorator

class ScrollDecorator(viewComponent: ViewComponent): ViewComponent {
    val viewComponent = viewComponent

    override fun draw() {
        viewComponent.draw()
        addScroll()
    }

    private fun addScroll() {
        print(":スクロール可能")
    }

}

部品が全部できました。使っていきましょう。

使う人

ComponentManager.kt
package decorator

class ComponentManager {
    init {
        // スクロール可能 影付き テキストビュー
        val scrollShadowTextView = ScrollDecorator(ShadowDecorator(TextView()))
        // スクロール可能 テキストビュー
        val scrollTextView = ScrollDecorator(TextView())

        // 描画
        scrollShadowTextView.draw()
        print("\n")
        scrollTextView.draw()
    }
}

実行すると以下のようになります。

[output]
【テキストビュー】:影付き:スクロール可能
【テキストビュー】:スクロール可能
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Gang of Four】デザインパターン学習 - Composite

複合

目次
クラスを木構造で組み立てる。とあるように、compositeとleafと呼ばれるオブジェクトで構成するパターンのようです。

目的

部分―全体階層を表現するために、オブジェクトを木構造に組み立てる。Composite パターンにより、クライアントは、個々のオブジェクトとオブジェクトを合成したものを一様に扱うことができるようになる。

構成要素

・Component 根、節、葉の抽象クラス
・Leaf 葉
・Composite 根または節
・Client 使用者

実装

木構造と聞くとディレクトリツリーが思い浮かびますね。
ということでディレクトリツリーを管理、出力できるプログラムを実装します。

directoryがcompositeでfileがleafですね。

Component 根、節、葉の抽象クラス
directoryとfileのインターフェース

Element.kt
package composite

interface Element {
    enum class ElementType(val type: String) {
        DIRECTORY("Direcotry"),
        FILE("File")
    }

    fun getType(): ElementType
    fun getName(): String
}

Composite 根または節
directory抽象クラス

elementListを持たせ、自分の配下にいるElementを管理できるようにします。

AbstractDirectory.kt
package composite

abstract class AbstractDirectory: Element {
    var elementList: MutableList<Element> = mutableListOf()

    abstract fun addElement(element: Element)
}

directory具象クラス

Directory.kt
package composite

class Directory(private val name: String): AbstractDirectory() {

    override fun getType(): Element.ElementType {
        return Element.ElementType.DIRECTORY
    }

    override fun addElement(element: Element) {
        elementList.add(element)
    }

    override fun getName(): String {
        return name
    }
}

Leaf 葉
file具象クラス

File.kt
package composite

class File(private val name: String): Element {

    override fun getType(): Element.ElementType {
        return Element.ElementType.FILE
    }

    override fun getName(): String {
        return name
    }
}

Client 使用者
ディレクトリツリーを管理する人

DirectoryManager.kt
package composite

class DirectoryManager {
    init {
        // ルートフォルダ
        val rootDirectory = Directory("/")
        rootDirectory.addElement(File("sample.config"))
        rootDirectory.addElement(File("gof.env"))

        // iOSアプリ用フォルダ
        val iosDirectory = Directory("iOS")

        val iosAppDirectory = Directory("GofiOSApp")
        iosAppDirectory.addElement(File("xcode.project"))

        val iosAppSourceDirectory = Directory("Source")
        iosAppSourceDirectory.addElement(File("hoge.swift"))
        iosAppSourceDirectory.addElement(File("fuga.swift"))

        iosDirectory.addElement(iosAppDirectory)
        iosAppDirectory.addElement(iosAppSourceDirectory)
        rootDirectory.addElement(iosDirectory)

        // Androidアプリ用フォルダ
        val androidDirectory = Directory("AndroidOS")

        val androidAppDirectory = Directory("GofAndroidApp")
        androidAppDirectory.addElement(File("AndroidManifest.xml"))

        val androidAppSourceDirectory = Directory("Source")
        androidAppSourceDirectory.addElement(File("moge.kt"))
        androidAppSourceDirectory.addElement(File("unbaba.kt"))

        androidDirectory.addElement(androidAppDirectory)
        androidAppDirectory.addElement(androidAppSourceDirectory)
        rootDirectory.addElement(androidDirectory)

        show(rootDirectory, 0)
    }

    private fun show(element: Element, indent: Int) {

        if (element is Directory) {
            println("${"----".repeat(indent)}【${element.getType().type}】${element.getName()}")
            element.elementList.forEach {
                show(it, indent + 1)
            }
        } else {
            println("${"----".repeat(indent)}【${element.getType().type}】${element.getName()}")
        }
    }
}

以上でcompositeパターンを用いたディレクトリツリーの完成です。

[output]
【Direcotry】/
----【File】sample.config
----【File】gof.env
----【Direcotry】iOS
--------【Direcotry】GofiOSApp
------------【File】xcode.project
------------【Direcotry】Source
----------------【File】hoge.swift
----------------【File】fuga.swift
----【Direcotry】AndroidOS
--------【Direcotry】GofAndroidApp
------------【File】AndroidManifest.xml
------------【Direcotry】Source
----------------【File】moge.kt
----------------【File】unbaba.kt

/iOS/GofIosApp/Sourceフォルダを同じ階層へ複製したくなった場合、iosAppDirectory.addElement(iosAppSourceDirectory)を追加します。

[output]
【Direcotry】/
----【File】sample.config
----【File】gof.env
----【Direcotry】iOS
--------【Direcotry】GofiOSApp
------------【File】xcode.project
------------【Direcotry】Source
----------------【File】hoge.swift
----------------【File】fuga.swift
------------【Direcotry】Source
----------------【File】hoge.swift
----------------【File】fuga.swift
略

ディレクトリを自由自在に操れてます。
kotlinは文字列操作なんかも簡潔に書けていいですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Gang of Four】デザインパターン学習 - Bridge

目次
通常、抽出できる部分を抽象クラスとし、それ以外の部分を抽象クラスを継承した具象クラスで実装します。

抽象クラスと具象クラスを分離し抽象クラスの実装を実行時に決定できるようにするのが本パターンです。

アイコンに関する情報を管理するアイコンクラスを例とします。
最初にモノクロアイコンクラス(抽象クラス)とモノクロアイコンを様々な大きさで表示させるための大モノクロアイコンクラス(具象クラス)中モノクロアイコンクラス(具象クラス)小モノクロアイコンクラス(具象クラス)を実装します。
しかしクライアントからの予定外の仕様変更により、途中でRGBを自由に設定できるカラーアイコンクラス(抽象クラス)を実装しなければならなくなりました。もちろんアイコンはモノクロアイコンと同様大、中、小の3種類を表示する仕様も満たしていなければいけません。

抽象クラスと具象クラスが継承により永続的に結合されているとカラーアイコンクラスを継承した大カラーアイコンクラス中カラーアイコンクラスを…略

といったようにカラーアイコンクラスを継承した全ての大~小アイコンを作成しなければならなくなります。さらにクリアアイコンを追加するときはまた同様に…略

DBで多対多のリレーショナルがあるテーブルを、中間テーブルを作成して正規化するのに近い考え方かな?

目的

抽出されたクラスと実装を分離して、それらを独立に変更できるようにする。

構成要素

・Abstraction 抽象クラス
・RefinedAbstraction Abstractionクラスを拡張したクラス
・Implementor 具象クラス
・ConcreteImplementor Implementorクラスを拡張したクラス

実装

まず抽象クラスのモノクロアイコンクラスとカラーアイコンクラスを実装します。

Abstraction 抽象クラス
抽象アイコンクラス
このクラスが所謂本パターンの肝、抽象クラスと具象クラスのになります。

AbstIcon.kt
package bridge

abstract class AbstIcon(iconType: IconType) {
    enum class IconType(val value: String) {
        BlackAndWhite("モノクロアイコン"),
        Color("カラーアイコン")
    }

    private var bigIcon: ImpIcon = BigIcon(iconType)
    private var middleIcon: ImpIcon = MiddleIcon(iconType)
    private var smallIcon: ImpIcon = SmallIcon(iconType)

    abstract fun getType(): String

    fun getBigIcon(): ImpIcon {
        return bigIcon
    }

    fun getMiddleIcon(): ImpIcon {
        return middleIcon
    }

    fun getSmallIcon(): ImpIcon {
        return smallIcon
    }
}

RefinedAbstraction Abstractionクラスを拡張したクラス
モノクロアイコン抽象クラス

AbstBlackAndWhiteIcon.kt
package bridge

class AbstBlackAndWhiteIcon: AbstIcon(IconType.BlackAndWhite) {
    override fun getType(): String {
        return AbstIcon.IconType.BlackAndWhite.value
    }

    // モノクロアイコン独自処理色々
}

カラーアイコン抽象クラス

AbstColorIcon.kt
package bridge

class AbstColorIcon: AbstIcon(IconType.Color) {

    override fun getType(): String {
        return AbstIcon.IconType.Color.value
    }

    // カラーアイコン独自処理色々
}

続いて具象クラスである大アイコン、中アイコン、小アイコンの作成

Implementor 具象クラス
具象アイコンインターフェース

ImpIcon.kt
package bridge

interface ImpIcon {
    enum class IconSize(val value: String) {
        Big("大アイコン"),
        Middle("中アイコン"),
        Small("小アイコン")
    }

    fun getIcon(): String
}

ConcreteImplementor Implementorクラスを拡張したクラス
大、中、小アイコンクラス

BigIcon.kt
package bridge

class BigIcon(iconType: AbstIcon.IconType): ImpIcon {

    private val iconType = iconType

    override fun getIcon(): String {
        return "【タイプ】:" + iconType.value + "【サイズ】:" + ImpIcon.IconSize.Big.value
    }

}
MiddleIcon.kt
package bridge

class MiddleIcon(iconType: AbstIcon.IconType): ImpIcon {

    private val iconType = iconType

    override fun getIcon(): String {
        return "【タイプ】:" + iconType.value + "【サイズ】:" + ImpIcon.IconSize.Middle.value
    }
}
SmallIcon.kt
package bridge

class SmallIcon(iconType: AbstIcon.IconType): ImpIcon {

    private val iconType = iconType

    override fun getIcon(): String {
        return "【タイプ】:" + iconType.value + "【サイズ】:" + ImpIcon.IconSize.Small.value
    }
}

各アイコンを使う人

Client.kt
package bridge

class Client {
    init {
        val colorIcon = AbstColorIcon()
        println(colorIcon.getType())
        println(colorIcon.getBigIcon().getIcon())
        println(colorIcon.getMiddleIcon().getIcon())
        println(colorIcon.getSmallIcon().getIcon())

        val blackAndWhiteIcon = AbstBlackAndWhiteIcon()
        println(blackAndWhiteIcon.getType())
        println(blackAndWhiteIcon.getBigIcon().getIcon())
        println(blackAndWhiteIcon.getMiddleIcon().getIcon())
        println(blackAndWhiteIcon.getSmallIcon().getIcon())
    }
}

以上でアイコンを用いたサンプルコードの実装が完了です。

[output]
カラーアイコン
【タイプ】:カラーアイコン【サイズ】:大アイコン
【タイプ】:カラーアイコン【サイズ】:中アイコン
【タイプ】:カラーアイコン【サイズ】:小アイコン
モノクロアイコン
【タイプ】:モノクロアイコン【サイズ】:大アイコン
【タイプ】:モノクロアイコン【サイズ】:中アイコン
【タイプ】:モノクロアイコン【サイズ】:小アイコン

ここで背景が透明なクリアアイコンを追加してみます。従来の方法では大クリアアイ…略ですが、本パターンではAbstIconクラスを継承したAbstClearIconクラスを実装するだけで実現できるようになっています。

AbstClearIcon.kt
package bridge

class AbstClearIcon: AbstIcon(IconType.Clear) {

    override fun getType(): String {
        return AbstIcon.IconType.Clear.value
    }

    // クリアアイコン独自処理色々
}

Clientクラスに下記コードを追加したあと再び実行してみます。

Client.kt
    val clearIcon = AbstClearIcon()
    println(clearIcon.getType())
    println(clearIcon.getBigIcon().getIcon())
    println(clearIcon.getMiddleIcon().getIcon())
    println(clearIcon.getSmallIcon().getIcon())
[output]
カラーアイコン
【タイプ】:カラーアイコン【サイズ】:大アイコン
【タイプ】:カラーアイコン【サイズ】:中アイコン
【タイプ】:カラーアイコン【サイズ】:小アイコン
モノクロアイコン
【タイプ】:モノクロアイコン【サイズ】:大アイコン
【タイプ】:モノクロアイコン【サイズ】:中アイコン
【タイプ】:モノクロアイコン【サイズ】:小アイコン
クリアアイコン
【タイプ】:クリアアイコン【サイズ】:大アイコン
【タイプ】:クリアアイコン【サイズ】:中アイコン
【タイプ】:クリアアイコン【サイズ】:小アイコン

簡単に拡張することができました。

これで抽象クラス側がいくら増えても、具象クラス側がいくら増えても用意に実装することが可能になりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Gang of Four】デザインパターン学習 - Adapter

適合させる人[物]

目次

目的

あるクラスのインタフェースをクライアントが求める他のインタフェースへ変換する。Adapterパターンはインタフェースに互換性のないクラス同士を組み合わせることができるようにする。

構成要素

・Target 組み合わせたあとのクラスのインターフェースを定義する。
・Client 利用者クラス
・Adaptee 組み合わせたいクラス
・Adapter Adapteeクラスを組み合わせて作成したクラス

実装

目的を読んで「そんなことできるがやろか」と思っていたらあるクラスに多重継承させて色々なクラスをまとめるような感じでした。GoF本のサンプルコードは基本的にC++で記述されているため、kotlin(もといjava)では実現できないようです。構造に関するパターンの一発目なのに。

色々調べてみるとjavaでadapterパターンを実現している(しようとしている?)ページが幾つかありました。
is-a関係またはhas-a関係の二つで実現することができる。とどのページでも書かれていましたがそれは本当にadapterパターンなのか…?と思います。

例えばTextViewクラスRectクラスColorBackgroundクラスというのが標準ツールキットに含まれているけれど、今から作るシステムには角を丸くできて背景色を変更できるテキストビューが必要なんだ!!!というようなときに本パターンを用いるもんだと私は認識しています。

もしjavaで実現できるならば、下記のようなクラスができます。
※kotlinだとextendsとimplementsの見分けがつきにくいのでjavaでコーディングします。

ColorRectTextView.java
public class ColorRectTextView extends TextView, Rect, ColorBackground {
    // 角丸にしたり、背景色変更したりするメソッド
}

がしかし、javaは多重継承を許しません。これをis-a関係has-a関係で実現するとなると

IsAColorRectTextView.java
public class IsAColorRectTextView extends TextView implements RectInterface, ColorBackgroundInterface {
    // RectInterfaceとColorBackgroundInterfaceで定義されたメソッドを実装する
}

のような形や

HasAColorRectTextView.java
public class HasAColorRectTextView extends TextView {
    private Rect rect;
    private ColorBackground colorBackGround;

    public HasAColorRectTextView(Rect rect, ColorBackground colorBackGround) {
        this.rect = rect;
        this.colorBackGround = colorBackGround;
    }

    public void rectMethod() {
        rect.rectMethod()
    }

    public void colorBackGroundMethod() {
        colorBackGround.colorBackGroundMethod()
    }
}

のような形になります。果たしてこれをデザインパターンと呼べるのか…?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Gang of Four】デザインパターン学習 - Singleton

1枚札

目次
生成に関するパターンの最後はシングルトンです。これは誰にとっても馴染みのあるパターンかと思います。自分も一番最初に知ったデザインパターンはシングルトンだったような気がする。

DB接続を提供するクラスやアセットを取得するようなクラスは大体このパターンで実装されていますね。プログラム中で当該クラスのインスタンスの唯一性を保証します。

目的

あるクラスに対してインスタンスが1つしか存在しないことを保証し、それにアクセスするためのグローバルな方法を提供する。

構成要素

・Singleton インスタンスが1つしか生成されないクラス

実装

本パターンはkotlinでコーディングすると下記のように鬼シンプルな形になってしまいよくわからないため、javaでコーディングします。

SingletonObject.kt
package singleton

object SingletonObject {
    var str = ""

    fun print() {
        println("出力結果:$str")
    }
}

複数の画面からシングルトンオブジェクトを介して画像や音楽ファイルを作成します。

Singleton インスタンスが1つしか生成されないクラス
シングルトンオブジェクト

JavaSingletonObject.java
package singleton;

public class JavaSingletonObject {
    /**
     * staticで定義し自クラスのインスタンスを唯一とする
     */
    public static JavaSingletonObject instance = new JavaSingletonObject();
    private String address = "/assets/temp";
    enum Assets {
        IMAGE1("image_1"),
        IMAGE2("image_2"),
        AUDIO1("audio_1"),
        AUDIO2("audio_2");

        private final String value;

        Assets(final String value) {
            this.value = value;
        }
    }

    /**
     * コンストラクタをprivateで定義し、新たなインスタンスを生成できないようにする。
     */
    private JavaSingletonObject(){
        System.out.println("接続:" + address);
    }

    public String getAssets(Assets target) {
        System.out.println("シングルトンオブジェクトで資材取得:" + target.value);
        return target.value;
    }
}

画面1クラス

DisplayOne.kt
package singleton

class DisplayOne {
    fun draw() {
        println("User1で${JavaSingletonObject.instance.getAssets(JavaSingletonObject.Assets.IMAGE1)}を描画!!")
    }
}

画面2クラス

DisplayTwo.kt
package singleton

class DisplayTwo {
    fun start() {
        println("User2で${JavaSingletonObject.instance.getAssets(JavaSingletonObject.Assets.AUDIO1)}を再生!!")
    }
}

使用者
画面管理クラス

Manager.kt
package singleton

class Manager {
    init {
        DisplayOne().draw()
        DisplayTwo().start()
    }
}

以上でシングルトンパターンの実装が完了しました。JavaSingletonObjectクラスのコンストラクタでassetsへの接続を確立しているため、本処理は1回のみ実行されることになっています。

またコンストラクタをprivateで定義しているため当該クラスのインスタンスを新たに生成することができなくなり唯一性が保証されます。

[output]
接続:/assets/temp
シングルトンオブジェクトで資材取得:image_1
User1でimage_1を描画!!
シングルトンオブジェクトで資材取得:audio_1
User2でaudio_1を再生!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Gang of Four】デザインパターン学習 - Prototype

試作品

目次
部品をプールし、クローンすることでオブジェクトを生成するパターンです。

目的

生成すべきオブジェクトの種類を原型となるインスタンスを使って明確にし、それをコピーすることで新たなオブジェクトの生成を行う。

構成要素

・Prototype 複製を行う抽象クラス
・ConcretePrototype 複製を行う具象クラス
・Client 複製を依頼するクラス

実装

車を量産するために必要な試作品工場を実装します。

まずは試作品工場が作る部品から実装します。

エンジンクラス

Engine.kt
package prototype

class Engine(displacement: Int): Cloneable {
    var displacement = displacement

    fun show() {
        print("【エンジン】"+ displacement +"cc ")
    }

    public override fun clone(): Any {
        return super.clone()
    }
}

タイヤクラス

Tire.kt
package prototype

class Tire(num: Int): Cloneable {
    var num = num

    fun show() {
        print("【タイヤ】" + num + "個 ")
    }

    public override fun clone(): Any {
        return super.clone()
    }
}

以上が部品です。続いて製品の車を実装します。

車クラス

Car.kt
package prototype

class Car(engine: Engine, tire: Tire) {
    val engine = engine
    val tire = tire

    fun show() {
        print("【製品】車 ")
        engine.show()
        tire.show()
        print("\n")
    }
}

今まで実装したEngineとTireを次々量産できる工場を実装します。

Prototype 複製を行う抽象クラス
試作品インターフェース

ProtoType.kt
package prototype

/**
 * 試作品
 * Prototype
 */
interface ProtoType {
    fun createEngine(): Engine
    fun createTire(): Tire
}

ConcretePrototype 複製を行う具象クラス
試作品工場クラス

ProtoTypeFactory.kt
package prototype

class ProtoTypeFactory: ProtoType {
    var engine = Engine(0)
    var tire = Tire(4)

    override fun createEngine(): Engine {
        return engine.clone() as Engine
    }

    override fun createTire(): Tire {
        return tire.clone() as Tire
    }
}

最後に試作品工場から部品を受け取り製品を量産する工場を実装します。

Client 複製を依頼するクラス
車製造工場クラス

Factory.kt
package prototype

class Factory {
    var carList:MutableList<Car> = mutableListOf()
    var prototypeFactory = ProtoTypeFactory()
    init {

        // 排気量を1000ccに設定
        prototypeFactory.engine.displacement = 1000
        // 排気量1000ccの車を3台量産する
        massProduction(3)

        // 排気量を2000ccに設定
        prototypeFactory.engine.displacement = 2000
        // 排気量2000ccの車を2台量産する
        massProduction(2)

        // 排気量を12000ccに設定
        prototypeFactory.engine.displacement = 12000
        // タイヤを8本に設定
        prototypeFactory.tire.num = 8
        // 排気量12000cc タイヤ8本の車を5台量産する バス?
        massProduction(5)

        for (car in carList) {
            car.show()
        }
    }

    private fun massProduction(num: Int) {
        for (i in 0..num) {
            carList.add(Car(prototypeFactory.createEngine(), prototypeFactory.createTire()))
        }
    }
}

以上で試作品を次々量産できる工場が実装できました。Abstract Factoryパターンとは異なり、いろいろな車種を量産したくなってもProtoTypeFactoryクラスが持つ部品のメンバを変更すれば新たな工場を実装せずに済みます。

[output]
【製品】車 【エンジン】1000cc 【タイヤ】4個 
【製品】車 【エンジン】1000cc 【タイヤ】4個 
【製品】車 【エンジン】1000cc 【タイヤ】4個 
【製品】車 【エンジン】1000cc 【タイヤ】4個 
【製品】車 【エンジン】2000cc 【タイヤ】4個 
【製品】車 【エンジン】2000cc 【タイヤ】4個 
【製品】車 【エンジン】2000cc 【タイヤ】4個 
【製品】車 【エンジン】12000cc 【タイヤ】8個 
【製品】車 【エンジン】12000cc 【タイヤ】8個 
【製品】車 【エンジン】12000cc 【タイヤ】8個 
【製品】車 【エンジン】12000cc 【タイヤ】8個 
【製品】車 【エンジン】12000cc 【タイヤ】8個 
【製品】車 【エンジン】12000cc 【タイヤ】8個 
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Gang of Four】デザインパターン学習 - Factory Method

工場の方法

目次
Abstract Factoryパターンの具象工場クラスの部分のみが本パターンに該当すると理解しています。

目的

オブジェクトを生成するときのインタフェースだけを規定して、実際にどのクラスをインスタンス化するかはサブクラスが決めるようにする。Factory Methodパターンは、インスタンス化をサブクラスに任せる。

構成要素

・Product 生成される部品の抽象クラス
・ConcreteProduct 生成される部品
・Creator 部品製造工場の抽象クラス
・ConcreteCreator 部品製造工場

実装

プロスポーツ選手に主催者が宣誓してもらうようなプログラムを実装します。宣誓を行う代表選手はそれぞれのスポーツ連盟により派遣されます。

Product 生成される部品の抽象クラス
プロスポーツ選手抽象クラス

ProfessionalAthlete.kt
package factorymethod

abstract class ProfessionalAthlete(type: Type) {
    enum class Type(val value: String) {
        Soccer("サッカー"),
        BaseBall("野球")
    }

    val myType = type

    abstract fun athletesOath()
}

ConcreteProduct 生成される部品
プロサッカー選手具象クラス

ProfessionalSoccerAthlete.kt
package factorymethod

class ProfessionalSoccerAthlete(name: String): ProfessionalAthlete(Type.Soccer) {

    private val name = name

    override fun athletesOath() {
        println("宣誓!私は" + name + "です。プロ【" + myType.value + "】選手としてサッカーボールを使い正々堂々プレーします!")
    }
}

プロ野球選手具象クラス

ProfessionalBaseBallAthlete.kt
package factorymethod

class ProfessionalBaseBallAthlete(name: String): ProfessionalAthlete(Type.BaseBall) {

    private val name = name

    override fun athletesOath() {
        println("宣誓!私は" + name + "です。プロ【" + myType.value + "】選手としてバットと野球ボールを使い正々堂々プレーします!")
    }
}

以上で選手(product)が実装できました。次は選手(product)を派遣(create)するスポーツ連盟(factory)を実装していきます。

Creator 部品製造工場の抽象クラス
スポーツ連盟抽象クラス

SportsFederation.kt
package factorymethod

abstract class SportsFederation {
    fun getAthlete(): ProfessionalAthlete {
        return dispatchRepresentativeAthlete()
    }

    protected abstract fun dispatchRepresentativeAthlete(): ProfessionalAthlete
}

ConcreteCreator 部品製造工場
サッカー連盟具象クラス

SoccerFederation.kt
package factorymethod

class SoccerFederation: SportsFederation() {
    override fun dispatchRepresentativeAthlete(): ProfessionalAthlete {
        return ProfessionalSoccerAthlete("サカ田 代表太郎")
    }
}

野球連盟具象クラス

BaseBallFederation.kt
package factorymethod

class BaseBallFederation: SportsFederation() {
    override fun dispatchRepresentativeAthlete(): ProfessionalAthlete {
        return ProfessionalBaseBallAthlete("野球本 キャプテン男")
    }
}

最後に各スポーツ連盟(factory)に代表選手(product)を要求し、宣誓させる主催者を実装します。

使用者
主催者クラス

Organizer.kt
package factorymethod

class Organizer {
    init {
        var athleteList: MutableList<ProfessionalAthlete> = mutableListOf()
        // それぞれのスポーツ連盟から代表選手を派遣してもらう
        // サッカー連盟から代表選手取得
        athleteList.add(SoccerFederation().getAthlete())
        // 野球連盟から代表選手取得
        athleteList.add(BaseBallFederation().getAthlete())

        // 宣誓してもらう
        for (athlete in athleteList) {
            athlete.athletesOath()
        }
    }
}

以上で生成しなければならないオブジェクトのクラスを事前に知ることができない場合に用いることができるパターンができました。ような気がします。

[output]
宣誓!私はサカ田 代表太郎です。プロ【サッカー】選手としてサッカーボールを使い正々堂々プレーします!
宣誓!私は野球本 キャプテン男です。プロ【野球】選手としてバットと野球ボールを使い正々堂々プレーします!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Gang of Four】デザインパターン学習 - Builder

建築業者

目次
Abstract Factoryがその名の通り工場であり各部品を製造する機能を提供するならば、本パターンは部品を組み合わせた製品(複合オブジェクト)量産するようなイメージながかな?

目的

複合オブジェクトについて、その作成過程を表現形式に依存しないものにすることにより、同じ作成過程で異なる表現形式のオブジェクトを生成できるようにする。

構成要素

・Builder Productクラスを生成するための抽象クラス
・ConcreteBuilder 具象Builderクラス。Productクラスを生成する
・Director Builderクラスのインタフェースを使ってオブジェクトを生成する
・Product 多くの構成要素からなる複合オブジェクト

実装

下記のようなフローでクリエイターが街づくりを行います。

専門の建築業者へ建てたい建築物を依頼 -> 建物のフロアの数、部屋の数を決める -> 希望する数だけ建ててもらう

Builder Productクラスを生成するための抽象クラス
建築業者インターフェース

Builder.kt
package builder

interface Builder {
    enum class ProductType(val value: String) {
        ArtMuseum("美術館"),
        Museum("博物館"),
        MovieTheater("映画館")
    }

    fun getProduct(): Product
    fun addFloor(floorNum: Int)
    fun addRoom(targetFloor: Int, roomNo: Int)

}

ConcreteBuilder 具象Builderクラス。Productクラスを生成する
博物館専門建築業者具象クラス

MuseumBuilder.kt
package builder

class MuseumBuilder(productName: String): Builder {
    private var product = Product(Builder.ProductType.Museum, productName)

    override fun getProduct(): Product {
        product.countUpProductNumber()
        return product.clone() as Product
    }

    override fun addFloor(floorNum: Int) {
        if (product.floorList.asSequence().filter { floor -> floor.floorNum == floorNum }.count() == 0) {
            product.floorList.add(Floor(floorNum))
        }
    }

    override fun addRoom(targetFloor: Int, roomNo: Int) {
        val floor= product.floorList.filter { floor -> floor.floorNum== targetFloor }
        if (floor.count() > 0) {
            floor[0].addRoom(roomNo)
        }
    }

}

美術館専門建築業者具象クラス

ArtMuseumBuilder.kt
package builder

class ArtMuseumBuilder(productName: String) : Builder {
    private var product = Product(Builder.ProductType.ArtMuseum, productName)

    override fun getProduct(): Product {
        product.countUpProductNumber()
        return product.clone() as Product
    }

    override fun addFloor(floorNum: Int) {
        if (product.floorList.asSequence().filter { floor -> floor.floorNum == floorNum }.count() == 0) {
            product.floorList.add(Floor(floorNum))
        }
    }

    override fun addRoom(targetFloor: Int, roomNo: Int) {
        val floor= product.floorList.filter { floor -> floor.floorNum== targetFloor }
        if (floor.count() > 0) {
            floor[0].addRoom(roomNo)
        }
    }

}

映画館専門建築業者具象クラス

MovieTheaterBuilder.kt
package builder

class MovieTheaterBuilder(productName: String): Builder {
    private var product = Product(Builder.ProductType.MovieTheater, productName)

    override fun getProduct(): Product {
        product.countUpProductNumber()
        return product.clone() as Product
    }

    override fun addFloor(floorNum: Int) {
        if (product.floorList.asSequence().filter { floor -> floor.floorNum == floorNum }.count() == 0) {
            product.floorList.add(Floor(floorNum))
        }
    }

    override fun addRoom(targetFloor: Int, roomNo: Int) {
        val floor= product.floorList.filter { floor -> floor.floorNum== targetFloor }
        if (floor.count() > 0) {
            floor[0].addRoom(roomNo)
        }
    }

}

建物~部屋までは読み飛ばしてもらって結構です。
Product 多くの構成要素からなる複合オブジェクト
建物クラス

Product.kt
package builder

class Product(productType: Builder.ProductType, productName: String): Cloneable {

    var productType = productType
    val floorList: MutableList<Floor> = mutableListOf()
    var productName = productName
    var productNumber = 0

    fun countUpProductNumber() {
        productNumber++
    }

    fun show(): String {
        var ret = "【建物】${productType.value}:$productName$productNumber 棟目"
        for (floor in floorList) {
            ret += floor.show()
        }
        return ret
    }

    public override fun clone(): Any {
        return super.clone()
    }

}

階層クラス 1階 2階...

Floor.kt
package builder

class Floor(floorNum: Int) {
    var floorNum = floorNum
    private val roomList:MutableList<Room> = mutableListOf()

    fun addRoom(roomNo: Int) {
        if (roomList.asSequence().filter { room -> room.roomNo == roomNo }.count() == 0) {
            roomList.add(Room(roomNo))
        }
    }

    fun show(): String {
        var ret: String = "【階層】$floorNum 階 "
        for (room in roomList) {
            ret += room.show()
        }
        return ret
    }
}

部屋クラス

Room.kt
package builder

class Room(roomNo: Int) {
    val roomNo = roomNo

    fun show(): String {
        return "【部屋】 $roomNo 号室"
    }
}

Director Builderクラスのインタフェースを使ってオブジェクトを生成する
街を作る人クラス

Creator.kt
package builder

class Creator {
    init {
        var productList:MutableList<Product> = mutableListOf()

        // 西洋美術館の建築を美術館専門建築業者に依頼する
        var artMuseumBuilder1 = ArtMuseumBuilder("西洋美術館").apply {
            addFloor(1)
            addFloor(2)
            addFloor(3)
            addRoom(1, 101)
            addRoom(1, 102)
            addRoom(2, 201)
            addRoom(3, 301)
        }
        // 東洋美術館の建築を美術館専門建築業者に依頼する
        var artMuseumBuilder2 = ArtMuseumBuilder("東洋美術館").apply {
            addFloor(1)
            addFloor(2)
            addRoom(1, 101)
            addRoom(2, 201)
        }

        // 国立博物館の建築を博物館専門建築業者に依頼する
        var museumBuilder = MuseumBuilder("国立博物館").apply {
            addFloor(1)
            addRoom(1, 101)
        }
        // ホーゲーシネマズの建築を映画館専門建築業者に依頼する
        var movieTheaterBuilder = MovieTheaterBuilder("HOGEシネマズ").apply {
            addFloor(1)
            addRoom(1, 101)
        }

        // 街に建築するリスト作成
        productList.add(artMuseumBuilder1.getProduct())
        productList.add(artMuseumBuilder1.getProduct())
        productList.add(artMuseumBuilder1.getProduct())
        productList.add(artMuseumBuilder2.getProduct())
        productList.add(artMuseumBuilder2.getProduct())
        productList.add(museumBuilder.getProduct())
        productList.add(movieTheaterBuilder.getProduct())

        for (product in productList) {
            println(product.show())
        }

    }
}

クリエイターはどんな建物を街に建てたいかを考え、それぞれの建築業者へ依頼し、必要な分だけ建ててもらいます。
コンビニとかにしたほうがわかりやすかったろうか...

あんまり作りこんでもしょうがないので簡潔な構成にしていますが、住所プロパティなんかをProductクラスに保持させ、建ててもらうgetProduct()たびに住所プロパティを設定できるようにすればよりイメージしやすかったか?

以上で製品(複合オブジェクト)量産するという目的は達成できました。
街作りの途中で映画館をもう一軒追加したい!場合もmovieTheaterBuilder.getProduct()を呼べば同じ建物を複製(量産)することができます。各Builder#getProduct()メソッドのreturn product.clone() as Productがいい仕事してますね。

[output]
【建物】美術館:西洋美術館1 棟目【階層】1 階 【部屋】 101 号室【部屋】 102 号室【階層】2 階 【部屋】 201 号室【階層】3 階 【部屋】 301 号室
【建物】美術館:西洋美術館2 棟目【階層】1 階 【部屋】 101 号室【部屋】 102 号室【階層】2 階 【部屋】 201 号室【階層】3 階 【部屋】 301 号室
【建物】美術館:西洋美術館3 棟目【階層】1 階 【部屋】 101 号室【部屋】 102 号室【階層】2 階 【部屋】 201 号室【階層】3 階 【部屋】 301 号室
【建物】美術館:東洋美術館1 棟目【階層】1 階 【部屋】 101 号室【階層】2 階 【部屋】 201 号室
【建物】美術館:東洋美術館2 棟目【階層】1 階 【部屋】 101 号室【階層】2 階 【部屋】 201 号室
【建物】博物館:国立博物館1 棟目【階層】1 階 【部屋】 101 号室
【建物】映画館:HOGEシネマズ1 棟目【階層】1 階 【部屋】 101 号室

もし、product.clone()していなければ下記のような結果になり、何の役にも立たない建築業者ができあがります。

[output]
【建物】美術館:西洋美術館3 棟目 ...
【建物】美術館:西洋美術館3 棟目 ...
【建物】美術館:西洋美術館3 棟目 ...
【建物】美術館:東洋美術館2 棟目 ...
【建物】美術館:東洋美術館2 棟目 ...
【建物】博物館:国立博物館1 棟目 ...
【建物】映画館:HOGEシネマズ1  ...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

if文のelse要否について

else句を利用せずとも問題ない?

private static final String MISOSHIRU = "misoshiru";

public String isJapaneseFood(String food) {

  if (food.equals(MISOSHIRU)) {
    return "日本食なのだー";
  }

  return "日本食じゃないのだー";
}

例えば、味噌汁かどうかを判定する際のコードは上記のようにかけますが、

「味噌汁が日本食であるかどうか?」

の答えは、「日本食なのだー」, 「日本食じゃないのだー」 の2択でありどちらの答えも
同じウエイトで、正常な答えのため下記のように書いた方が読みやすかったりします。

public String isJapaneseFood(String food) {

  if (food.equals(MISOSHIRU)) {
    return "日本食なのだー";
  } else {
    return "日本食じゃないのだー";
  } 

}

if文でelse句の要否に迷った際は、下記を考えてみると良さそうです。

  • 条件判定は正常条件なのか
  • if・else句は同等のウエイトなのか

逆に条件判定がシンプルで答えが複数ある場合は、早期リターンの利用がおすすめです。

public String getEvaluation(int point) {
  if (point > 90) {
    return "s";
  }
  if (point > 80) {
    return "a";
  }
  return "C";
}

マーチン ファウラー著書の、「リファクタリング―プログラムの体質改善テクニック」 には下記のように細かく説明されていました。コードを書く際の指針が欲しい方は一読してみると良いかもしれません。

条件記述は次の2つの形式に分類されます。
1つは、条件判定のいずれも正常条件であって、どちらのルートを取るかを判定するものです。
もう1つは、条件判定の結果の一方が正常条件で、それ以外は特殊条件である場合です。

もし、両方が正常処理ならば、ifとelseを持った条件記述を使うべきです。
特殊条件では、条件をチェックしてその結果が真のときにはリターンします。

if-then-else構造が使われるときは、if部にもelse部にも同じウェイトが置かれています。
これが、プログラムの読み手に対して両方とも等しく起こり得ること、等しく重要であることを伝えます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Gang of Four】デザインパターン学習 - Abstract Factory

抽象工場

目次

オブジェクト指向の基本的な思想としてオブジェクト間の結合度が低いほうが良い、といったものがあります。
javaではinterfaceを用いることで、具象クラスを使う側が意識する必要がないようにできます。

インターフェース

Screen.java
interface Screen {
    public void view();
}

具象クラス

TopScreen.java
public class TopScreen implements Screen {
    public void view() {
        System.out.println("トップ画面");
    }
}
LoginScreen.java
public class LoginScreen implements Screen {
    public void view() {
        System.out.println("ログイン画面");
    }
}

使う側

Client.java
public class Client {
    public void displayTopScreen() {
        Screen top = new TopScreen()
        top.view();
    }

    public void displayLoginScreen() {
        Screen login = new LoginScreen()
        login.view();
    }
}

一見なんの問題もないように見えますが、各クラスをインスタンス化する部分であるScreen top = new TopScreen()Screen login = new LoginScreen()において、どうしても具象クラスを意識する必要がでてきます。
これは結合度が低いほうが良いという思想に反することになるうえ、使用しているコンストラクタを修正すればインスタンス化している箇所をすべて修正しなければならなくなります。

これをfactoryクラスというインスタンスを生成してくれるクラスを作成することによって解決しますが、そのfactoryクラスも抽象クラスと具象クラスに分けることで、より柔軟なオブジェクト生成工場を作ろうというのが本パターンです。

目的

互いに関連したり依存し合うオブジェクト群を、その具象クラスを明確にせずに生成するためのインタフェースを提供する。

構成要素

・AbstractFactory AbstractProductクラスを生成するインターフェースを定義する
・ConcreteFactory ConcreteProductクラスを生成するインターフェースを定義する
・AbstractProduct 生成される部品の共通部分を抽出した抽象クラス
・ConcreteProduct 生成される部品
・Client 利用者

実装

ではサンプルコードです。
車に必要な部品を製造する工場を実装し、下記のようなフローでディーラーへ車両を陳列します。

ディーラーがメーカーへ車両を要求 -> メーカーが工場へ車用部品を要求 -> 工場がメーカーへ各種部品を返却 -> メーカーが車を組み立ててディーラーへ返却 -> ディーラーに車両が陳列される。

AbstractFactory AbstractProductクラスを生成するインターフェースを定義する
車用部品製造工場クラス

CarFactory.kt
package abstractfactory

open class CarFactory {

    open fun makeEngine(displacement: Int): Engine {
        return Engine(displacement)
    }

    open fun makeTire(position: Int): Tire {
        return Tire(position)
    }

    open fun makeSteering(weight: Int): Steering {
        return Steering(weight)
    }

}

AbstractProduct 生成される部品の共通部分を抽出した抽象クラス
エンジンクラス

Engine.kt
package abstractfactory

open class Engine(displacement: Int) {
    open val type = "普通のエンジン"
    val displacement = displacement
}

タイヤクラス

Tire.kt
package abstractfactory

open class Tire(position: Int) {
    open val type = "普通のタイヤ"
    val position = position
}

ハンドルクラス

Steering.kt
package abstractfactory

class Steering(weight: Int) {
    val type = "普通のハンドル"
    val weight = weight
}

車クラス

Car.kt
package abstractfactory

open class Car(type: String, engine: Engine, tire: Tire, steering: Steering) {
    val type = type
    val engine = engine
    val tire = tire
    val steering = steering

    fun getConstitution(): String {
        return  "【車種】:" + type + "," +
                "【エンジン】:" + engine.type + "," +
                "【タイヤ】:" + tire.type + "," +
                "【ハンドル】:" + steering.type
    }
}

Client 利用者
自動車メーカークラス

Maker.kt
package abstractfactory

class Maker {

    /**
     * 普通の車製造
     */
    fun getCar(): Car {
        return make("ファミリーカー", CarFactory())
    }

    /**
     * 製造
     */
    private fun make(type: String, factory: CarFactory): Car {
        val engine = factory.makeEngine(1000)
        val tire = factory.makeTire(1)
        val steering = factory.makeSteering(100)

        return Car(type, engine, tire, steering)
    }

}

自動車ディーラークラス

AutomobileDealer.kt
package abstractfactory

class AutomobileDealer {

    val cars = mutableListOf<Car>()

    init {
        openingUp()
    }

    /**
     * ディーラーにある車一覧
     */
    fun showCars() {
        cars.forEach {
            println(it.getConstitution())
        }
    }

    /**
     * 店舗開店
     */
    private fun openingUp() {
        val maker = Maker()
        cars.add(maker.getCar())
    }
}

AutomobileDealer#showCars()を呼び出せば、陳列されている車両(のスペック)が出力されます。

[output]
【車種】:ファミリーカー,【エンジン】:普通のエンジン,【タイヤ】:普通のタイヤ,【ハンドル】:普通のハンドル

以上で完成しましたが、突然社長が今までの製造ラインはそのままに、同様のラインでスーパーカー用部品も製造できる工場を作れといいだしました。

ConcreteFactory ConcreteProductクラスを生成するインターフェースを定義する
スーパーカー用部品製造工場クラス

SuperCarFactory.kt
package abstractfactory

class SuperCarFactory: CarFactory() {

    override fun makeEngine(displacement: Int): Engine {
        return HiPerformanceEngine(displacement)
    }

    override fun makeTire(position: Int): Tire {
        return HiGripTire(position)
    }
}

ConcreteProduct 生成される部品
高出力エンジンクラス

HiPerformanceEngine.kt
package abstractfactory

class HiPerformanceEngine(displacement: Int): Engine(displacement) {
    override val type = "高出力エンジン"
}

ハイグリップタイヤクラス

HiGripTire.kt
package abstractfactory

class HiGripTire(position: Int): Tire(position) {
    override val type = "ハイグリップタイヤ"
}

メーカーにスーパーカー製造メソッドを追加します。

Client 利用者
メーカークラス

Maker.kt
package abstractfactory

class Maker {

    /**
     * 車製造
     */
    fun getCar(): Car {
        return make(CarFactory())
    }

    /**
     * スーパーカー製造
     */
    fun getSuperCar(): Car {
        return make("スーパーカー", SuperCarFactory())
    }

    /**
     * 製造
     */
    private fun make(type: String, factory: CarFactory): Car {
        val engine = factory.makeEngine(1000)
        val tire = factory.makeTire(1)
        val steering = factory.makeSteering(100)

        return Car(type, engine, tire, steering)
    }

}

ディーラーは新たな車両(スーパーカー)をメーカーに要求します。

AutomobileDealer.kt
package abstractfactory

class AutomobileDealer {

    val cars = mutableListOf<Car>()

    init {
        openingUp()
    }

    /**
     * ディーラーにある車一覧
     */
    fun showCars() {
        cars.forEach {
            println(it.getConstitution())
        }
    }

    /**
     * 店舗開店
     */
    private fun openingUp() {
        val maker = Maker()
        cars.add(maker.getCar())
        cars.add(maker.getSuperCar())
        cars.add(maker.getSuperCar())
    }
}

AutomobileDealer#showCars()を呼び出せば、スーパーカーが新たに陳列されていることがわかります。

[output]
【車種】:ファミリーカー,【エンジン】:普通のエンジン,【タイヤ】:普通のタイヤ,【ハンドル】:普通のハンドル
【車種】:スーパーカー,【エンジン】:高出力エンジン,【タイヤ】:ハイグリップタイヤ,【ハンドル】:普通のハンドル
【車種】:スーパーカー,【エンジン】:高出力エンジン,【タイヤ】:ハイグリップタイヤ,【ハンドル】:普通のハンドル

以上で社長の要求に応じることができました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerコンテナ上でJavaプログラムを動かすときにLANG環境変数を設定すると日本語のファイル名が文字化けする問題

概要

CentOS の Docker コンテナ上で Java プログラムを動かしていたところ、日本語のファイル名を含むファイル一覧の取得で謎の文字化けが発生しました。

Sample.java
import java.io.*;

public class Sample {
   public static void main(String[] args) {
      // ファイル名が日本語のファイル「/sample/あいうえお.csv」を配置しておく
      new File("/sample").listFiles(new FilenameFilter() {
         public boolean accept(File dir, String name) {
            System.out.println(name);   // => ファイル一覧を取得すると日本語ファイル名が文字化けする
            return false;
         }
      });
   }
}

ちなみに、LANG環境変数を en_US.UTF-8 とした場合は文字化けが発生せず、 ja_JP.UTF-8 とした場合は文字化けが発生することが確認できています。

本記事では日本語ファイル名の文字化けの原因と対処方法について記載します。

原因と対処方法

まず、LANG環境変数へ ja_JP.UTF-8 を設定すると文字化けが発生する原因ですが、これは Docker の CentOS イメージに日本語ロケールが登録されていないため です。

LANG環境変数に指定可能なロケールについては locale -a コマンドから確認することができます。
CentOS イメージのコンテナ内でコマンドを実行して確認してみます。

# locale -a
C
POSIX
en_US.utf8

上記の通り、Docker の CentOS イメージのコンテナ内には日本語ロケールが含まれていません。
このコンテナ内で以下のようにLANG環境変数を指定してJavaプログラムからファイル一覧を取得しようとすると日本語ファイル名の文字化けが発生します。

LANG=ja_JP.UTF-8
export LANG

java Sample
=> 文字化けした日本語ファイル名.csv

対処方法として、 localedef コマンドを使用して 日本語ロケールを追加する ことで文字化けが解消します。
以下のコマンドを Dockerfile の RUN 命令として追加するかコンテナ内で実行します。

# localedef -f UTF-8 -i ja_JP ja_JP.UTF-8

もう一度 locale -a コマンドで指定可能なロケールを確認してみます。

# locale -a
C
POSIX
en_US.utf8
ja_JP.utf8

localedef コマンドによって ja_JP.utf8 が追加されました。
これでLANG環境変数を設定した場合も文字化けすることなく日本語のファイル名を扱えるようになります。

結論

  • Docker の CentOS イメージのコンテナ内には日本語ロケールが含まれていない
  • 日本語ロケールは localedef コマンドで追加できる
  • 指定できない(環境に存在しない)ロケールをLANG環境変数から指定するとJavaプログラムで日本語ファイル名が文字化けする
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

デザインパターン ~Observer~

1. はじめに

GoFのデザインパターンにおける、Observerパターンについてまとめます。

2. Observerパターンとは

  • Observerという英単語は、観察者という意味になります。
  • Observerパターンは、観察対象の状態が変化すると、観察者に対して通知が行われる方式です。
  • Observerパターンは、状態変化に応じた処理を記述するときに有効です。
  • GoFのデザインパターンでは、振る舞いに関するデザインパターンに分類されます。

3. サンプルクラス図

Observer.PNG

4. サンプルコード

4-1. Observerインターフェース

観察者を表すインターフェースです。

Observer.java
public interface Observer {
    public abstract void update(NumberGenerator generator);
}

4-2. DigitObserverクラス

数字で数を表す行うクラスです。Observerインターフェースを実装します。

DigitObserver.java
public class DigitObserver implements Observer {

    public void update(NumberGenerator generator) {
        System.out.println("DigitObserver:" + generator.getNumber());
    }
}

4-3. GraphObserverクラス

簡易グラフで数を表す行うクラスです。Observerインターフェースを実装します。

GraphObserver.java
public class GraphObserver implements Observer {

    public void update(NumberGenerator generator) {
        System.out.print("GraphObserver:");
        int count = generator.getNumber();
        for (int i = 0; i < count; i++) {
            System.out.print("*");
        }
        System.out.println("");
    }
}

4-4. NumberGeneratorクラス

数を生成するオブジェクトを表す抽象クラスです。

NumberGenerator.java
import java.util.ArrayList;
import java.util.Iterator;

public abstract class NumberGenerator {

    private ArrayList<Observer> observers = new ArrayList<Observer>();

    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    public void deleteObserver(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers() {
        Iterator it = observers.iterator();
        while (it.hasNext()) {
            Observer o = (Observer) it.next();
            o.update(this);
        }
    }

    public abstract int getNumber();
    public abstract void execute();
}

4-5. RandomNumberGeneratorクラス

ランダムに数を生成するクラスです。

RandomNumberGenerator.java
import java.util.Random;

public class RandomNumberGenerator extends NumberGenerator {

    private Random random = new Random();
    private int number;

    public int getNumber() {
        return number;
    }

    public void execute() {
        for (int i = 0; i < 10; i++) {
            number = random.nextInt(50);
            notifyObservers();
        }
    }
}

4-6. Mainクラス

メイン処理を行うクラスです。

Main.java
public class Main {
    public static void main(String[] args) {

        NumberGenerator generator = new RandomNumberGenerator();
        Observer observer1 = new DigitObserver();
        Observer observer2 = new GraphObserver();
        generator.addObserver(observer1);
        generator.addObserver(observer2);
        generator.execute();
    }
}

4-7. 実行結果

DigitObserver:35
GraphObserver:***********************************
DigitObserver:33
GraphObserver:*********************************
DigitObserver:40
GraphObserver:****************************************
DigitObserver:28
GraphObserver:****************************
DigitObserver:4
GraphObserver:****
DigitObserver:45
GraphObserver:*********************************************
DigitObserver:7
GraphObserver:*******
DigitObserver:30
GraphObserver:******************************
DigitObserver:31
GraphObserver:*******************************
DigitObserver:22
GraphObserver:**********************

5. メリット

Observerパターンでは、状態を持っているRandomNumberGeneratorクラスと、状態変化を通知してもらうDigitObserverクラス、GraphObserverクラスが登場します。そしてその2つの役目を繋いでいるものが、ObserverインターフェースとNumberGeneratorクラスになります。
RandomNumberGeneratorクラスは、自分が現在監視しているのが、DigitObserverインスタンスなのか、GraphObserverインスタンスなのかを知りません。しかし、observersフィールドに格納されているインスタンスが、Observerインターフェースを継承していることは知っており、updateメソッドを呼び出せることが保証されています。
一方、DigitObserverクラス、GraphObserverクラスは、自分を観察しているのがRandomNumberGeneratorインスタンスなのか、他のXXXNumberGeneratorインスタンスなのかを知りません。ただ、NumberGeneratorのサブクラスのインスタンスであり、getNumberメソッドを持っていることは知っています。
- 抽象クラスやインターフェースを使って、具象クラスから抽象メソッドを引きはがす
- 引数でインスタンスを渡すときや、フィールドでインスタンスを保持するときは、抽象クラスやインターフェースの型にしておく
このようにすることで、具象クラスの部分をカチッと交換することができます。

6. 参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】エラトステネスの篩で素数を求める(その2)

はじめに

前回の投稿のコードは、@swordone さんからの指摘で「リストを全探査して割り算して篩い落とす」という高コストなコードだと判明しました。
そこで、@swordone さんから頂いたヒントを基にして、コードを大幅に直してみました。

コード

前回のコードとの違いは以下の通りです。

  • 探索リストをList<Integer>からboolean[]に変更。
    • 「2から上限値までのリスト」の代わりに、「2から上限値までの添え字のリスト」としました。
    • 最初に配列の全要素を素数扱い(true)としておき、篩い落とす時に素数以外の値(false)としました。
  • 「リストを全探査して割り算して篩い落とす」のではなく、「篩い落とす値の要素にランダムアクセスして篩い落とす」形に変更。
    • for文のインクリメントを「1ずつ増加」ではなく、「現在の素数(firstNum)の値ずつ増加」させることで、現在の素数(firstNum)の倍数だけを篩い落としています。
PrimeNumberFinder.java
static void printPrimeNumbers2(int maxNumber) {

    // ステップ1:「2から上限値までの整数」を探索リストに入れる。
    boolean[] targetNumbers = new boolean[maxNumber + 1];
    Arrays.fill(targetNumbers, true);
    targetNumbers[0] = false;
    targetNumbers[1] = false;

    // 素数リスト
    List<Integer> primeNumbers = new ArrayList<Integer>();

    int sqrt = (int) Math.sqrt(maxNumber);

    // ステップ3:探索リストの先頭の値が、引数の平方根に達するまでふるい落とし操作を続ける。
    for(int i=2; i<=sqrt; i++) {
        // ステップ2:探索リストの先頭の数を素数とし、その倍数を探索リストから篩い落とす。
        // ※この時、既に篩い落とされた数(false)は除外する。
        int firstNum = i;
        if (targetNumbers[i]) {
            for (int j=i+firstNum; j<targetNumbers.length; j+=firstNum) {
                targetNumbers[j] = false;
            }
        }
    }

    // ステップ4:探索リストに残った値を素数リストに移して処理終了。
    for (int i=2; i<targetNumbers.length; i++) {
        if (targetNumbers[i]) {
            primeNumbers.add(i);
        }
    }

    // 素数の表示
    System.out.println(primeNumbers.stream().map(pNum -> pNum.toString()).collect(Collectors.joining("\t")));
}

どれだけ高速化したか?

  • 前回のコードと比べて、10倍以上高速化していました。
  • ギレン総帥じゃなくても「圧倒的じゃないか、我が軍は。」と言いたくなるほどの差です。
上限値が$10^2$ 上限値が$10^3$ 上限値が$10^4$ 上限値が$10^5$
前回のコード 54ms 55ms 61ms 102ms
今回のコード 0ms 1ms 1ms 9ms

まとめ

  • 今回のコードを通じて、「計算量」を考慮した開発が重要だと再認識させられました。
    • 今回のコードでも、まだまだ改善できるポイントはあると思いますが...
  • @swordone さん、色々とヒントを頂き有難うございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spring Boot DefaultErrorViewResolverを拡張してエラー画面を動的カスタマイズする

やりたいこと

Spring Boot + Thymeleafでの画面開発において、何らかのエラーが発生した時にステータスコード(4xx、5xx等)に応じて表示するエラー画面をカスタマイズしたい。

Spring Boot Version

2.1.3.RELEASE

BasicErrorController

Spring Boot + Thymeleafでの画面開発は、エラーが発生した時用のエンドポイントが用意されている。エンドポイントとなるコントローラークラスは以下。

org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.java
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {

@RequestMapping("${server.error.path:${error.path:/error}}") の記述にある通り、application.yaml(properties)に「server.error.path」でエラー画面のURLを記述しておけばそのURLがエンドポイントとなる。設定していなければデフォルトで「/error」がエンドポイントとなる(${error.path:/error}の記述の通り)。
そのため、開発者がエラー用コントローラーを作らなくても、以下のような構成でエラー画面HTMLを配置しておくだけでエラー画面の表示が可能となる。
d.PNG
http://localhost:8080/error -> 5xx.html
http://localhost:8080/error/404 -> 4xx.html

エラー画面を動的カスタマイズする

ただこの場合、用意されているエラーHTMLは静的なものであるため、例えば「セッションからログイン情報を取り出してエラー画面ヘッダ、フッタに表示する」「DBからある値を取り出してXXXしてエラー画面に表示する」といったように、動的にカスタマイズしたいという要望も出てくる。

そんな時は以下の要領で拡張してみる。
BasicErrorController#resolveErrorView(request, response, status, model)のところで、親クラスであるAbstractErrorControllerのresolveErrorViewを呼んでおり、その中のresolver.resolveErrorView(request, status, model)でErrorResolverのresolveErrorViewを呼んでいる。resolverはデフォルトで「org.springframework.boot.autoconfigure.web.servlet.errorDefaultErrorViewResolver」が利用されるため、このクラスをextendsしたResolver(ここではCustomeErrorViewResolverと命名)を作成し、resolveErrorViewメソッドをオーバーライドしてそこに動的カスタマイズしたModelAndViewを作成しreturnする。これだけで動的カスタマイズ可能となる。

org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.java
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request,
        HttpServletResponse response) {
    HttpStatus status = getStatus(request);
    Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
            request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
    response.setStatus(status.value());
    ModelAndView modelAndView = resolveErrorView(request, response, status, model);
    return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController.java
protected ModelAndView resolveErrorView(HttpServletRequest request,
        HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
    for (ErrorViewResolver resolver : this.errorViewResolvers) {
        ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
        if (modelAndView != null) {
            return modelAndView;
        }
    }
    return null;
}

以下のようなイメージ。

CustomeErrorViewResolver.java
@Component
public class CustomeErrorViewResolver extends DefaultErrorViewResolver {

    /**
     * Create a new {@link DefaultErrorViewResolver} instance.
     * @param applicationContext the source application context
     * @param resourceProperties resource properties
     */
    public CustomeErrorViewResolver(ApplicationContext applicationContext,ResourceProperties resourceProperties) {
        super(applicationContext, resourceProperties);
    }

    @Override
    public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
        final ModelAndView mav = super.resolveErrorView(request, status, model);
        if (status.is4xxClientError()) {
            // 4XX系エラーの時の処理
       } else if (status.is5xxServerError()) {
            // 5XX系エラーの時の処理
        }
        return mav;
   }
}

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaの開発でServletとJSPを分けて使う理由

プログラミング初学者です。
ServletとJSPで分ける理由を調べたので大まかな内容だけアウトプットします。

Q1: なぜServletとJSPで分けるのか?

A: ソースコードを管理しやすくするため

基本的にServletにはロジックJSPには見た目部分(HTML等)を書くそうです。
イメージとしては、、
Servlet→普通のjavaコードを書く。
JSP→大体はHTML等の見た目に関わるコードだが必要に応じてjavaコードが入っている。

【具体例】

・複数人で開発する際、別々のファイルで担当を分けて作業をすれば競合が起きない。
・ソースコードの可読性が上がる。 etc........
※他にも様々なメリットがあるので興味のある方は調べてみてください。

調べている中で知ったのですがJSPも仕事をしているときは最終的にサーブレットコンテナによってServletに変換されているそうです。詳しい内容は下記のURLから↓

URL

【参照元】
「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コマンドライン引数とは

プログラミング初学者です。(Javaを学習中)
コマンドライン引数について疑問に思ったことと、疑問に対する自分なりの理解を書きます。

Q1: コマンドライン引数とは?

A: プログラムを起動した際に一番最初に渡される情報

コマンドライン引数に最も多く設定されるのはファイルの情報だそうです。(文字や数値でもOK)
設定するときに必要なデータが入っているフォルダのパスを指定してあげればフォルダの中身の情報が取得できます。
いちいちコードを書かなくてもファイルの情報を勝手に持ってきてくれるから便利だなー。
と感じました。

Q2: コマンドライン引数の情報はどこに渡されるの?

A: mainメソッドの引数部分

↓ここです↓
fullsizeoutput_18.jpeg
javaの仮想マシン(VM)から最初に呼び出されるのは、"public static void main(String[] args)"と決まっているらしくString型の配列で受け取っています。ちなみに変数名だけは別に"args"じゃなくても良いそうです。

Q3: mainメソッドは他の場所から引数の値を受け取れないの?

A: mainメソッドの引数はコマンドライン引数専用である

mainメソッドプログラムの中で一番最初に呼ばれるため下記の画像のようにメソッドで引数を渡すことができません。
だからコマンドライン引数で情報を渡してあげる必要があるんですね。
fullsizeoutput_19.jpeg

初学者らしい拙い内容になっているとは思いますが、誰かの役に立てれば幸いです。
内容に不備がありましたら恐れ入りますがコメントをください。
読んでいただきありがとうございました。

【下記参照元】
「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
https://wa3.i-3-i.info/word11643.html
Samurai Blog
https://www.sejuku.net/blog/65082

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む