銀色うつ時間

思い出すたび何か胸につっかえてるだけ

TemplateMethodパターンの雑感

メモ

  • ロジックの手順を定義し、個々のロジックの実装はサブクラスに先送りするパターン
  • コード再利用のための基本的な話
  • 抽象クラスが定義するのは以下
  • フックはクラス内で何もしないか、またはデフォルトの振る舞いを実行する
    • サブクラスで適宜オーバーライドもできる
  • サブクラスがテンプレートメソッドを変更しないようにfinal修飾子を宣言しておく必要がある
  • 「こちらを呼び出さないでください。こちらから呼び出します。」
    • スーパークラスが主導権を持ち、必要に応じてサブクラスを呼び出す(実処理を実行する)ように設計するべき
  • Storategyパターンと混同しがちだが、Storategyパターンはコンポジションを用いてロジックをカプセル化するのに対して、TemplateMethodパターンは継承を用いてロジックをカプセル化する
  • FactoryMethodTemplateMethodの特化型と言えるかもしれない
  • サブクラスがスーパークラスを呼び出すことは許されないのか?
  • スーパークラスが定義する抽象メソッドの数が多すぎると実装が超めんどくさいと思うのだが
    • ロジックの粒度を意識するのが大事。細かくし過ぎると実装は大変になる
    • とはいえ、これは設計の柔軟性とトレードオフな気がする
    • オプションとしてフックが利用できるから、全てを抽象メソッドとせずにオプショナルなロジックはフックにするとよい

実例

  • JavaのArray.sort()など
    • 教科書通りには設計されていないが、「実装をサブクラスに先送りする」という思想が存在する

実装

  • 時間がない

Adapterパターンの雑感

メモ

  • 既存のクラスがあり、そのインターフェースが使いにくい、求めているインターフェースと異なる場合に適用するとよい
    • Adapterパターンによってクライアントが期待するインターフェースに変換することができる
  • アダプタの実装は、ターゲットインターフェースの大きさや煩雑さにより必要な作業量が変わる
  • コンポジションを使うオブジェクトアダプタと多重継承が前提のクラスアダプタが存在する
  • なぜオブジェクトをラップするか
    • アダプタは「オブジェクトのインターフェースを変換するために」オブジェクトをラップする
    • デコレータは「新しい振る舞いや責任を追加するために」オブジェクトをラップする
    • ファサードは「インターフェースの簡素化のために」オブジェクトをラップする
  • 複数のインターフェースをラップするのか
    • Facadeパターンを使おう。Adapterパターンの目的はインターフェースを「変換」するためである
  • システムに古いインターフェースを期待する部分と新しいインターフェースを期待する部分がある
    • 双方向アダプタを用いる。アダプタは関連する複数のインターフェースを実装する

実例

  • Javaの古いインターフェースであるEnumerationインターフェースを使っているが、新しいインターフェースであるIteratorを使いたい。こういう場合にAdapterクラスを作成しIteratorインターフェースを実装する
    • クライアントはIterator型のオブジェクトとしてEnumerationを透過的に呼び出すことができる

実装

  • 時間がない

Iteratorパターンの雑感とRubyでの実装

メモ

  • Iteratorパターンとは、内部表現を公開することなくアグリゲーションオブジェクトの要素に順次アクセスする方法を示した設計
  • Iteratorによる実装を行えば、複数のアグリゲーションオブジェクトの要素を走査する場合に透過的に扱うことができる。これによってその他のメソッド多態性のある機能として追加できる
  • 大事なのは、要素間のトラバースを行う役割をアグリゲーションオブジェクトではなくイテレータに移譲することで、これによりアグリゲーションオブジェクトは反復処理ではなく、本来の役割(オブジェクトのコレクション管理)に専念できるようになる
  • 内部イテレータと外部イテレータ   - 外部イテレータはクライアント側がnext()を呼び出して、次の要素への処理を決定する
    • 内部イテレータイテレータ自身が次の要素に対する処理を決定する。クライアントが反復処理を制御しないため、柔軟性に欠けるとも言えるが、反復処理を命令するだけで内部イテレータに全ての処理を行わせることが可能とも言い換えられるので、こちらの方が利便性が高いとも言われる
  • 上記の多態性のある機能とは、Iteratorを引数に取るメソッドのことである。Iteratorを活用することでコレクションの実装を考えることなく特定の振る舞いを実装できる。以下の様なもの
public void print(Iterator iterator) {
    while(iterator.hasNext()) {
        System.out.println(iterator.next());
    }
}
  • アグリゲーションオブジェクトを扱うクライアントは、アグリゲーションオブジェクトの具象クラスではなくインターフェースを参照して各アグリゲーションオブジェクトを利用するようにすると、クライアントと疎結合にできる
  • JavaのHashTable等のコレクションをIteratorで扱うとどうなるか
    • 順番を担保するものではない
    • あくまで「値に対する走査」なので、値を取得してからIteratorを生成する必要がある
  • Java.Util.Iteratorインターフェースを用いればよいのか
    • ケースバイケース
    • ArrayListはデフォルトでサポートされているので不要
  • Java.Util.Itreatorインターフェースのremove()どうするの。。。
    • 謎い。いみゅーたぼーにしたい場合はどう扱えばいいのか。

Rubyによる実装

# 内部イテレータ
# コードブロックの利用によって集約オブジェクトにロジックを伝える
# 繰り返し処理の全てが集約オブジェクト内で起こるため、内部イテレータと呼ぶ
p '内部イテレータ'
array = %w(red blue green)
array.each do |val|
  p val
end

# 外部イテレータ
# 外部機能として渡すため柔軟性に富む
# 複数コレクションから一つずつ要素を取り出して並行に処理するようなことも可能
p '外部イテレータ'
class ArrayIterator
  def initialize(array)
    @array = array
    @index = 0
  end

  def has_next?
    @index < @array.length
  end

  def item
    @array[@index]
  end

  def next_item
    value = @array[@index]
    @index += 1
    value
  end
end

# 配列を走査する
i = ArrayIterator.new(array)
while i.has_next?
  puts("item: #{i.next_item}")
end
# 文字列を走査する
i = ArrayIterator.new('abc')
while i.has_next?
  puts("item: #{i.next_item}")
end

# 平行させる
# こういうのは外部イテレータの方が容易
def merge(array1, array2)
  merged = []
  iterator1 = ArrayIterator.new(array1)
  iterator2 = ArrayIterator.new(array2)

  while iterator1.has_next? && iterator2.has_next?
    if iterator1.item < iterator2.item
      merged << iterator1.next_item
    else
      merged << iterator2.next_item
    end
  end

  set_merged(iterator1, merged)
  set_merged(iterator2, merged)

  merged
end

def set_merged(iterator, merged)
  while iterator.has_next?
    merged << iterator.next_item
  end
end

array1 = [10, 50, 100]
array2 = [15, 45, 95]
merged = merge(array1, array2)
merged.each do |val|
  p val
end

Flyweightパターンの雑感とJavaおよびcoffeescriptでの実装

復習がてら殴り書きしつつ、Javacoffeescriptでの実装を貼っておく

メモ

  • GoFが定義したデザインパターンの1つ。同一のインスタンス複数箇所で利用するときに、1つのインスタンスを再利用することで省リソース化することをねらう
  • 共有したいインスタンスFlyweightと、キャッシュを保持しつつ必要に応じてFlyweightインスタンスを生成するFlayweightFactoryが想定される
  • Factoryはシングルトンで実装される場面が多く、それによりキャッシュ機構が複数存在してしまうことを防ぐ
  • Factoryの振舞いは次の通り
  • 利用シーン
    • イミュータブルなクラスを複数生成したい場合など
  • 注意点
    • 一度 FlyweightFactory に保存されたインスタンスは、たとえ不要になった場合でもガベージコレクションされることがないため、場合によっては明示的に FlyweightFactory から削除する必要がある
  • Factoryの裏でシングルトンをいくつも存在させたいという場面で、自然と思いついていそうなパターンである。
  • プリミティブでないものをキャッシュ機構に載せるということは、参照を保持するということなので、もし参照先で変更が生じた場合、それは参照元にも影響を与える。こう考えるとよくあるバグを生みそうなパターンである。

Javaによる実装

  • 素直
package com.sisidovski.flyweight;

public class HumanLetter {
    private String letter;
    public HumanLetter(String character) {
        this.letter = character;
    }
    public void display() {
        System.out.println(letter);
    }
}

package com.sisidovski.flyweight;
import java.util.HashMap;
import java.util.Map;
public class HumanLetterFactory {
    private Map<String, HumanLetter> map = new HashMap<String, HumanLetter>();
    public static HumanLetterFactory singleton = new HumanLetterFactory();
    private HumanLetterFactory(){}
    public HumanLetterFactory getInstance() {
        return singleton;
    }
    public int countInstance() {
        return this.map.size();
    }
    public synchronized HumanLetter getHumanLetter(String letter) {
        HumanLetter humanLetter = map.get(letter);
        if (humanLetter == null) {
            humanLetter = new HumanLetter(letter);
            map.put(letter, humanLetter);
        }
        return humanLetter;
    }
}

package com.sisidovski.flyweight;
public class Test {
    public static void main(String args[]) {
        HumanLetterFactory factory = HumanLetterFactory.singleton;
        factory.getHumanLetter("a").display();
        factory.getHumanLetter("b").display();
        factory.getHumanLetter("c").display();
        factory.getHumanLetter("d").display();
        factory.getHumanLetter("e").display();
        factory.getHumanLetter("a").display();
        factory.getHumanLetter("b").display();
        factory.getHumanLetter("c").display();
        factory.getHumanLetter("d").display();
        factory.getHumanLetter("e").display();
        System.out.println(factory.countInstance());
    }
}

coffeescriptによる実装

  • どちらかと言うとsingletonの実装に躓いた
  • 生jsでの実装は結構辛そうだが、時間があるときにやってみたい
class FlyWeight
  constructor: (string) ->
    @string = string
  display: ->
    console.log @string

class Factory
  map = {}
  instance = null

  class Private
    getFlyWeight: (string)->
      flyweight = map[string]
      unless flyweight
        flyweight = new FlyWeight(string)
        map[string] = flyweight
      return flyweight
    count: ->
      Object.keys(map).length

  @get: ->
    instance ?= new Private()

# test
factory = Factory.get();
factory.getFlyWeight('a').display()
factory.getFlyWeight('b').display()
factory.getFlyWeight('c').display()
factory.getFlyWeight('d').display()
factory.getFlyWeight('e').display()
factory.getFlyWeight('a').display()
factory.getFlyWeight('b').display()
factory.getFlyWeight('c').display()
factory.getFlyWeight('d').display()
factory.getFlyWeight('e').display()
count = factory.count()
console.log count

追記

coffeescriptの例でinstance変数の設定が間違っていたので修正した

Fuelのテストがコケる件

いきなりテスト落ちてるんだが

FuelPHPを使ってみようと公式に書いてある通りにダウンロードしてコードを生成し、まずはテストを走らせてみようと思ったらこんな感じ。

$ php oil test
Tests Running...This may take a few moments.
PHPUnit 3.7.29 by Sebastian Bergmann.

Configuration read from /home/fuelSample/fuel/core/phpunit.xml

FFFFFFFF.....FFFF.F.F.....F....................................  63 / 361 ( 17%)
............................................................... 126 / 361 ( 34%)
............................................................... 189 / 361 ( 52%)
..............................................F................ 252 / 361 ( 69%)
............................................................... 315 / 361 ( 87%)
..............................................

Time: 6.89 minutes, Memory: 18.00Mb

There were 16 failures:

1) Fuel\Core\Test_Agent::test_browser
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'Firefox'
+'unknown'

/home/fuelSample/fuel/core/tests/agent.php:47

2) Fuel\Core\Test_Agent::test_platform
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'Linux'
+'unknown'

/home/fuelSample/fuel/core/tests/agent.php:59

3) Fuel\Core\Test_Agent::test_version
Failed asserting that 0 is of type "float".

/home/fuelSample/fuel/core/tests/agent.php:71

4) Fuel\Core\Test_Agent::test_property with data set #0 ('Browser', 'Firefox')
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'Firefox'
+'unknown'

/home/fuelSample/fuel/core/tests/agent.php:165

5) Fuel\Core\Test_Agent::test_property with data set #1 ('Version', 3.6)
Failed asserting that 0 matches expected 3.6.

/home/fuelSample/fuel/core/tests/agent.php:165

6) Fuel\Core\Test_Agent::test_property with data set #2 ('MajorVer', 3)
Failed asserting that 0 matches expected 3.

/home/fuelSample/fuel/core/tests/agent.php:165

7) Fuel\Core\Test_Agent::test_property with data set #3 ('MinorVer', 6)
Failed asserting that 0 matches expected 6.

/home/fuelSample/fuel/core/tests/agent.php:165

8) Fuel\Core\Test_Agent::test_property with data set #4 ('Platform', 'Linux')
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'Linux'
+'unknown'

/home/fuelSample/fuel/core/tests/agent.php:165

9) Fuel\Core\Test_Agent::test_property with data set #10 ('Frames', true)
Failed asserting that false matches expected true.

/home/fuelSample/fuel/core/tests/agent.php:165

10) Fuel\Core\Test_Agent::test_property with data set #11 ('IFrames', true)
Failed asserting that false matches expected true.

/home/fuelSample/fuel/core/tests/agent.php:165

11) Fuel\Core\Test_Agent::test_property with data set #12 ('Tables', true)
Failed asserting that false matches expected true.

/home/fuelSample/fuel/core/tests/agent.php:165

12) Fuel\Core\Test_Agent::test_property with data set #13 ('Cookies', true)
Failed asserting that false matches expected true.

/home/fuelSample/fuel/core/tests/agent.php:165

13) Fuel\Core\Test_Agent::test_property with data set #15 ('JavaScript', true)
Failed asserting that false matches expected true.

/home/fuelSample/fuel/core/tests/agent.php:165

14) Fuel\Core\Test_Agent::test_property with data set #17 ('JavaApplets', true)
Failed asserting that false matches expected true.

/home/fuelSample/fuel/core/tests/agent.php:165

15) Fuel\Core\Test_Agent::test_property with data set #23 ('CssVersion', 3)
Failed asserting that 0 matches expected 3.

/home/fuelSample/fuel/core/tests/agent.php:165

16) Fuel\Core\Test_Model_Crud::test_get_connection
Failed asserting that null matches expected 'read'.

/home/fuelSample/fuel/core/tests/model/crud.php:35

FAILURES!
Tests: 361, Assertions: 411, Failures: 16.

記録

まとめ

issuesにも上げられてるけど、「1.8でやってみ?」とか書いてあるし、まあ一言で言うとウケるとしか言いようがない。

興味のあること

ちょっと学びたいことを整理する。

言語とかライブラリ

  • Go言語
    • ハッカソンで使おうとちょっと触ったり、Go Confに行ったりした。今年は日本でも流行る兆しもあり、尊敬するvisionmediaもnodeを去りGoへのコミットを開始した。最近何かと話題だが、キャッチアップしておきたい。
    • (あまり手に馴染むレベルまで行っていないが)defer、複数戻り値がコードを完結にさせ、気持よかった。goroutineなどももっと使いこなしたい
    • martiniフレームワークはexpressっぽく使えて取っ付き易い
    • go get便利。が、デファクトが分からない場合各ライブラリのコード読んだりするべき
    • templateの仕組みまだ調査不足感もあるが、ちょっと馴染まない
  • Socket.IO 1.0
    • 1.0以前のSocket.IOは業務で利用した。1.0によりEngine.IOとのモジュール分割、バイナリ転送サポート、複数ノードでのstore機能サポート、CDN等が変更されている。もう少し学んでおく
    • HTML5 audioを用いた音声送信などは面白そう
    • Socket.IO meetupに行った
    • 同人誌かく
  • swift
    • developer programお金払ったしもう少しやりたい
    • swift+cocoaをダブルで知らないとつらいので割かしつらい
    • 今は言語仕様とか学んでおくべき
  • javascript
    • もっと高みを目指したい
    • 最近触ってないので、定期的に触ったりしライブラリを試したりしてキャッチアップしておく
  • fuel PHP
    • 新卒のコード読むために最低限は

人間として

  • 英語
    • 切実
    • 今までサボってきたので。カンファレンスなどでライブラリの作者などが来ているのに、十分に感想を伝えたり思想を聞いたりすることができないのはつらい。界隈の著名エンジニアやイケてる感ある人達が当たり前のごとく英語を使っていて悔しい。
    • 寝る前にpodcastでリスニングを開始した
    • 座学っぽくボキャブラリ増やしたりする時間を確保すること、英文にもっと目を通すことが必要
    • 少し慣れたらオンライン英会話とか始めたい
  • 数学
    • エンジニアとして、というのもあるが、メタ的な思考やアプローチができるようになりたい
    • いずれHaskell
    • 結城さんの本など余裕があれば読みたい