関数型プログラミング(？？)でストレージ操作・その2


[前回の記事](http://qiita.com/youkaiantena/items/e7d30b0503ab302ffee2, "関数型プログラミング(？？)でストレージ操作")を書いた時よりも、関数型プログラミングの理解度が増した(はず、、、)ので、改めて表題の件について処理を書き直してみました（正確には、書き直し中）。

#関数型プログラミングとは？

副作用がない、、、とか色々あると思いますが、まだ理解していないので説明出来ません。
ですが、関数型プログラミングで使用する「関数」について、自分が勝手に師匠と思っている方から興味深い事を教えてもらいました。
自分的にはすごく合点がいったので、反芻の為にも、書き連ねてみます。


##関数型プログラミングにおける「関数」とは？

### 「◯◯とは、☓☓である」

この「◯◯」には、「関数名」が入ります。
そして「☓☓」には、「関数の内容」が入ります。

つまり _「関数名とは、関数の内容である」_ となるわけです。

しかしながら、いちいち上記の様なカタチで表さなくても、こんなの当たり前の様な気がします。
こう表す事の本質はなんなのでしょうか？


### 「手続き」ではなく、「定義」または「宣言」

_「関数名とは、関数の内容である」_

上記の様な形式で関数を説明する際に大事なことは、「手続き」としての説明ではなく「定義|宣言」としての説明をする事です。
うーん、よく分かりません。
プログラミングしながら考えてみましょう。

例えば、`商品単価、数量から小計を導くプログラム`を作成しなければならないとしたら、どう書くでしょうか？
オブジェクト指向的なプログラミングをされていた方々の場合、こんな風に書きませんか？
自分は今までこう書いてました。

```Javascript:手続き
function calcSubtotal(price, quantity){
  return price * quantity;
}
```

そして、`calcSubtotal`の説明は、こう考えてました。

```手続き

「calcSubtotal は、商品単価 × 数量を行った結果を返す」とか、
「calcSubtotal は、商品単価 × 数量の計算を行う」

```

説明からも何する関数かは分かりますし、関数名や仮引数名も問題ないと思います。


次に、「定義|宣言」を意識して書いてみます。


```Javascript:定義|宣言
function subtotal(price, quantity){
  return price * quantity;
}
```

（当たり前ですが、、、）処理は一切変わってないのが読み取れると思います。
変わったのは関数名ですね。`calcSubtotal`が`subtotal`になっています。

次に、`subtotal`の説明について考えてみましょう。
こう考える事が出来ます。

```定義|宣言
「subtotal とは、商品単価 × 数量 である」
```

うおぉ！

_「関数名とは、関数の内容である」_ 

と同じ形式で説明出来てますね！

###つまり

_「subtotal とは、商品単価 × 数量 である」_

とは、関数の説明というよりも、「ルール」といった方がしっくりきます。
ルールである以上、この様に説明できる`subtotal`という関数は、商品単価と数量の組み合わせが同じであれば、いつでも、必ず同じ結果が取得可能な関数でなければなりません。数学的なあれですね。

###まとめ

__いつでも必ず同じ結果が取得可能である__

これについては、関数型だとか、オブジェクト指向だとか、そんなの関係なく大事だと思っています。
ですが、関数型プログラミングを勉強するまで __「関数を定義する」__ という考え方が自分にはありませんでした。
関数型プログラミングを正しく書いていけるかは分かりませんが、思考の幅が広がったと感じています。

##表題の件ですが、、、

以前書いた時もそうだったのですが、個人的には`オブジェクト指向`をベースにした方がプログラムが分かりやすいのでは無いかと考えています。
ですので、今回も内部に閉じ込めておきたい処理は関数型を意識した形で書き、基本はオブジェクト指向という体にしています。
本記事を読まれた方で「俺が添削してやる！」という心優しい方がおられましたら、下記の点についてご教示いただければありがたいです。何卒宜しくお願い致します。

- 関数型を意識した説明が出来ているかどうか
- 説明に則したプログラムが書けているかどうか

### プログラム

```Javascript:mystorage.js
'use strict';

var app = app || {};

app.MyStorage = (function(){
  function MyStorage(storageType){
    this.resource = storage(storageType);
  }

  /**
   * ストレージから情報を取得する
   *
   * @param string key
   * @return string
   */
  MyStorage.prototype.getItem = function(key){
    if (canUseGetItem(this.resource) === true) {
      return JSON.parse(this.resource.getItem(key));
    }
  };

  /**
   * ストレージに情報を保存する
   *
   * @param string key
   * @param string value
   * @param object option
   * @return this
   */
  MyStorage.prototype.setItem = function(key, value, option){
    this.resource.setItem(key, value);
    return this;
  };

  /**
   * ストレージから情報を削除する
   *
   * @param string key
   * @return this
   */
  MyStorage.prototype.removeItem = function(key){
    this.resource.removeItem(key);
    return this;
  };

  /**
   * storage とは、任意のストレージオブジェクトである
   *
   * @param string storageType
   * @return Object
   */
  function storage(storageType){
    try {
      if (storageType == 'local') {
        return localstorage(browserSupported);
      }
      if (storageType == 'session') {
        return sessionstorage(browserSupported);
      }
    }
    catch (err) {
      return browserCookie(browserSupported);
    }

    return browserCookie(browserSupported);
  }

  /**
   * localstorage とは、ローカルストレージ機能をサポートしている場合のみ、localStorageオブジェクトである
   *
   * @param Function supported
   * @return localStorage
   */
  function localstorage(supported){
    if (supported(localStorage) === false) {
      throw new Error('localStorage is not supported.');
    }

    return localStorage;
  }

  /**
   * sessionstorage とは、セッションストレージ機能をサポートしている場合のみ、sessionStorageオブジェクトである
   *
   * @param Function supported
   * @return sessionStorage
   */
  function sessionstorage(supported){
    if (supported(sessionStorage) === false) {
      throw new Error('sessionStorage is not supported.');
    }

    return sessionStorage;
  }

  /**
   * browserCookie とは、クッキー機能をサポートしている場合のみ、document.cookieオブジェクトである
   *
   * @param Function supported
   * @return document.cookie
   */
  function browserCookie(supported){
    if (supported(document.cookie) === false) {
      throw new Error('cookie is not supported.');
    }

    return document.cookie;
  }

  /**
   * browserSupported とは、ブラウザーが任意のストレージ機能をサポートしている事である
   *
   * @param Object storage
   * @return boolean サポートしている:true | サポートしていない:false
   */
  function browserSupported(storage){
    return (_.isUndefined(storage) === false);
  }

  /**
   * canUseGetItem とは、任意のストレージオブジェクトが、getItemというメソッドを使える事である
   *
   * @param Object storage
   * @return boolean getItemが使える:true | getItemが使えない:false
   */
   function canUseGetItem(storage){
     return (storage.hasOwnProperty('getItem'));
   }

  return MyStorage;
})();
```

```Javascript:app.js
(function(){
  'use strict';

  app = app || {};

  try {
    var MyStorage = new app.MyStorage('local');
  }
  catch (ex) {
    alert("Sorry, your browser not browserSupported a Webstorage");
  }

})();
```