#Proxy
目次
動機の一つとして、生成と初期化にコストのかかるオブジェクトを使用する時(GUIアプリでいう画面に表示する時)に初めてインスタンス化する、といった場合に本パターンを適用します。
ある画面内には1000枚画像を表示する必要がありますが、有効描画領域には10枚までしか表示することができません。にも関わらず初期表示時にGraphicsクラスのオブジェクトを1000個分インスタンス化し、画像を1000枚ロードしていては、初期表示にあまり関係のない処理にリソースを大きく割かなければならなくなります。
そういった課題を解消するために本パターンを用いるようです。本来Graphicsオブジェクトを割り当てておく領域にProxyを割り当てて置き、表示するタイミングになって初めて画像をロードします。
##目的
あるオブジェクトへのアクセスを制御するために、そのオブジェクトの代理、または入れ物を提供する。
##構成要素
・Proxy 影武者クラス
・Subject ProxyクラスとRealSubjectクラスの抽象クラス
・RealSubject 本物クラス
##実装
ではサンプルコードとして画像描画プログラムを実装します。実際に1000個分インスタンス化するのもめんどくさいので初期表示時の描画数を3とします。
###Subject ProxyクラスとRealSubjectクラスの抽象クラス
package proxy
interface Subject {
fun draw()
}
###RealSubject 本物クラス
イメージクラス
インスタンス化した瞬間画像を読み込むクラス
class Image(private val filePath: String): Subject {
init {
loadImage(filePath)
}
override fun draw() {
println("${filePath}を描画")
}
private fun loadImage(filePath: String) {
println("${filePath}を読み込み")
}
}
ではこのImageクラスを使用して画面を初期表示してみます。
###クライアントクラス
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クラス
一度読み込んだものは永久に保持するようになっていますが、有効描画領域外になった場合、ロードした画像を解放するメソッドを実装したほうがより良いですね。
しかし、めんどくさいので今回は省略します。
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クラスを利用します。
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を描画
今度は表示する分だけロードできるようになりました。