A Day In The Boy's Life -28ページ目

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

私たちの身の回りの多くはソフトウェアによって制御されているものが数多くあり、そのソフトウェアの構造は年々複雑化の一途を辿っていっています。

TVにしろ、カーナビにしろ、エアコンにしろ、ゲームにしろ、Webサービスにしろ、全てがその製品があり続ける限り多機能化され、モデルチェンジやバージョンアップによって新しい処理が加わっています。

TVのリモコンを見るたびに使っていないボタンの多さに戸惑い、自分が望むものはどの操作をすれば解決するのかわからず悩みます。

自分が欲しいものはもっとシンプルなものだ!といったところで、それに反するものが世の中にあふれるのは何故なのでしょうか。

 

 

 

シンプルなものは満足されない

 

 

モノがあふれる時代では、あるものに特化したごくシンプルな機能というのは、それにかなりの強みやメリットが無い限りなかなか満足されません

 

スマートフォンは、もはや元の携帯電話の型から逸脱していますし、単に電話ができればよいというシンプルな機能だけでは満足されません。

らくらくホンといった電話の機能だけに特化したニッチな製品もありますが、それが多数派となっていない以上、少なくとも携帯電話が通話だけの機能でよいと考えている人は少ないのでしょう。

 

単に自分のつぶやきを140文字で投稿するというシンプルなTwitterでも、サービス開始以降に公式のリツイート機能が実装されたり、フォロワーをグループ化できるリストや、他人の行動を確認できるアクティビティ、(誰が何のために使うんだと思う)自分のつぶやきのダウンロード機能などが加わっています。

 

単に地図を表示するというシンプルな機能であったGoogle MAPもストリートビューやコミュニティの機能が加えられたり、写真を投稿するシンプルなアプリであるInstagramも写真の加工機能やフィルタが多種多様に追加されていっています。

 

こういったソフトウェアのバージョンアップは、便利だと思うものも多くある中でそれと同じぐらいに自分には不要と思うものがあったりします。

 

こういった経緯の中で、自分の中でシンプルでわかりやすく取り扱いやすいものと思っていたものが、複雑でとっつきにくいものへと変貌していくのでしょう。

しかし、一方で単にシンプルだったものに飽きがきてしまうこともあります。

単純だから好きというものが単純すぎると感じられるようになったり、あとこの機能があればいいのにという欲が出てきて似たような別の製品やサービスに心変わりしてしまうのよくあることです。

今のユーザーの心理としては、無いよりはあったほうがマシ、あって邪魔なら無視すればいいだけのこと、と考える人の方が多いからかもしれません。

 

 

 

ソフトウェア開発に纏わる複雑化の要因

 

 

ソフトウェアを開発する上で、その最初はシンプルだった機能がどんどん複雑化する要因はどんなものがあるのでしょう。

 

1つは、先ほど書いたようにシンプルな機能だけでは強みが無く、例えニッチだとしても少数派を取り込み差別化を図りたいというものがあるかと思います。

 

要はシェアを獲得するためには多くのユーザーが必要であり、その獲得のためには広く満足してもらうための機能が必要だという観点です。

世の中、似たような製品があるわけで競争に勝つためには、他に無い機能をどんどんと取り入れていかないといけないわけで、その過程でソフトウェアは複雑化していきます。

 

2つ目は、下位互換によって機能が残されるというものがあったりします。

新バージョンではその機能は別のものに取って代わっているのに、旧バージョンから引き継ぐためにその機能を残さざるを得ない状況です。

いきなり無くすのはユーザーへの影響度をから乱暴だと考え、段階的に廃止するためにしばらくの間、下位互換としてその機能が継続されることはよくあったりします。

ただし、すっぱりなくなくるということの方が稀だったりもして、何時までもその機能を引き継ぎメンテナンスせざるを得ないソフトウェアも数多く存在します。

 

3つ目は、ステークホルダーとの関係によるものだったりします。

株主などそのサービスに投資している人がいて利益拡大のためにサービスを常に拡張しつづければならなかったり、そもそもユーザーからの声というものが機能追加の後押しとなったりします。

実はサービスや製品を複雑化しているのは私たちの意見からくるのかもしれません。

場合によっては、権威ある人からの鶴の一声で要らぬ機能が追加されるということもあるでしょう。

 

4つ目は、ソフトウェア開発コストの低下に伴う使い捨ての思想によるものがあります。

ソフトウェアは複雑化しても単機能としての開発コストは下がっているので、リリースして使われなければ捨てればよいという発想があったりします。

シンプルな機能で良いといっても、そのシンプルという定義は人によって異なるので、多機能なものを与えてその人に取捨選択してもらうほうが楽だったりします。

なので、数打てば当たると言わんばかりに、多くの機能をスクラップ&ビルドしていくわけですが、一度作ったものはなかなか取り下げることが難しく、スクラップするはずのものがそのまま残されていったりもします。

 

5つ目は、関連するソフトウェアの多さによるものです。

技術の発展により、ソフトウェアの開発コストは下がり、簡単に万能なインフラがすぐに構築できる環境であっても、そこには数多くのソフトウェアが存在しています。

例えば、仮想化の技術によってより多くのOSが1つのハードウェアの中に集約できたとしても、その上で起きたソフトウェアの問題は、アプリケーションによるものなのか、OSによるものなのか、ハイパーバイザーによるものなのか、ファームウェアによるものなのかの切り分けが非情に難しかったりします。

ソフトウェアがソフトウェアと関連するたびに、その複雑さは単純に増していくことになります。

 

 

まとめ

ということで、ソフトウェアの複雑さは単にその制御の仕組みだけでなく、ユーザーのニーズや心理であったり、サービスの強みによるものだったり、開発手法であったり、と様々な要因によって複雑化していっているのではないでしょうか。

 

一昔前なら、こんな機能があるなんて夢のようだといわれたものが今や付いてて当たり前という時代になってたりもします。

しかし当然ながら、それを制御するソフトウェアというものはその機能の数以上に存在しており、それらを制御するためにはかなり難易度の高い処理をしなくてはなりません。

こんな機能、誰が使うんだという感情はそれを使うユーザー以上に、それを作るエンジニア自身が持っていたりするのかもしれません。

 

 

 

 

 

 

今やWebサイトを開設したらモバイルへの対応は必須という状況になってきています。

CakePHPで作ったサイトでも簡単なステップを踏めばPC用とモバイル用を分けて表示するということができたりします。


※ ここで書いたモバイルというのはスマートフォンが中心なので、正確に言えば「CakePHPのサイトをスマートフォンに対応させる」かもしれません。


今回の環境では、CakePHP2.3.7を使っています。



isMobileを使ってモバイル判定をする


CakePHPのRequestHandlerComponentの中には、モバイル判定をしてくれるメソッドが標準で用意されています。

それが、isMobileメソッド でユーザーエージェント内に特定の文字列があるかどうかで判定を行っています。

日本のものでは何故かDocomoだけが入っていますが、ほぼ海外の端末判定しかしてくれません。

まぁ、スマートフォンであれば多くはiPhoneかAndroidになるかと思いますので大部分はカバーできるかもしれませんが。


使い方は単純で、メソッドを呼び出せばアクセス元のユーザーエージェントから自動的に判別してくれます。

ですので、共通の事前処理としてbeforeFilter()などでisMobileを呼び出して判定してあげます。


    public function beforeFilter() {
        if ($this->request->isMobile()) {
            $this->mobile = TRUE;
        }
    }

次に、モバイルの場合はPC用のテンプレートが使えないので、モバイル専用のテンプレートを用意してあげます。

これは、各アクションの中で


$this->render('mobiePage');

などとしてもいいのですが、PC用のテンプレートと混在すると保守しづらかったり、アクションごとに判定と設定をしていくのは手間なので、beforeRender()でモバイル判定された場合のテンプレートのディレクトリを別で参照するようにしておきます。


    function beforeRender() {
        if ($this->mobile === TRUE) {
            $this->viewPath = 'Mobile';
        }
    }

これで、モバイル判定された場合はView/Mobileディレクトリが参照されます。

あとは、モバイル用のViewファイルを用意しておきます。


<?php $this->layout = "mobile"; ?>
<div data-role="page">
  <div data-role="header" data-theme="b">
    <h1>モバイルページ</h1>
  </div>
  <div role="main" class="ui-content">
    <p>モバイルからのアクセスです</p>
  </div>
</div>
</body>
</html>

モバイル用のレイアウトファイルも別に用意しておいた方が便利かもしれません。


<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title><?php echo $title; ?></title>
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.0/jquery.mobile-1.4.0.min.css" />
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.0/jquery.mobile-1.4.0.min.js">
</script>
<script src="/apps/js/jquery.mobile.nestedlists.js">
</script>
</head>
<body>
<?php echo $this->fetch('content'); ?>

これは、jQueryモバイルを使ったレイアウトおよびテンプレートファイルとなりますが、モバイルページの作り方は様々なので環境に応じて作りこみます。

これで一通りモバイルへの対応が完了です。



isMobileのモバイル判定を拡張する


isMobileの判定はマニュアルページにも書いてあるとおり、あまり多くのモバイルに対応しているわけではありません。

これを、その他のモバイル環境でも判定できるように拡張したい場合もあるでしょう。


その前に、isMobileの判定方法を簡単に書いておきます。


/path/to/cakephp/lib/Cake/Controller/Component/RequestHandlerComponent.php

isMobileはRequestHandlerComponentに定義されていると書きましたが、具体的には上記のパスに存在します。


    public function isMobile() {
        return $this->request->is('mobile') || $this->accepts('wap');
    }

この中で呼び出されているis()メソッドは、Network/CakeRequest.phpの中で定義されていて、is()メソッドは_detectorsという特殊な変数の中身に該当するかどうかをチェックする動きをします。

detectorsの中身はデフォルトで下記のように定義されています。


    protected $_detectors = array(
        'get' => array('env' => 'REQUEST_METHOD', 'value' => 'GET'),
        'post' => array('env' => 'REQUEST_METHOD', 'value' => 'POST'),
        'put' => array('env' => 'REQUEST_METHOD', 'value' => 'PUT'),
        'delete' => array('env' => 'REQUEST_METHOD', 'value' => 'DELETE'),
        'head' => array('env' => 'REQUEST_METHOD', 'value' => 'HEAD'),
        'options' => array('env' => 'REQUEST_METHOD', 'value' => 'OPTIONS'),
        'ssl' => array('env' => 'HTTPS', 'value' => 1),
        'ajax' => array('env' => 'HTTP_X_REQUESTED_WITH', 'value' => 'XMLHttpRequest'),
        'flash' => array('env' => 'HTTP_USER_AGENT', 'pattern' => '/^(Shockwave|Adobe) Flash/'),
        'mobile' => array('env' => 'HTTP_USER_AGENT', 'options' => array(
            'Android', 'AvantGo', 'BlackBerry', 'DoCoMo', 'Fennec', 'iPod', 'iPhone', 'iPad',
            'J2ME', 'MIDP', 'NetFront', 'Nokia', 'Opera Mini', 'Opera Mobi', 'PalmOS', 'PalmSource',
            'portalmmm', 'Plucker', 'ReqwirelessWeb', 'SonyEricsson', 'Symbian', 'UP\\.Browser',
            'webOS', 'Windows CE', 'Windows Phone OS', 'Xiino'
)), 'requested' => array('param' => 'requested', 'value' => 1) );

この配列の中で、mobileキーがモバイル判定するためのユーザーエージェントの一部の文字列が入っています。

モバイル判定を拡張したい場合はこれに追加すればいいわけですが、addDetector()という専用のメソッドが用意されています。

例えば、Kindleからのアクセスをモバイル判定にしたい場合、下記のように事前にaddDetector()でKindleという検知用の文字列を追加しておきます。


$this->request->addDetector('mobile', array('options' => array('Kindle')));
if ($this->request->isMobile()) {
    $this->mobile = TRUE;
}

これで、isMobile()の判定データを拡張しておけば定義されていない環境からのアクセスでもモバイル判定できるようになります。





CakePHPを利用する場合、そのサーバーのDocument Rootにインストールしてしまうのが一番手っ取り早いのですが、場合によってはDocument Rootを他のツールやコンテンツで利用したいために、他のパスで動作させたいということがあったりします。


ということで、元々Document Rootで動かしていたCakePHPを別のパスで動くようにお引越しした際の備忘録です。

環境は、CakePHP2.3.7+Apache2.2.15で動かしています。



CakePHPをDocument Root以外で動かす


まず、CakePHPは下記のパスにインストールされているとします。


/var/www/cakephp

DocumentRootでCakePHPを動かしていた頃のApacheの設定ファイル(httpd.conf)のVirtualHostは下記のようにしていました。


<VirtualHost *:80>
    ServerAdmin webmaster@example.com
    DocumentRoot /var/www/cakephp/app/webroot
    ServerName www.example.com
    ErrorLog logs/error_log
    CustomLog logs/access_log combined
</VirtualHost>

DocumentRoot以外でCakePHPを動かすので、URL上でのCakePHPのパスを決めなくてはなりません。

今回は、「http://www.example.com/apps/」でCakePHPが動くように設定をしたいと思います。

合わせて、DocumentRootを他のコンテンツが表示されるように置き換えるので、まずはApacheの設定ファイルを下記のように編集します(編集後は要Apache再起動)。


<VirtualHost *:80>
    ServerAdmin webmaster@example.com
    DocumentRoot /var/www/html
    ServerName www.example.com
    ErrorLog logs/error_log
    CustomLog logs/access_log combined
    Alias /apps/ /var/www/cakephp/app/webroot/
</VirtualHost>

これで、DocumentRootのパスはCakePHP以外のディレクトリに移り、Aliasによって/appsにアクセスがあるとCakePHPのディレクトリが参照されるようになります。


次に、CakePHPのインストールディレクトリ以下にある.htaccessファイルを編集します。

これは、下記の3つのパスにそれぞれ存在します。


/var/www/cakephp/.htaccess
/var/www/cakephp/app/.htaccess
/var/www/cakephp/app/webroot/.htaccess

追加する設定は、mod_rewriteの下記の設定で、URLの基点となるパスを指定しておきます。


RewriteBase /apps

例えば、/var/www/cakephp/app/webroot/.htaccessのファイルには下記のように設定します。


<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /apps
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>

これだけで一通りの設定が完了です。


http://www.example.com/apps/コントローラ名/アクション名

にアクセスすれば、今までのページが見えるようになっているはずです。

ちなみに、画像ファイルやJavaScript、CSSなどのパスもそれぞれ/apps以下となるため、直接パスを書いているテンプレートやレイアウトファイルなどは一通り編集する必要があります



変更したURLを元のパスに戻す


これは余談ではあるんですが、今回の目的はDocumentRootを他のコンテンツのために明け渡したいということであって、決してURLのパスを変えたいというものではありません。

そこで、Apacheの設定ファイルに下記のようにAliasを設定することで、DocumentRootをCakePHPが使わなくても元々のパスを使い続けることができます。


Alias /apps/ /var/www/cakephp/app/webroot/
Alias /foo/ /var/www/cakephp/app/webroot/

/fooはCakePHPのコントローラ名です。

前述の設定により、通常は


http://www.example.com/apps/foo/bar


というアクセスをしなければならないのを、


http://www.example.com/foo/bar


という元々のパスに戻すことができます。

ただし、コントローラごとにAliasを設定しなくてはならないのと、CakePHPの画像ディレクトリ(/img)やJavaScriptディレクトリ(/js)やCSSディレクトリ(/css)などは一般的過ぎる名前のため、Aliasの設定はしないほうが無難かもしれません(他のDocumentRootを使うコンテンツと競合するので)


今回の設定変更は、先ほどのmod_rewriteでのRewriteBaseの追加により、http://www.example.com/foo/barのURLでアクセスをしてもベースとなるURLを/appsに置き換えられて内部的にリクエストが投げられるため、URLをごまかすことができます。

URLを変更する影響が大きい場合は、このように設定してもよいかもしれません。