カテゴリー: programming

Java8 + AWS Lambda + API Gateway で遊ぶ

 この記事では Java8 で書いたメソッドを AWS Lambda に実行させ、その実行結果を API Gateway を用いて外から取得できるような簡単なアプリケーションを作成していきます。

 めっちゃ雑にいうと、Java で String を返すメソッドを書いて、その文字列を返すAPIを作るというようなことをやります。

 この際、String を返す Java のコードを実行するのが、AWS Lambda の役割で、その実行結果を取得できるエンドポイントを提供するのが API Gateway の役割になります。

 また、ちょっと手を加えるだけで簡単に JSON を返す API を作ることもできるので、使い方次第では遊べるツールになるのではないでしょうか?

固定した文字列を返すAPIを作成する

 まず、あるエンドポイントを叩くと、ただ文字列が帰ってくるだけのAPIを作成しながら、AWS LambdaAPI Gateway の使い方を確認していきます。

Gradleプロジェクト作成〜実行対象のメソッド作成

 gradleで適当にプロジェクトを作成してください。手元で実際に使った build.gradle ファイルは以下のような具合です。

 続いて、AWS Lambda で実行する対象のメソッドを作成します。めっちゃ簡単なので「馬鹿にしてんのか!」って思うかもしれませんが、まあチュートリアルですので…。

あとはプロジェクトルートから ./gradlew builde を叩いて build/libs 以下にできた jarAWS Lambda にアップロードするだけ!めっちゃ簡単じゃないですか?

AWS Lambda への jar のアップロードと実行

以下のように進めてください

1. AWSのマネジメントコンソールから Lambda を探してクリックします
2. create a lambda function ボタンをクリック
3. 適当なテンプレート一覧が出てくるが、スルーして右下の skip をクリック
4. Name を適当に埋め、Runtimejava8 を選択
5. zip をアップロードできるようになるので、さっき作った jar を選択する
6. Handlerのところにはさっき作ったメソッドを指定。上の例だと com.github.gochiusa.lambda.routes.api.v1.text.Words::get という感じで {package}.{class}::{method} という感じでしていすれば良い。残りの項目も適当に埋める。

適当にすすめると、左上に test というボタンが出てくるので押せば、晴れて AWS Lambda上 で、自分が書いた Java8 コードが実行されてくれる。だいぶ説明を端折っているので詳しくは AWS のドキュメントを見ればいいと思います。

API Gateway でエンドポイントを作る

マネジメントコンソールから API Gateway へ行き、create API をクリック

スクリーンショット 2016-02-28 0.40.49

API の名前を適当につけ先に進む

スクリーンショット 2016-02-28 0.40.57

create Method をクリックする

スクリーンショット 2016-02-28 0.44.14

以下のような感じで適当に各フィールドを埋める。ここで指定する lamda resion はさっき作った lamda function が置いてあるリージョンを指定。lambda function は作ったやつの名前を入れる。

スクリーンショット 2016-02-28 0.41.53 1

以上がおわったら Save を押して、Deploy API のほうに進めば、"ご注文はうさぎですか?" とだけ返してくれる最高なAPIが出来上がる。

JSON を返す API を作る

 JSON を返す lambda function も簡単につくれます。AWS Lambda は、POJO を返す Lamda function を実行すると JSON のレスポンスに変換してくれるみたいでめっちゃ楽しそうです。具体的にはこんなAPIがつくれます。

まずは、上記の感じ構造を持った POJO を定義しましょう。

んであとは、関数の戻り型、戻り値をこいつにしてやればOKです。

ソースコードはここにまとめて置いてあるのでまあ適当に見ていただければ…。

おわりに

 AWS Lambda で Java のコード動かしている記事があんまりなかったので書いたが、だいぶ雑なので迷ったらドキュメントか、他の優しい人が書いた記事を読むと良いと思います。AWS Lambda と API Gateway、どちらも素晴らしいサービスですね。どんどん活用していきたい…。

謝辞など…

 実はWantedly さんでやっていた Serverless Framework の勉強会に参加させていただき、この周辺のものに触れることができました。Wantedlyさん、ありがとうございます…。この記事の内容に近いもののハンズオンでしたので、AWS LambdaやAPI Gatewayに初めて触る方は、まずそちらを試して見ると良いと思います。

Swift2で静的なDIを実現する謎のソースコード

とりあえず書いたら動いたけど、なんだかよくわかっていないのでコードだけ貼っておきます。元ネタはここ。元ネタではAppContextで頑張って配線しているけど、めんどうくさいのでどうにかならんのか、と思っていろいろいじってたらこうなった。

いろいろ分かったらまとめて記事を書きます。XcodeのPlaygroundで動いたので、貼っていじってみるとよいのではないですかね。

プルリクエストを便利に取得できるアレ

ReSwiftのサンプルコードの仕組みをRxSwiftで実現する

 ReSwiftのサンプルコード程度であれば、RxSwiftの機能を使って自前で実装ができてしまうのでは、と考えたので実際にやってみました。不必要に依存ライブラリを増やしたくないという気持ちがモチベーションとなっています。まずはReSwiftのサンプルコード的なものを確認するところから始めます。

 ちなみにこの記事は ReSwift を使い始めて数時間の人間が書いているので、間違っている可能性が大いにあります。嘘を嘘と見抜きながら読んでいただけると幸いです(そして、コメント欄で刺してください)。

ReSwiftとは?

 ReduxのSwift実装らしいです。そもそもReduxがよくわからないのですが、そんなものわかってなくても動くコードはかけそうでした。ということで、とりあえずコードを書いて読んでから、小難しい記事を読んで理解してく方針にして、Redux および ReSwift の解説は他の記事に譲ります。

簡単な数字のカウントアップ・アプリケーション

 まずはReSwiftを用いてどのようにアプリケーションを構成していくか、ごくごく簡単な数字のカウントアップ・アプリケーションの例を用いてみていきましょう(ただし実際にはこんな単純すぎるアプリケーションに小難しいアーキテクチャを採用するメリットはないと思っています、あくまで例示としてのお話です)。まず CocoaPods でも Carthage でもなんでもいいので適当に ReSwift を導入してください。

 ReSwiftではアプリケーションの持つ状態をStateTypeプロトコルを持つ構造体 or クラスにまとめるようです。

 つづいて Action の宣言です。今回のアプリケーションに発生しうるアクションとして数字をカウントアップするアクションとカウントダウンするアクションを宣言してみます。

 簡単ですね?さて続いては、アクションを解釈して、新しい状態を発行する役割を持つ Reducer を実装してみましょう。状態を変化させるのではなく 新しい状態を発行する という点に注意しながらコードをみてみると良いと思います。

 そんなに難しいものではなく、action がもし、CountActionIncrese 型だったら、現在の状態のcounterをカウントアップしたものを生成して返すというだけです。Decrease も同様に考えれば同じようなものですね。

 最後に皆さんおなじみの ViewController を実装してみましょう。まずは、ストーリーボードに適当にラベルとボタンを配置してください。

スクリーンショット 2016-02-12 7.12.03

 つづいて、ViewController に label と ボタンのアクションを紐づけて、以下のようなコードを記述します。

 各アクションで文字通り Action を発行しているだけで、ラベルのテキストを書き換えたりカウンターの状態(数字)を書き換えたりしていない点が非常に重要なポイントです。アプリケーションの状態の書き換えは、Reducerで行います。そして、ビューの書き換えは newState で行います。こうすることにより、状態遷移(実際には新しいステートの発行)の記述と、ビューへの反映を分離しています。

 以上のような簡単なアプリケーションだとしても、多分コードみただけじゃ、感覚はつかめないのではないでしょうか?簡単なのでXcode起動して、ちゃちゃっと実験してみてください。5分で終わるので絶対自分の手でコードを書いてみてください。

RxSwiftを使って、上述の形式のコードを実現する

 細かいことは抜きにして、RxSwiftを使って同じ動きをエミュレートしてみましょう。まずは state, action, reducer の定義から。

 ほとんど変化はないですね?次に ViewController は次のような感じ。ちょっと記述は長くなりましたが、だいたい読み替えは可能なのではないでしょうか?

 もちろん、厳密なお話をするとおなじものにはなっていないのですが、ちゃんとプロトコルと基底となるコードをちょっと書いてあげれば、たちまち同じようなものになるのではないでしょうか?(自分の理解が間違っていたらご指摘をお願いします:ReSwiftを触ってまだ1日目です)

おわりに

 楽なので、よくやってしまいがちなのは、ViewControllerに var で状態を宣言して、イベント処理時に変化させていくという感じの実装ではないでしょうか?このサンプルレベルの簡単なアプリケーションであれば、その実装は正しいと思います。シンプルでわかりやすいので、別にそれで全然かまわないと思います。

 しかし状態の数が増えると、結果としてアプリケーションの取りうる状態の数は、各プロパティの取りうる状態の組み合わせ(高校で習った数学的なお話)になります。状態が10個程度を超えてくると、もうアプリケーションコードを読むのが辛くなってきたり、一つの変更が別の箇所に影響してしまい、意図しない動作を引き起こしてしまうなどということがあるのではないでしょうか。

 自分もまだ ReSwiftRedux がどういうものがわかっていませんが、状態の遷移とビューへの反映の混在を分離するところから、まずはじめてみようと思っています。

Swiftで同期HTTP通信を走らせる

 NSURLSession を使って通信を走らせる場合、コールバックやデリゲートなどで後処理を指定する非同期通信となります。基本的に通信処理を非同期で扱うのは正しいと思います。しかし、コールバックやデリゲート、FutureObservable などに縛られず処理を書きたいとなると、同期通信でないと困ります。

 そこで強制的に同期通信にしてしまうコードを書いてみました。これで晴れてコールバックを渡す必要も無く、デリゲートを指定する必要もない。そして FutureObservable に包む必要のない状態になりました。

 実際にUI層から呼び出す前に、適当なタイミングで Future なり Observable なりに lift すればよいので、同期である方が柔軟な設計が可能になるのではないかなと思います。execute の戻り型が、 Either<NSError, HttpResponse> とかになっているとさらに良さそうですね。

 純粋な HttpClientFutureObservable に依存する必然性がないので、このコードが安定したものであれば、使っていきたいと考えています。iOS開発に詳しい方、このコード実際どうなんですかね?(丸投げ)

MVC再考 (1)

どの文献を参照しても共通していること

  • ユーザーインターフェースを持つアプリケーションを Model/View/Controller の3つにの部分に分離する

解釈が難しい点

  • モデル・ビュー・コントローラの責務と関係
    • 特にコントローラの責務
    • コントローラとビューの関係性
  • 各プラットフォームにおいて、上記の責務をどう実装に落とし込むのか
    • ほんとうにビューとコントローラは分離できるのか?
    • そもそもビューとコントローラを分離する利点は?

分離とは?

Model-View-Controller に分離するパターンを見ていくが、クラスに分割するだけが分離に相当するわけではない

  • サブルーチン・メソッド
  • クラス
  • パッケージ
  • モジュール

MSDNのMVC記事まとめ

MSDNの記事がわかりやすかったのでまとめた

背景

  • コンピュータシステムの多くは データ を取ってきて、 ユーザ に表示する
  • ユーザがデータを変更したら、 データストア の内容を更新することもある

そのため、データストアとUI間のデータの流れに目がいきがちであり、パフォーマンスのため、またはコード量を減らすために、しばしばその処理を密結合にしてしまうことがある。

このアプローチには2つ問題点がある。

  • UIはデータストアと比較して頻繁に変更が入る傾向にある
  • 複雑なアプリケーションでは、単にデータストアからデータを送受信するだけでなく、複雑なビジネスロジックが存在する傾向にある

問題

ドメインロジックとUIの変更をそれぞれ手軽に独立して行えるようにするためには、Webアプリケーションをどのようにモジュール化すれば良いのだろうか

前提

問題を解決する際に、次のようなことを考慮する必要がある

  • UIロジックはビジネスロジックに比べて頻繁に変更が入る傾向に有る
    • 特にWebアプリケーションにおいては顕著
    • 例えば、新しい画面が追加されたり、すでに存在するページのレイアウトが変更されたりすることが多い
    • したがって、サーバー側に処理をまとめたシステム(シンクライアントシステム)は、アプリケーションを再配布しなくて済む
    • プレゼンテーションコードとビジネスロジックがひとつのオブジェクトとして結合している場合、UIだけを変更したくてもビジネスロジックを含んだオブジェクトを変更しなければならない
    • これはUIの些細な変更であっても、エラーを誘発したり、再テストが必要だったりすることを意味する。
  • 場合によっては、アプリケーションは同じデータを異なる形で表示することがある
    • 例えば、アナリストはスプレットシートを好み、マネージャは円グラフを望むことがある
    • またそれらを同時に表示することも十分に考えられる
    • もしユーザがデータに変更を加えた場合、すべてのビューに反映されるべき
  • デザインと複雑なビジネスロジックを作る作業は全く異なるスキルであり、両方のスキルを持ち合わせる人は滅多にいない
    • そこで、これらの作業はバラバラに実行できることが望ましい。
  • プレゼンテーションはデータソースからデータを取得し、データをフォーマットして表示する
    • 表示されたデータに応じてユーザーはなんらかのアクションを行い、そこからうまれた変更を反映させるためにビジネスロジックへ制御が渡る
  • WebアプリケーションはHTTPのステートレス性の上で、異なるアクションを実現しなければならない
  • UI層のコードは、ビジネスロジックと比較して、デバイスに依存する傾向にある
    • したがって、もしプラウザベースのアプリケーションからモバイル端末などに移植したい場合はUI層のコードを置き換える必要がでてくる
    • UIに関するコードとビジネスロジックを分離しておくことは、移植を素早く行えるようにしたり、移植後のビジネスロジック層のエラーを減らしたりすることに役立つ
  • UI層のテストは難しくて、実行に時間がかかる傾向にある。したがってUI層と直接結びついているコードを減らすことはテスタビリティ向上につながる。

解決策

Model-View-Controllerパターンは、ドメインモデル、プレゼンテーション、ユーザーからの入力に対するアクションの3つのパートに分離をする[Burbeck92]。

  • Model: モデルは状態に応じたアプリケーションドメインの振る舞いを扱い、主にControllerからの操作に応じて状態を変化させる。
  • View: ビューは表示される情報を扱う
  • Controller: マウスやキーボードなどのユーザからの操作を解釈して、モデルやビューに適切に伝える

mvc

  • ビューとコントローラーはモデルに依存してよい
  • モデルはどちらにも依存してはいけない
    • これにより、モデル層とプレゼンテーション層のビルドやテストをバラバラに行うことができるようになる
  • ビューとコントローラーの分離はリッチクライアントアプリケーションでは二の次となる
    • 実際のところ多くのUIフレームワークではひとつのオブジェクトにビューとコントローラーの責務をおいているものが多い
    • 一方、Webアプリケーションにおいてはビュー(ブラウザ)とコントローラー(HTTPリクエストをさばくサーバーサイドコンポーネント)は明確に分かれている。

Model-View-Controller はUIロジックとビジネスロジックを分離するための基本的なデザインパターンである。有名であるがゆえに、多くの間違った説明がなされている。

特に Controller は色々な文脈で異なる説明がなされている。ただし、Webアプリケーションにおいてはビューとコントローラーは明確に分かれているために、こういった曖昧さを解消するためには丁度良い。

passive型 MVC

Steve Burbeckは、In Application Programming in Smalltalk-80: How to use Model-View-Controllerの中で passive 型と active 型の2種類の MVC のかたちについて説明している。

passive型MVCはあるコントローラがモデルを排他的に制御するときに用いることができる。コントローラはモデルを変更し、ビューにモデルが変更されたので内容を更新する必要があることを伝える。このやり方だと、モデルはビューやコントローラと完全に独立した形になり、モデルはビューに変更を伝える手段がない状態になる。HTTPプロトコルなどがこのパターンに合致する。ブラウザがサーバーから更新を受け取るための、この上なくシンプルな方法である。ブラウザはユーザからの入力に応じてビューを表示するが、サーバー上のデータの変化を検出することはできない。ユーザが明示的に更新を問い合わせる必要が有る。

passive

active型 MVC

active 型はコントローラが関わらなくてもモデルの状態が変化する際に用いられる。このモデルの変化は他の入力ソースからデータの変更が発生してしまう場合に起こりうる。基本的にはその変更はビューに反映されるべきである。電光掲示板を作るときにはこうしなければならないだろう。表示内容に変更があった場合、データ取ってきてビューに反映させる必要がありそうである。

しかし、MVCパターンを用いる大きな目的のひとつに、モデルとビューを分離するというものがあったことを思い出そう。モデルがビューに変更を通知するとなると、ふつうにやれば依存関係が発生してしまう。そこでGoFのObserverパターンを思い出そう。これはオブジェクト同士の依存を避けつつも、変更を通知することを実現するデザインパターンである。それぞれのビューはObservarインターフェースを実装して、モデルを購読する。モデルが変更されたら、登録されているすべての observer に通知を行うという仕組みになる。このやり方はしばしば、publish-subscribe と呼ばれる。モデルはビューに関する情報は一切必要としない。たとえば、メニューのオプションを使えなくする必要があるなど、コントローラーがモデルの変更通知を必要とする場合、すべてのコントローラはobserverインターフェースを実装する必要がある。また、たくさんのビューがある場合、それぞれ異なる領域ごとにモデルを定義し、各ビューは関心のあるモデルのみを subscribe すると良いでしょう。

mvc

activemvc

もたらされる影響

MVCのもたらす恩恵

  • 複数のビューをサポートできる: モデルとビューが分離されているため、モデルからビューへ直接の参照は存在しない。したがって、同時に同じデータを様々なビューで表示することができる。これはWebアプリケーションにおける複数のページは同じモデルオブジェクトを使っていることがあることからもわかるだろう。またユーザがページの見た目を変更できるようなWebアプリケーションの存在からも理解することができるだろ。そういったページは同じモデルから得られた同じデータを、異なる方法で表示することができている。
  • 変更に強くなる: UIはビジネスロジックと比較して変更が生じやすい。ユーザは異なる配色やフォントやレイアウトを好むかもしれないし、デバイスに特化したインターフェースを望むかもしれない。モデルがビューに依存していないので、新しいビューのセットをシステムに追加しても、モデルは何の影響も受けない。結果として、変更の影響はビューだけに限られる。この部分をさらに細分化したパターンとして Page Controller と Front Controller というパターンがある。

MVCのもたらす弊害

  • 複雑になる: MVCパターンは遠回りなやり方であり、システムを複雑にする。またデバッグの難しいイベント駆動なコードベースになる傾向にある。
  • 頻繁な更新に対するコストが大きい: モデルとビューを切り離すことは重要だが、決してモデルがビューの存在を無視してよいということではない。モデルが頻繁に更新を行うような場合、ビューにそれを反映させるコストは大きなものとなるだろう。特にグラフィカルなユーザーインターフェースにおいては顕著になる。このような10教科ではビューは描画に失敗することもあるだろう。したがってモデルを作成するときにはビューを念頭に置くことは重要だ。場合によってはモデルの複数の変更をまとめてビューに通知するなどということも必要なはずである。

Document-View パターン

Model-View-Controllerの責務分離は行ったまま、ViewとControllerを結合 したものを Document-View パターンとよんでいる。今日の、多くのGUIプラットフォームではこのパターンが用いられている。VC++のMFCなどはこのパターンの実例のひとつになる。

単一責任の原則(SRP)

Single Responsibility Principle(単一責任の原則)は、Tom DeMarco の “Structured Analysis and Systems Specification” と、Meilir Page-Jones の “The Practical Guide to Structured Systems Design” で説明されている。この原則は次のようなものだ。

クラスを変更する理由は複数存在してはいけない

なぜこうする必要があるのか?

 役割が単一なクラスの仕様要求が変化した場合、その変更部分は浮き彫りになり、どのように変化したのかわかりやすい。これは、ほとんどの人が納得するはなしではないだろうか。

 しかし、役割が複数あると、その1つ1つが変更理由になってしまう。複数の変更理由によってクラスが変更されると、変更部分がぼやけてしまう。また、ある理由により変更した部分が、他の役割に影響してしまい、連鎖的に変更が必要になるなどといったケースが生じうる。これは もろい設計 にあてはまる。もろい設計がソフトウェア開発にどんな影響を及ぼすかは、前に書いた記事を参照されたい。

役割とはなにか?

 「役割=変更理由」と定義している。この判断が非常に難しいケースも多々有る。

変更の理由が変更の理由たるのは、実際に変更の理由が生じた場合だけである

 変化の兆候がないのにSRPを含めた原則を適用するのは賢明ではない。原則を適用することを目的にしちゃいけないというのは、いつも気に止めておかなければならないなぁと思います。

結合している役割を見つけそれらを分離する作業は、ソフトウェア設計の本質である

 非常に身にしみる言葉。

どのように分離するのか

たとえば、次のようなモデムインターフェースを考える。

 最初の2つのメソッドは接続の開始と終了、すなわち接続管理の機能を持つ。後半の2つのメソッドはメッセージのやりとりに用いる。この2つの役割を分離すべきかどうかは今後このアプリケーションがどのように変更されるか次第。たとえば接続管理の機能が影響を受けるような変更がはいる場合は、sendrecv のみを呼び出しているクラスをリコンパイル、リロードしなおさなければならない。もっとも単純にこの状態から打破するには、以下のように分離すればよい。ModelImplはその場しのぎではあるが、すべての依存関係が解消しているはずだ。

oymhIIrAIqnELN19B4bqpaZCoqlDKQZcqbOeJirBqR1ApaWiqWJoYbAJopGqkMgvk3BXEfppWOgJIpBpyy0QKZ8JSpGAylAj589BYZBpqa7QWEQbbnP0D29RO5EZgwlWcv-IckkPMv2Zm8La59eS0000

 テスト主導の開発方法に従えば、設計に怪しい兆しが現れる前に、複雑な役割を持ったクラスは分離されるはずだ。なぜならば、複雑な役割をもったクラスのテストを書くのは非常に難しいからだ。しかしそれでもなお、「硬さ」や「もろさ」を感じる場合には FacadeProxy などを使ってリファクタリングするとよいそうだ[要確認]。

余談

 アジャイル開発の奥義を読むまでは、この原則の名前からして、なぜ「クラスの役割は複数存在してはいけない」という内容ではないのかという疑問を持っていた。読んだ結果自分なりには、「変更」という観点に立ったときにはじめて「役割」ごとに分離する必要がでてくるから、「クラスを変更する理由は複数存在してはいけない」と主張しているのではないかと思った。

 たしかに役割は1つのほうが、見通しがよいかもしれないが、複雑にはなる。変更がとうてい生じないような場所に関して、無駄に分離してコードを複雑にする必要がないという意味を込めたい場合、たしかに「クラスを変更する理由は複数存在してはいけない」のほうがよい言葉だと感じた。この解釈自体は、完全に自分が読んだ感想なので、なにか間違っているなどの指摘がある方はコメントいただけるとありがたい。

アジャイル設計の本を読んでいる

 SOLID原則良いなぁ(←適当)…と思いつつ、それが書かれているアジャイルソフトウェア開発の奥義を読んでいなかったので、最近ちらちらと読み始めた。開発プロセスのあたりにはあんまり興味がわかないので、さらさらと軽い感じに読み飛ばしている。開発プロセス関連のはなしはなるほどなぁとは思うし、全員が内容を理解した上でやっていくと意味はありそうなんだけど、現実的にそんなことは起こり得ないと思っていて、学習コストが必要になる開発プロセスに意味はあるのか…とか偉そうなことを思っている。まあそんなことはどうでもよくて、第2部からのアジャイル設計の部分は本当に参考になるなぁ…と思いながら読んでる。

注意:この文章はポエムです

アジャイル設計とは?

 本によると、アジャイルはソフトウェアを少しずつ開発する手法だそうだ。基本的にラビットハウスでは完成させたソフトウェア(完成とは…という話になるが)を納品するようなお仕事ではなく、ソフトウェアを少しずつ拡張していくスタイルなので、まさに合致している。

腐敗するソフトウェアの兆候

 さて、そんな「少しずつ開発していく」スタイルの場合に、どうやってソフトウェアを設計できるのだろうか?という問いが冒頭にくる。少しずつ開発するとしても、最終的な目的は大きなソフトウェアを保守可能な状態で動かすことにあるので、その段階まで持っていけるような状態を各工程(アジャイル言葉ではイテレーションとかよんでるっぽい)で踏んでいかなければならない。それを阻む要素として次のような項目が挙げられている。

  1. 硬さ: 設計変更が困難
  2. もろさ: 設計が壊れやすい
  3. 移植性のなさ: 設計の再利用が困難
  4. 扱いにくさ: 正しい設計をするのが困難
  5. 不必要な複雑さ: 行き過ぎた設計
  6. 不必要な繰り返し: マウスを使いすぎ
  7. 不透明さ: 整然としない表現

 なるほど、たしかにという気持ちになる。こういった構造を作らないための原則として、有名なSOLID原則が紹介されている

  1. 単一責任の原則(SRP: Single Responsibility Principle)
  2. オープンクローズド原則(OCP: Open-Closed Principle)
  3. リスコフの置換原則(LSP: Liskov Substitution Principle)
  4. 依存関係逆転の原則(DIP: Dependency Inversion Principle)
  5. インターフェース分離の原則(ISP: Interface Segregation Principle)

 設計に関するいろいろな本がでているが、基本的にまずこれを守れてない上でそういうものを取り入れようとしても、かえってカオスになるケースのほうが多いのではないかなぁと個人的に感じている。まあ採用しているアーキテクチャなど状況によりけりでしょうけど。とはいえ、ソフトウェアのうまくいっている部分に無理やり、この5原則を適用しにかかるのはやめなさいという忠告も書かれている。そういう部分にはいろいろ変更がくわわってカオスになってきてから、原則に則ってリファクタしても遅くないと思うので、なるほどなぁ…と思った。こいつらについては、別記事で個別にみていく。

硬さ

 ソフトウェアのちょっとした変更が難しい、1行の変更をしようとすると芋づる式に変更箇所が膨らむということを、硬い設計とよんでいる。これって要はその一行に他のモジュールが依存しているのと変わらないので、結局のところモジュールに分割している意味がないってことな気がする。そうならないようにちゃんと考えて書けよ〜ってことなんでしょうね。

もろさ

 1つの変更により他の多くの部分が壊れてしまう傾向のことを、もろい設計というらしい。そういった箇所は、ソフトウェアの部品としての抽象化に失敗しているということを指すのではないかなと思った。

移植性のなさ

 他のシステムでも役に立つ部分を含んでいるのに、その場所から切り離すことが困難・リスクをともなうような状態。これも機能の抽出に失敗してしまっているときに起こるようなことかなと解釈した。移植するかしないかはともかく、移植できる状態にあることは良いことだとも思う。ただ他のシステムでも役に立つが、移植可能な状態にするのがあまりにも厳しいものはあると思うので、そういった部分はまた別の話かなぁとも思う。難しかったり前例がなかったりする実装はそうなってしまいがちなのはしょうがないと思う。バランスが大事。

(ソフトウェアの)扱いにくさ

 設計構造を保持するやり方が難しく、これまでの設計とは違うが圧倒的に簡単に機能を追加したり変更したりできてしまう場合、そのソフトウェアは扱いにくい。これは非常に重要だと思っていて、難しい設計技法とかをあたまのいい人たちが頑張って取り入れても、そこに追っついていける人が開発メンバでなければ、思想のまったくことなる実装が入り組み、余計複雑なソフトウェアを生み出してしまうと思う。同じ設計手法を用いるとしても、その考え方を理解して、やり方を揃えていけるのであればきっとうまくいくのではないかなと思う。そうでない場合は失敗するだろう。当然のことのはずだけど、新しいことを覚えて感動しているときには、こういうバランス感覚を忘れてしまいがちになるはずで、気をつけなければならない。

(環境の)扱いにくさ

 開発環境がクソゴミで非効率なことを指す。コンパイルに時間が掛かる場合に、設計を維持できないとわかっていてもモジュール分割してしまうみたいなこと。差分管理システムへのコミットに時間が掛かるようなことも例として書かれていた。まあgit使ってる分にはよほどなことがない限りこんな感じにはならないかなと思う。モジュール分割については、もうちょっと考えないと本のいっていることの意味が理解しきれない。そもそもコンパイル時間を減らすことを直接の目的としてモジュール分割するのはどうなんだろうか?あたまが悪いので自分には判断がつかない。

不必要な複雑さ

 設計している時点で不必要な要素が含まれている設計のことを指す。これ気をつけないとやってしまいがちですよね。結局あとから手直しするハメにあったり、つかわれることがないまましれっと削除コミットをしたり。このようなことをすると不必要にソフトウェアは複雑になるそうだ。たしかに分かるような、分からないような。不必要なコードをコミットするのは読み手にとっては非常につらみを増やすだけだが、それがソフトウェアの複雑さに結びつくかどうかの部分が自分の中であんまりはっきりとイメージできないので、これからも継続的に考えていきたい。

不必要な繰り返し

 同じようなコードが繰り返しでてくる。読み手がつらい。こういうコードのレビューをしているとき、推敲されていない文章(この記事のように…)を読んでいるかのようでつらい気持ちになる。このリポジトリはお前のブログじゃねえんだよという気持ちにさせないように、ちゃんと自分の書いたコードは推敲しないといけない。でもこれを応用すれば、嫌いなレビュワーを苦しめることができるということか…(嫌いなレビュワーとは…)。そしてこれを放置していると複雑さにつながると思う。

不透明さ

 わかりにくいモジュールのこと。他人が見て、わかりずれえなってところはこれに該当するんじゃないだろうか。モジュールという言葉はJavaのパッケージ的なものだけでなく、普通にサブルーチン、クラスレベルを含んだ話だと解釈した。書いている本人はわかったつもりだけど、誰かが読むと意味不明なのは、日本人の書く英語のようなものなのかなぁと思う。

アジャイルな設計とはなんなのか?

 アジャイルな設計とは、プロセスやイベントではなく、ソフトウェアの構造や可読性を向上させるために、原則・パターン・プラクティスを継続的に適用する行為だそうだ!(意識たかそう!!!)そのための原則とかパターンが8章以降に書かれているので随時まとめていきたい。

乱数生成を純粋化する

乱数の生成についての簡単な例を見てみよう。scala.util.Random が用意されているのでそれを使えば簡単だ。

nextInt メソッドは呼び出しのたびに返ってくる値が異なる。呼び出しのたびに rng の状態が遷移していることが想像出来る。たとえば、ランダム性を利用したメソッドのテストを書こうとした場合、テストを再現可能にする必要があるが scala.util.Random では難しそうである。仮に乱数ジェネレータを直接扱うとしても、ジェネレータの状態を揃えてあげる必要がある。こういった状態に対処するために、副作用を使用しないという原点に立ち返ろう。

純粋関数型の乱数生成

状態を遷移させるのではなく、新しい状態を返すという気持ちでやっていく。乱数ジェネレータの場合は、ジェネレータの nextInt を呼び出すと、値と新しいジェネレータを返すという感じになる。もちろん、もとのジェネレータは状態遷移しない。したがって、もとのジェネレータの nextInt を何回呼び出しても、同じ値と新しいジェネレータが返ってくる。コードで表現すると次のようになる。

こいつは以下のような挙動を示す。

見ての通り、nextInt は純粋な関数になった。

ステートフルなAPIと向き合う

乱数生成の例で見たようなことと同じような方法で、すべてのステートフルAPIは純粋化できる。基本的には状態を遷移させるのではなく、値と新しい状態を返してやるという方針になる。ただし普通にこれをやってみるとボイラープレートが多くて辛い気持ちになるだろう。たとえば RandomGenerator に対していろんな便利関数を定義してみる。同じことばっかりやっているように見えないだろうか?

すべて RandomGenerator => (A, RandomGenerator) な関数になっている。「状態を遷移させるのではなく、値と新しい状態を返してやる」という方針が関数に明確に現れてきている。この部分は繰り返し出現するのですこし抽象化しよう。type を使って RandomGenerator => (A, RandomGenerator)Rand[A] というエイリアスを張り付ける。すると見なれた mapmap2 などの高階関数が定義できて、かつそれを用いて上で定義した関数などを実装できるようになる。

遅延リストを作る

FP in Scala 第5章読書会の予習。Streamの作成については、半年前くらいに記事にまとめた。

リストの操作と無駄な中間リスト

リストへのmapやfilter操作の評価は、模式的には以下のような感じで行われる。

中間リストが無駄に生成されているので、mapやfilterといった高階関数をつかって合成性をおとさずに、つまりwhileループに頼らずなんとかしたい。そんなときに非正格性(non-strictness)を使うとよい。ということで「ワンランク上のリストの構築のしかた」を探っていく。

正格関数と非正格関数

「ワンランク上のリスト構築」をする前に正格性について確認を行っておく。
正格性とは関数の特性である。非正格関数では、引数の1つ以上を評価しないという選択が可能で、正格関数では引数が常に評価される。

Scalaの if 制御構造は非正格の一例。array.isEmpty の状態に応じて、then または else のステートメントが評価されない。

正確には、条件パラメータについては常に正格であり、各条件分岐後のステートメントは非正格である。Scalaで非正格関数を記述するためには、引数の一部を評価されない状態で受け取ればよい。具体的には以下のようにできる。

評価されない式をサンクとよぶ。これを評価するためには、空の引数リストを渡せばよい。() => A は実際には Function0[A] 型のシンタックスシュガーになっている。上の if2 関数はさらに簡単に以下のように書ける。

どちらの構文においても、評価されずに渡される引数は、関数の本体ないで参照されている場所ごとに毎回評価される。これを避けるためには、lazy キーワードを使って値を明示的にキャッシュすればよい。

遅延リストを作る

非正格にする方法はわかっているので、あとはリストでやったことに手を加えてやれば良い。
データ構造の定義は以下のとおり。

これを用いて、冒頭で示したリストへの操作の効率化を図っていくことを考える。

コンストラクタに潜む非効率性

Consデータコンストラクタを直接呼び出した場合、評価が無駄に走ることがある。

無駄な処理を減らすためにはキャッシュをさせてやればよい。以下のようにスマートコンストラクタを定義する。

Empty のスマートコンストラクタには型推論を助ける狙いもある。これらを用いると以下のようにできる。

各種関数の定義

foldRightmap, flatMap などを定義する

評価のされ方

以上のようにして定義した Stream の評価がどうされるかを最後に確認しておく。