リファクタリングの原則

書籍「リファクタリングruby」2章 ~ リファクタリングの原則 ~ を読み直してみました。また内容を忘れた頃に摘んで読みたい内容をいくつか書いておこうと思います。

機能追加とリファクタリングという別々の活動のために時間をはっきり区別すべきだ 自分が現在どちらの作業をしているか意識する必要がある

そもそも機能追加は既存コードの処理を変えずテストを追加するのに対し、リファクタとは既存コードを変更しテストは変更しないといったように手法が異なるからですね。確かに混ぜて作業を行うと効率が悪そうです。

私は、コードが何をしているのかを理解しなければならなくなるたびに、その理解が最も早く得られるようにコードをリファクタリング出来ないかということを考えるようにしている。そして、リファクタリングを行う。読みながらコードをわかりやすくすればより多くの事が理解できる。

既存プロジェクトに途中でJOINする場合は、機能追加の前にその機能周りのリファクタをしながら理解を深めるのも良さそうですね。何かの機会に実践してみようと思います。

設計変更は高くつくので、予測できる変化に耐えられるように設計を作ろうとした。柔軟な解(設計)は、単純な解よりも複雑になるのだ。 変更はシステムの障害を通じて発生し続ける。それらすべての箇所に柔軟性をもりこんでいったら、システム全体が非常に複雑になりメンテナンスにコストがかかるようになってしまう。 柔軟性を得るためには、実際に必要とされるよりもずっと大幅に柔軟でなければならないのである。

リファクタリングの習慣があれば、柔軟な設計を考えることなく単純な方を選択できるため設計プロセスの重圧が軽減されるとのこと。単純な方に方向をたおし、必要になった時にリファクタすれば良いという感覚なんだとか。

パフォーマンス改善のために、システムの内部で何が行われているかを正確に理解していたとしても計測せよ。推測に走ってはいけない。推測で何がを学ぶかもしれないが、10回に9回までは、正しくない。

RubyKaigi2014の基調講演スピーカーのaman gupta氏もプロファイリングし徹底的な計測を行っていましたね。

ハッシュからオブジェクトへ

書籍「リファクタリングruby」8.6 ハッシュからオブジェクトへ。異なる種類のオブジェクトを格納し、復数の目的のために渡され使用されるhashはhashのキーに何が格納されているか分からなくなる。ということでオブジェクトする。

 1 class NetWork
 2   attr_reader :name
 3 
 4   def initialize(name)
 5     @name = name
 6   end
 7 end
 8 
 9 class Node
10   attr_reader :new_network
11   def initialize(network)
12     @new_network = network
13   end
14 end
15 
16 a_net, b_net, c_net  = NetWork.new("a_net"), NetWork.new("b_net"), NetWork.new("c_net")
17 a_node, b_node, c_node = Node.new(a_net), Node.new(b_net), Node.new(c_net)
18 
19 new_network = { nodes: [], old_networks: [] }
20 new_network[:old_networks] << a_node.new_network
21 new_network[:nodes] << a_node
22 
23 new_network[:old_networks] << b_node.new_network
24 new_network[:nodes] << b_node
25 
26 new_network[:old_networks] << c_node.new_network
27 new_network[:nodes] << c_node
28 
29 new_network[:name] = new_network[:old_networks].collect {|net_work| net_work.name}.join(" - ")

new_networkは異なる種類のオブジェクトが格納されている

 1 class ResultNetwork
 2   attr_accessor :old_networks, :nodes
 3 
 4   def initialize
 5     @nodes, @old_networks = [], []
 6   end
 7 
 8   def [](attribute)
 9     instance_variable_get("@#{attribute}")
10   end
11 
12   def []=(attribute, value)
13     instance_variable_set("@#{attribute}", value)
14   end
15 end
16 
17 new_network = { nodes: [], old_networks: [] }
18 new_network[:old_networks] << a_node.new_network
19 new_network[:nodes] << a_node
20 
21 new_network[:old_networks] << b_node.new_network
22 new_network[:nodes] << b_node
23 
24 new_network[:old_networks] << c_node.new_network
25 new_network[:nodes] << c_node
26 
27 new_network[:name] = new_network[:old_networks].collect {|net_work| net_work.name}.join(" - ")
28 
29 new_network = ResultNetwork.new

ひとまずinstance_variable_get instance_variable_setを使用しhashをオブジェクトに置き換えても既存処理が通るようにする。

 1 class ResultNetwork
 2   attr_accessor :old_networks, :nodes, name
 3 
 4   def initialize
 5     @nodes, @old_networks = [], []
 6   end
 7 end
 8 
 9 new_network.old_networks << a_node.new_network
10 new_network.nodes << a_node
11 
12 new_network.old_networks << b_node.new_network
13 new_network.nodes << b_node
14 
15 new_network.old_networks << c_node.new_network
16 new_network.nodes << c_node
17 
18 new_network.name = new_network.old_networks.collect {|net_work| net_work.name}.join(" - ")

置き換える

 1 class ResultNetwork
 2   attr_accessor :old_networks, :nodes
 3 
 4   def initialize
 5     @nodes, @old_networks = [], []
 6   end
 7 
 8   def [](attribute)
 9     instance_variable_get("@#{attribute}")
10   end
11 
12   def []=(attribute, value)
13     instance_variable_set("@#{attribute}", value)
14   end
15 
16   def name
17     old_networks.collect {|network| network.name}.join(" - ")
18   end
19 end
20 
21   new_network = ResultNetwork.new
22 
23   new_network.old_networks << a_node.new_network
24   new_network.nodes << a_node
25 
26   new_network.old_networks << b_node.new_network
27   new_network.nodes << b_node
28 
29   new_network.old_networks << c_node.new_network
30   new_network.nodes << c_node
31 
32   new_network.name

nameメソッドを移すことができ、いっそうすっきり

非推奨なメソッドに警告をだす

非推奨なメソッドに警告をだす

 1 class Module
 2   def deprecate(method_name, &block)
 3     module_eval <<-END
 4       alias_method :deprecated_#{method_name}, :#{method_name}
 5       def #{method_name}(*args, &block)
 6         $stderr.puts "Warning: calling deprecated method #{self}.#{method_name}"
 7         deprecated_#{method_name}(*args, &block)
 8       end
 9     END
10   end
11 end
12 
13 class Foo
14   def foo
15     puts "deprecated"
16   end
17 
18   deprecate :foo
19 end

データ値からオブジェクトへ

データ値が整形や値の抽出などで特別な振る舞いが増えてきた場合はデータ値をオブジェクトにするのが良い

 1 class Order
 2   def initialize(customer)
 3      @customer  = customer
 4   end
 5 
 6   def customer_full_name
 7     @customer
 8   end
 9 
10   def customer_first_name
11     @customer.split(" ").first
12   end
13 
14   def customer_family_name
15     @customer.split(" ").last
16   end
17 end

Orderクラスに属性(customer)を操作する処理が復数定義されている

 1 class Order
 2   def initialize(customer_name)
 3      @customer  = Customer.new(customer_name)
 4   end
 5 
 6   def customer
 7     @customer
 8   end
 9 
10   def customer=(name)
11     @customer = Customer.new(name)
12   end
13 end
14 
15 class Customer
16   def initialize(name)
17     @name = name
18   end
19 
20   def name
21     @name
22   end
23 
24   def first_name
25     @name.split(" ").first
26   end
27 
28   def family_name
29     @name.split(" ").last
30   end
31 end

属性をクラスに昇格されて、操作メソッドをクラス内で定義する

アクセサとインスタンス変数

アクセサを定義するか、インスタンス変数に直接アクセスするか

アクセサは単なるgetterかどうか確認する必要があるので,インスタンス変数のほうが可読性は高い.
しかし、スーパークラスのインスタンス変数値をサブクラスで変更したい場合は、 アクセサを定義しておくとオーバーライドで変更できるのでスーパークラスのメソッドをそのまま使用することができる.

 1 class Item
 2   def initialize(base_price, tax_rate)
 3     @base_price = base_price
 4     @tax_rate   = tax_rate
 5   end
 6 
 7   def total
 8     @base_price * (1 + @tax_rate / 100.0)
 9   end
10 end
11 
12 class ImportedItem < Item
13   def initialize(base_price, tax_rate, import_duty)
14     super(base_price, tax_rate)
15     @import_duty = import_duty
16   end
17 
18   def total
19     @base_price * (1 + (@tax_rate + @import_duty) / 100.0)
20   end
21 end

税抜き価格 + 税率 の定義が変更したわけでなく税率が加算され変化しただけなので上記の様にtotalを再定義するのは、保守面からも問題がある。

 1 class Item
 2   attr_accessor :base_price, :tax_rate
 3 
 4   def initialize(base_price, tax_rate)
 5     @base_price = base_price
 6     @tax_rate   = tax_rate
 7   end
 8 
 9   def total
10     base_price * (1 + tax_rate / 100.0)
11   end
12 end
13 
14 class ImportedItem < Item
15   attr_reader :import_duty
16 
17   def initialize(base_price, tax_rate, import_duty)
18     super(base_price, tax_rate)
19     @import_duty = import_duty
20   end
21 
22   def tax_rate
23     super + import_duty
24   end
25 end

スーパークラスでtax_rateのアクセサを定義したことで、オーバーライド可能となりtotalを再定義せずに済んだ。

書籍「Webを支える技術」を買った

「電車に乗っていては足元の線路なんてみえない。奴がブラックボックス化しているモノも探求せねば」と本書を購入&読了。
以下、各章のメモ書き。

本書における「Webを支える技術」の勘所の網羅性は甚だ素晴らしく、なるほど評価が高い訳だと納得できるのだが、結局は技術仕様上の理想的知見を得ただけで、実際に論を利用、応用出来るかはやはり別なので、手を動かし訓練する必要がある。ということで読習と平行し、Webを支える技術の宝庫であろうWebApplicationFrameworkというやつを作り始めた。「今日はredirect_toをサポートしたので、次はrenderをサポートしよう。」「sinatraっぽく書けるようにしてみよう」「いっその事hamlをデフォルトテンプレートにしてしまえ」といった感じで内なりに楽しんでいる。おすすめ。(ゆくゆくはクライアントサイドMVCと組み合わせて使う、軽量JSON発行機Frameworkにしていく)


[Kyuden/salen]

Salen is a small and callow Web Application Framework.

1 require 'salen'
2 
3 class SampleApp < Salen::App
4   get '/' do
5     'Hello Salen'
6   end
7 end
8 
9 SampleApp.run!

And run with:

1 ruby sample_app.rb

View at: http://localhost:8080

Installation

Add this line to your application’s Gemfile:

1 gem 'salen'

And then execute:

1 $ bundle

Or install it yourself as:

1 $ gem install salen

Routes

In Salen, a route is an HTTP method paired with a URL-matching pattern. Each route is associated with a block:

1 get '/' do
2   .. show something ..
3 end

Route patterns may include named parameters, accessible via the params hash:

1 get '/hello/:name' do
2   # matches "GET /hello/foo" and "GET /hello/bar"
3   # params[:name] is 'foo' or 'bar'
4   "Hello #{params[:name]}!"
5 end

Browser Redirect

You can trigger a browser redirect with the redirect helper method:

1 get '/hello' do
2   redirect_to "/"
3 end

Views / Templates

Default template is haml

1 get '/hello' do
2     haml 'index'
3   end

書籍「Webを支える技術」5部メモ

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
          summary
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

5部 Webサービスの設計
  • 読み取り専用のWebサービス設計
  • 書き込み可能なWebサービス設計
  • リソースの設計

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

15章.読み取り専用のWebサービス設計


検索などクエリパラメータを受けとりリソースを返す場合は.json.htmlなどの様に拡張子で区別せず、typeパラメータを使用する方が自然。

1 # クエリパラメータを受け取ららない場合
2 http://zip.ricollab.jp/東京都/文京区/小石川.json
3 
4 # クエリパラメータを受け取る場合
5 http://zip.ricollab.jp/search?q="小石川"&type=json

HTMLにて特定の記事のリンクを貼るように、WebAPIも次の記事のリンクを提供すると親切。検索結果が複数ページにまたがる場合に次のページのリンクを追加するなども同様。

 1 {
 2   "query": "112",
 3   "next": "http://zip.ricollab.jp/search?q=112&type=json&page=2"
 4   "result":[{
 5     "zipcode": "1120000",
 6     "address": "東京都文京区小石川",
 7     "link": "http://zip.ricollad.jp/1120000"
 8   },
 9   {
10     "zipcode": "1120001",
11     "address": "東京都文京区白山",
12     "link": "http://zip.ricollad.jp/1120001"
13   }]
14 }

16章.書き込み専用のWebサービス設計


リソースの更新
  • バルクアップデート
    • 更新以外の全ての情報を送受信する。
    • クライアントの実装が簡単になる。
    • データ量が多いためAjaxなどの非同期通信では不向き。
  • パーシャルアップデート
    • 更新情報のみ送受信する。
    • データ量が少なくて済む。
    • GETしたリソースを一部修正してそのままPOSTすることができない。

サーバ側でパーシャルアップデートをサポートする場合は、バルクアップデートもサポートするのが一般的。

トランザクション
  • 処理をトランザクションリソースへ送信し、トランザクションの実行リクエストが送信されたタイミングで処理を行う。失敗時にロールバックする。
  • 複数リソースへの処理をPOSTで一括にリクエストする。失敗時にロールバックする。
排他制御
  • 悲観的ロック
    • リソースをロックすることで排他制御を実現する方式。
      1. WebDavのLOCK/UNLOCKを使用する。
      2. LOCK相当の機能をサーバ側で用意する。 
    • 多数のユーザが同時に編集できない、などの問題が発生する。
    • 少人数向き
  • 楽観的ロック
    • 競合発生時に適切に対処することで排他制御を実現する方式。
      1. 条件付きリクエストにてリソースの更新状態を把握し競合に対処する。
    • 不特定多数向き

17章.リソース設計


  • リソース設計には決まりきった設計法が定まっていないので、ER図やクラス図などよりリソースを抽出、階層化すると良い。
  • リソースを設計する上で大切なことは「WebサービスとWebAPIを分けて考えないこと」。人間用、プログラム用とインターフェスは違いこそあれど提供するものは同じリソースであり、技術的違いも少ないため分ける必要がない。

書籍「Webを支える技術」4部メモ

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
          summary
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

4部 ハイパーメディアフォーマット
  • HTTP
  • microformats
  • Atom
  • Atom Publish Protocol
  • JSON

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

4部は斜め読みしたので得られた知識は、AtomAtom Publish Protocolってのがあるんだなー程度だ。直近で必要そうでもないし、必要に迫られたら深く掘っていこうと思う。5部へ急ごう。

10章.HTTP

HTMLでリンクを設計する際は「リンクを辿ることでアプリケーションの状態が遷移する」ことを強く意識し、HTML自体がリソースの状態を表現する事が大切。

11章.microformats

microformats : HTML属性を使用し各要素に意味を情報として付加するもの。破綻したRDFより簡単で気軽にセマンティクス(メタ情報)を記述できる。

12章.Atom

Atom : タイトル、著者、更新日時といった基本的なメタデータを備えたリソース表現のためのフォーマット。独自のタグを使用する。

13章.Atom Publish Protocol

Atom Publish Protocol : Atomを利用しリソースを編集(CRUD)できるプロトコル。

14章.JSON

JSON : シンプルなデータフォーマット。JSONPでクロスドメイン通信が可能で、Ajaxでは必須の技術。

書籍「Webを支える技術」3部メモ

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
          summary
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

3部 HTTP
  • HTTPの基本
  • HTTPメソッド
  • ステータスコード
  • HTTPヘッダ

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

6章.HTTPの基本


HTTPはステートレスなプロトコルとして設計されている。

ステートフル利点

新規に必要な情報である差分のみをやりとりするので簡潔。

ステートフル欠点

各ユーザの状態を記憶するので複数のサーバで複数のユーザに対応する際、各ユーザの状態をサーバごとに同期する必要がありオーバーヘッドが大きい。

ステートレス利点

必要な情報を毎回繰り返しやりとりするので冗長。

ステートレス欠点

サーバは各ユーザの状態を記憶する必要がないので、新規に来るリクエスト一回に集中するだけで良く、サーバ側の処理が簡素にな。

Webの利用形態は不特定多数を前提としているので、全ユーザの状態を覚えるのは難がありそうですね。なので冗長なデータなどはHTTPヘッダなどで明示し、クライアント側でキャッシュするよう工夫されているのですね。この辺の話は9章辺りに詳しく書かれています。

7章.HTTPメソッド


POSTとPUTの使い分け

両者ともリソースの作成ができるが、POSTで作成したリソースにアクセスするURIはサーバが決定する。PUTで作成したリソースはユーザ自身がURIを決定する。TwitterのようにつぶやきのURIを自動的に決定するサービスはPOST、Wikiのようにユーザ自身がタイトルを決定する場合はPUTを使う。尚、POSTの場合は自動でサーバがURIを決定するのでレスポンスとしてLocationヘッダを返すと良い。クライアントがリソースを決定できるPUTはクライアントがサーバの内部実装を熟知する必要があるので結合が密になる。特別な理由がない限りは、リソースの作成はPOSTで行い、URIはサーバ側で決める設計が望ましい。

_methodパラメータ

_methodパラメータをフォームの隠しパラメータするとPOSTPUTDELETEの代用ができる。(GETPOSTだけの制限をかけているサーバもわりと多い)

べき等性と安全性
  • べき等性 : ある操作を何回行っても必ず同じ結果である。(リソースの内容が更新されている、削除されているなど)
  • 安全性  : 操作対象のリソースの状態を変化させない。
HTTP method   性質
GET べき等かつ安全
PUT、DELETE   べき等だが安全でない
POST べき等でも安全でもない

^^

べき等の性質を持つHTTP methodは、通信エラーなどによる送信の重複を恐れる必要はない。一方、べき等でないPOSTはダイアログで警告を促すなど工夫しなければ二重注文などが発生する可能性がある。べき等などの性質はHTTPの仕様に定められており、性質通りにHTTPメソッドを使用するべきである。 例えばPUTで数値を50ずつ更新するようなリクエストは更新のたびにリソース値が変化するため、べき等ではなくなる。このような場合は差の50でなく、合計値への更新をリクエストすべきである。

8章.ステータスコード


Acceptヘッダに応じたフォーマットでエラーレスポンスを返すと親切。

9章.HTTPヘッダ


MIMEメディアタイプ

XMLのように文章本体で文字エンコーディングを指定できる場合であっても、charasetヘッダを省略するとtextタイプの文字エンコーディングが指定されてしまうため、必ずcharasetで明示的にエンコードタイプを指定する事。合わせてContent-Typeでメッセージのボディーのメディアのタイプも指定するべき。

コンテントネゴシエーション

クライアントはAcceptAccept-Charset,Accept-Charset, Accept-Languageなどのヘッダをリクエストヘッダに付加することで、指定した条件でレスポンスを受け取る事ができる。

Content-Lengthとチャンク転送
  • content-Length    : 静的ファイルなど、予めサイズのわかっているリソースを転送する際に使用する。
  • Transfer-Encoding  : chunkedと指定することにより最終的なサイズの分からないボディを少しずつ転送可能にする。
キャッシュ用ヘッダ
  • pragma     : no-cacheを指定しキャッシュしてはならない事を知らせる。(リソースの取得は必ずサーバにアクセスする必要がある)
  • Expires     : 絶対時間を指定し、時間内はリソースが最新であることを知らせる。(時間内はクライアントはリソースをキャッシュする)
  • Chache-Control : 相対時間を指定し、時間内はリソースが最新であることを知らせる。その他に高機能なキャッシュ制約を指定できる。
キャッシュ用ヘッダの使い分け
  • キャッシュをさせない場合は、pragmaChache-Controlを同時に指定する。
  • キャッシュの有効期限が明確に決まっている場合は、Expirsを使用する。
  • キャッシュの有効期限を相対的に指定したい場合は、Chach-Controlmax-ageで相対時間を指定する。
条件付きGET

pragma, Expirs, Chache-controlはサーバ側からクライアント側のレスポンスにて使用されるのに対し、If-Modified-Since, If-None-Matchはクライアント側でキャッシュしてあるリソースが変更されているかを調べる条件、手段をサーバ側に提示し、キャッシュ可能かを検証するヘッダである。またIf-Modified-Since, If-None-MatchはリソースがLast-ModifiedまたはETagを持っている時に使用可能である。

  • If-Modified-Since : リソースの更新日時を指定し、指定時間以降に更新がない場合は304 Not Modifiedを返す。
  • If-None_Match   : キャッシュしていあるリソースのEtag値(gitのハッシュ値のようなもの)を指定し、サーバ側のリソースのETag値と比較する。同じであれば304 Not Modifiedを返す。(時計をもっていないサーバやミリ単位で変更される可能性のあるリソースを扱う際に有効)
If-Modified-Since、 If-None-Matchの使い分け

サーバ側を実装する場合はキャッシュ可能なリソースにはできるだけEtagを使用する。

書籍「Webを支える技術」1部、2部メモ

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
          summary
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

1部 Web概論
  • Webとは何か
  • Webの歴史
  • REST -Webのアーキテクチャ-
2部 URI
  • URIの仕様
  • URIの設計

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

本書は5部構成で全17章からなるが、通勤時間で2部(5章)読んでしまった。昔からじっくり本を読み進めていくタイプでなく、ざっくり繰り返し読むタイプなので2回目のためにトピックを簡単にdumpしておく。(強くてニューゲームほど楽しい発見があると信じている。)

1章.Webとは何か


Webとは不特定多数をリンクしうるハイパーメディアの特性をもつ概念、もしくは分散システム。またハイパーメディアを実現するHTTP, URI, HTMLなど要素技術の総称。

2章.Webの歴史


  • (1980年代)Webが登場する以前はハードウェアにしろソフトウェアにしろ数や種類が限定された分散システムが主流だった。
  • (1990年代)スイスの原子核研究所の研究員であるTim Berners-LeeがWebの提案書を書く。
  • (1994年)Tim Berners-LeeがW3Cを設立し、HTML、XML、HTTP、URI、CSSなどの標準化を開始する。
  • (2000年)California大学の大学院生であるRoy FieldingがWebのソフトウェアアーキテクチャを「REST」と名付け博士論文として提出する。
  • (1990年代後半〜2000年代前半)学術論文の交換用途に使用されてきたWebの用途が多様化し、プログラムより自動処理を行いたいという要求が出始める。
  • (2000年前後〜2003年)SOAPとRESTで論争が起こるが、「簡易に操作できる」としてGoogleやAmazonがRESTによるWebAPIを提供した事が決定打となりRESTに軍配が上がる。
  • (2003年~)RESTの普及と平行しAjaxなどの使い勝手の良い技術のブレイクスルーがあり、ますますWebは発展していった。
胸熱ポイント

Tim Berners-Leeは提案書を書き、クリスマス休暇を使って最初のブラウザとサーバを自身で実装し、W3CにてHTML、XML、HTTP、URI、CSSなど、今日のWebの礎をほぼ一人で築いた所ですかね。またRoy FieldingがSOAP押しの大手ベンダーと一人の技術者として繰り広げたバトルも涙ものです。圧倒的圧力と政治力の差がありながら彼は必死に叫んだのです。実用的な所でいくとRESTの勝因が「使い易さ」であることですかね。広く普及を考える場合は誰しもが簡単に使いやすい方が高機能より好まれるのですね。

3章.REST -Webのアーキテクチャ-


ハイペーメディアとして

リソースをリンクで接続することでアプリケーションを構築する考え方がRESTの基幹をなす思想。

アーキテクチャとして

RESTはクラ/サバに6つの制約を加えたアーキテクチャスタイル。(制約は理想であり、幾つか制約を妥協しようとも理想を念頭に置くことが重要)

  • クライアント/サーバ  : ユーザインターフェースと処理を分離する。
  • ステートレスサーバ   : サーバ側にアプリケーションの状態を保持しない。
  • キャッシュ       : クライアントとサーバの通信回数を減らす。
  • 統一インターフェース  : HTTPなど各サーバやクライアント感で統一のインターフェースを使用する。
  • 階層化システム     : ロードバランサやプロキシなどシステムを階層化可能である。
  • コードオンデマンド   : サーバからJSなどのコードをダウンロードしクライアント側で実行可能である。

4章.URIの仕様


URIで使用できる文字列はASCII文字(7bitで表現できるコードに割り当てられたアルファベットを記号)。ASCII文字以外を使用するにはencodingが必要。

URIの実装で気をつけること
  • 絶対URLを使用する。(相対URLを使用すると余計な処理が必要なため)
  • エンコーディングはUTF-8を用いる。(現代的な多くのWebサイトが採用しているため)

5章.URL設計


URLは変わらないべきだ。変わらないURLこそが最上のURLである
Tim Berners-Lee

変わりやすいURLはリンク切れを発生させるためですね。

URL設計指針
  • プログラミング言語に依存した拡張子を利用しない。(.pl, .rb, .do, .jspなど)
  • 実装依存のパス名を利用しない。(cgi-bin, servletなど)
  • プログラミング言語のメソッド名を利用しない。(people/show/123)
  • セッションIDを含めない。(友人にメールで送られてきたURLを友人と同じように見せるべき)
  • リソースを表現する名詞を使用する。(動詞はHTTPメソッド)
URL設計テクニック
  • 1つのリソースが複数の表現を持つ時は.ja.jsonなどのように拡張子を使用する。(ロケールはHTTPヘッダのAccept-Languageから取得すると良い)
  • 複数のパラメータで表現されるリソースは/,, ;を使用する。(日付、軽度&緯度など)
  • URIはクライアント側で変更できてはならない、サーバから強制される不透明なものである。

おまけ

Tim Berners-Lee氏が偉大すぎてお話を聞いてみたくなったのでした。



© 2015 kyuden