銀色うつ時間

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

2014年振り返り

少し早いけど、2014年の振り返り。

濃い1年だった。エンジニアとして多くのことを学び、多くのことが今だ学べずに残してしまっている。この1年を誰に感謝するかと問われれば、それはJavaScriptだろう。2月から4月まではサーバ・クライアントどちらもJavaScriptの新規サービスの開発をする機会があり、それが自身にとって得難い経験となった。今まではjQueryでDOM操作をする程度の表層でのみ利用していて気がついていなかったメソッドの意味や、曖昧だった言語の仕様、ソフトウェアに対する要求について考えることができた。必然的にモダンなJavaScript界隈の話を見聞きすることが多くなり、新しい技術やトレンドを追い出すようになった。また、チーム内で教える立場になったこともあり、曖昧な認識では説明できないことが増えた。聞かれたことを夜な夜な学んで翌日しれっと話す、といったことも何度かあったが、未だ充分な役割は果たせていないように思える。

Keep

  • いわゆるMEANスタックであるとか、WebScoket、WebComponentsなどについて学んだ。モダンなフロントエンド開発や技術については今後も継続して色々挑戦していきたい。NodeやMongoなども。

  • 自分が触ったことのない言語に挑戦した(主にGo, Swift, Rubyなど)。使いこなすレベルにまで至っているものは少ないが、手を動かしたり言語仕様について学んだりすることができた。

  • 多少のアウトプットはできた。何度か発信することができたように思う。同人誌にSocket.IOの記事を書いたりした。

  • 外のライブラリに機能追加pull requestを投げた。Angular.jsのプラグインになかった機能を自分で追加して後日pull request投げた。「機能はOK。ちょっとテスト足りないから追加で書いといたわ」と言われたのが切ない。

Problem

  • 全体的に、テストを書いた方がいい機会で書けなかった。よくある時間がないとかいうアレだったのだけれど、テスト書いておけばエンバグして手戻り、みたいな作業減るよなーという場面を作ってしまった。

  • インフラに関する知識が足りない。ネットワークやシステム構成の話の基本的な部分から、各種のミドルウェアに対する理解だったり。あとAWSとか全然ついていけてない。

  • 新しい言語やフレームワークを学んでも自分で何か作れるところまでいかなかった。また、設計やデザインパターンについてもっと理解を深めたいが。

  • 依然としてアウトプットが少ない

  • 英語が思ったより学べなかった。夏くらいまでは毎日Podcast聞いたりしていたけど、最近は全く手がついていない。

Try

  • テストが当たり前の状況にする。そのためにRailsRSpecを使って、テストの導入からテスト駆動な開発まで自分でやってみる。また、jsでもテストが当たり前で書けるように書ける部分は原則書くようにする。

  • マスタリングTCP/IPをひと通り読む。AWSで1つのアプリケーションを色々なミドルウェアを使って動かしてみる。herokuとかに色々任せきりにしない。nginx, (apache), mysql, redisなどを1人で色々設定してみる。

  • 自分でライブラリを作って公開してみる。デザパタ本を読みなおしたりDDD本を読んだりしてブログにまとめる。

  • 月1で記事を書く。また、月1で振り返りをして、軌道修正を行う。

  • 課題を課すためにも、できる限り毎日DUOとかやる。

今年読んだ本

Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)

Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)

開眼!  JavaScript ―言語仕様から学ぶJavaScriptの本質

開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

Swift離陸ガイド

Swift離陸ガイド

Swiftではじめる iPhoneアプリ開発の教科書 【iOS 8&Xcode 6対応】

Swiftではじめる iPhoneアプリ開発の教科書 【iOS 8&Xcode 6対応】

  • 読んでる・途中で止まってる本

シングルページWebアプリケーション ―Node.js、MongoDBを活用したJavaScript SPA

シングルページWebアプリケーション ―Node.js、MongoDBを活用したJavaScript SPA

オブジェクト指向入門 第2版 原則・コンセプト (IT Architect’Archive クラシックモダン・コンピューティング)

オブジェクト指向入門 第2版 原則・コンセプト (IT Architect’Archive クラシックモダン・コンピューティング)

エリック・エヴァンスのドメイン駆動設計

エリック・エヴァンスのドメイン駆動設計

来年もよろしくお願いします。

安全なWebアプリケーションの作り方1

読書会でのメモ

HTTPとセッション, 同一生成元ポリシー

HTTP

  • クライアントからサーバへのリクエスト、サーバからクライアントへのレスポンスで構成されるプロトコル
  • リクエストラインの構成は以下
  • メソッド
  • リクエストURL
  • プロトコルバージョン
  • 実際にはGET /hoge.php HTTP/1.1といった形式となる
  • リクエストヘッダにはRefererなどがつく
  • Refererはアプリケーションが意図した遷移を経ているかをサーバ側で検証する場合などに用いられるが、改変が容易
  • URLにセッションIDなど機密情報が含まれる場合にはセキュリティリスクとなる
  • レスポンスメッセージの構成は以下
  • ステータスライン HTTP/1.1 200 OK
  • ヘッダ Date: foo Server: Apache/2.2.14...
  • 空行
  • 本体 <body>...
  • レスポンスヘッダはContent-lengthやContent-Typeなどが代表的
  • Content-TypeはMIMEタイプというリソースの種類を指定するhtmlであればtext/html, jpg画像であればimage/jpg, pdf文書であればapplication/pdf
  • 特殊記号や日本語などのマルチバイト文字列をURL上に記述する場合はパーセントエンコーディングを行う必要がある
  • GET/POSTについて
  • GETメソッドを用いる場合は、「リソースに対して副作用を起こさない・機密情報を送信しない・送信するデータ量が少ない」場合に用いるのが望ましい
  • Referer経由で情報が漏洩する・アクセスログに記録されるため
  • hiddenパラメータは書き換え可能
  • HTTPには認識機構が用意されている
  • Basic認証
  • Digest認証
$user = $_SERVER['PHP_AUTH_USER'];
$pass = $_SERVER['PHP_AUTH_PW'];
if (!$user || !$pass) {
  header('HTTP/1.1 401 Unauthorized');
  header('WWW-Authenticate: Basic realm="Basic Authentification Sample"');
  exit;
}
  • Basic認証が設定されているサーバから401のレスポンスを受け取ると、ブラウザはBasic認証のidとpassを受け取るダイアログを表示する
  • 認証に成功すると、ブラウザはリクエストヘッダにAuthentificationを付与して送信し、ページの閲覧が可能となる
  • Cookieとセッション管理
  • HTTPはステートレスなプロトコルであるが、クライアント・サーバ間でログイン状態やその他の情報を記憶しておきたい場面が存在する
  • 認証状態の管理はセッション管理と呼ばれ、HTTPではブラウザのCookieを用いて実現される
  • 有効期限の設定されていないCookieはSessionと呼ばれ、ブラウザが閉じられるまで有効となる
  • Cookieは文字列長および利用者による変更が可能なため、Cookieにはセッションidのみ記録し、実際のセッション管理を行う値はサーバ側で保持することが多い
  • セッションidに求められる要件は以下
  • 第三者がセッションidを推測できない
  • 第三者からセッションidを強制されない
  • 第三者にセッションidが漏洩しない
  • 上記を満たすため、セッションには以下のようなことを考慮する必要がある
  • 乱数の質
  • ネットワーク的な盗聴の危険性
  • XSSからの漏洩
  • CookieのDomain属性/Secure属性/HttpOnly属性
  • Referer

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変数の設定が間違っていたので修正した