うまとま君の技術めも

2015年新卒入社した社畜の勉強内容などなど

エクストリームプログラミング

エクストリームプログラミング

エクストリームプログラミング(XP)はアジャイルソフトウェア開発手法の1つである。

  • XPとは、効果のない技術的・社会的な古い習慣を捨て、効果のある新しい習慣を選ぶことである
  • XPとは、自分が今日やるべきことを十分に理解することである
  • XPとは、明日をよりよくしようとすることである
  • XPとは、チームのゴールに貢献した自分を評価することである
  • XPとは、ソフトウェア開発で人間としての欲求を満たすことである

価値・原則・プラクティス

価値というものはとても抽象的なものである。なので、「この1000ページのドキュメントを書いたのは、コミュニケーションに価値をおいているからです」という意見は、正しいかもしれないし、間違っているかもしれない。

逆に、プラクティスといものはとても具体的である。「毎朝10時にスタンドアップミーティングに参加する」というプラクティスは、参加出来ているかどうかを簡単に判断できる。

この価値とプラクティスの間には大きな隔たりがある。価値は普遍的なものであるが、プラクティスは状況によって大きく異なる。この2つの間のギャップを埋めるのが原則である。

VALUE_PRINCIPLE_PRACTISE

価値

コミュニケーション:Communication

チームによるソフトウェア開発で最も重要なのはコミュニケーションであり、チーム感覚や効果的な協力関係を生み出すために重要なものである。

シンプリシティ:Simplicity

問題を解決するための最もシンプルな方法を選択する。無駄に複雑な方法を選択する必要性はない。ただし、シンプルであるかどうかは状況によって異なるものである。

フィードバック:Feedback

最初に決めた方向性が、ずっと有効であることは無い。それが、ソフトウェアの要件・システムのアーキテクチャなどであっても同じである。経験をする前に何かを決めてしまうとすぐに駄目になってしまう。変化は避けられないものであり、対応するためにはフィードバックが必要となる。

勇気:Courage

勇気とは恐怖に直面したときの効果的な行動である。勇気を持って真実を語れば、コミュニケーションや信頼が強化される。勇気を持って新しい解決策を見つければ、シンプリシティが促進される。

リスペクト:Respect

チームメンバーはお互いに人として等しく重要である。ソフトウェア開発において人間性と生産性を同時に高めるには、チームに対する個人の貢献をリスペクトする必要がある。

原則

人間性:Humanity

ソフトウェアを開発するのは人間であり、人間性を保つために個人の欲求を満たすことが重要である。しかし、チームによるソフトウェア開発で難しいのは、個人の欲求とチームのニーズの両方のバランスを取り、みんなが自分らしくいられるようにすることである。

経済性:Economics

ソフトウェア開発における経済性には2つの側面があり、貨幣のタイムバリューとシステムやチームのオプションバリューである。貨幣のタイムバリューでは、今日のお金は明日のお金よりもバリューがあるとされる。将来のオプションバリューでは、ある機能を再利用できたとすると本来の目的だけに使うよりもバリューが高いということになる。

相互利益:Mutual Benefit

相互利益とは現在の自分・将来の自分・顧客に対する利益を求めることである。誰かにとって不利益となる解決策をとってしまうと、そこから敵意が生まれ、人間関係を引き裂くことになってしまう。

自己相似性:Self-Similarity

ソフトウェア開発においても、異なる規模の問題に対して、解決策の構造を新しい文脈にコピーしようとする。

改善:Improvement

「完璧な」プロセス・設計・ストーリーは存在しない。だが、「完璧に」プロセス・設計・ストーリーをやることはできる。改善によってソフトウェア開発の高みを目指し、今日できる最高のことをやるのである。

多様性:Diversity

お互いがよく似ているチームは、居心地は良いかもしれないが、機能的ではない。解決策を導き出すには多様性が必要である。

ふりかえり:Reflection

優れたチームは、どうやって仕事をしているのか・なぜ仕事をしているのかを考えている。なぜ失敗したのか・なぜ成功したのかを分析している。ミスを明らかにしそこから学ぼうとするのである。偶然に優秀になれる人などいないのである。

流れ:Flow

流れとは、すべての開発作業を同時に行い、バリューあるソフトウェアの安定した流れを生み出すことである。ソフトウェアのデプロイやインテグレーションの頻度を下げると、フィードバックの頻度が減り状況が悪化してしまう。

機会:Opportunity

問題を変化の機会と考えよう。その場を切り抜けるための問題解決しかしないサバイバルな姿勢では不十分である。より高みを目指すには問題を学習や改善の機会へと変換すべきである。

冗長性:Redundancy

重要で困難な問題は、複数の方法で解決すべきである。ひとつの解決策が失敗しても、その他の解決策で惨事を食い止めることができる。冗長性にコストが掛かるとしても、大惨事を回避できるならそのコストは支払うべきである。

失敗:Failure

失敗することにより解決策を導き出すのに必要な知識を得ることができる。その知識が得られたのであれば決して無駄ではない。ただし、よりよい方法を知っていながら失敗することを正当化するものではない。

品質:Quality

低品質を受け入れることでプロジェクトが早くなることはない。高品質を要求することでプロジェクトが遅くなることもない。むしろ、品質を高めることでデリバリーが高速になることが多い。品質基準を下げてしまうと、デリバリーが遅くなり予測できなくなってしまう。

ベイビーステップ:Baby Step

大きな変更は、大きなステップでやりたくなるものである。しかし、重大な変更を一気に行うのは危険であり不安を伴う。大きな変更が失敗してチームが無駄に後退するよりも、小さなステップで進むオーバーヘッドのほうが小さい。

責任の引受:Accepted Responsibility

責任とは割り当てるものではなく引き受けるものである。責任には権限が伴わなければいけない。このバランスが取れていないと、チームのコミュニケーションが破綻してしまう。

ラクティス

全員同席:Sit Together

チーム全体が入れる十分な広さのオープンスペースで開発すること。物理的に近くにいれば、コミュニケーションが促進される。ただし、近くにプライベート空間を用意するなどして、メンバーのプライバシーや自分だけの空間といった欲求を満たせるようにすること。

チーム全体:Whole Team

プロジェクトの成功に必要なスキルや視点を持った人たちをチームに集めること。1日に無理なくやり取りできる人数は12人までである。また、小数点以下の人間を入れてしまうと、情報の共有に時間を割く必要がでてきたり、チーム感が失われたりと、非生産的である。

情報満載のワークスペース:Informative Workspace

仕事の内容が分かるようなワークスペースを作ること。プロジェクトに関しがある人が近づいて見たときにが、抱えている問題などの情報が得られるようにしておこう。また、お菓子を用意したり、仕切りのある机を用意したりと人間のその他の欲求も満たす必要がある。

いきいきとした仕事:Energized Work

生産的になれる時間だけ働くこと。無理なく続けられる時間だけ働くこと。意味もなく燃え尽きてしまい、次の2日間の作業が台無しになれば、自分にとってもチームにとってもよろしくない。

ペアプログラミング:Pair Programming

同じマシンを前にした2人で、本番用の全てのプログラムを書くこと。これにより、お互いにタスクに集中したり、意見を出し合ったりすることができる。ただし、ペアプログラミングは疲れるプラクティスであるので、適度な休憩をはさみつつ進めると良いだろう。

ストーリー:Stories

「これまでのレスポンスタイムで5倍のトラフィックを処理する」といった、顧客に見える機能の単位を使って計画すること。そして、開発工数を見積もること。これにより、ビジネスと技術の観点からお互いにやり取りができる。

週次サイクル:Weekly Cycle

1週間分の仕事の計画をまとめて立てること。そして週の初めに、進捗の確認・着手するストーリーの選択・必要なタスクの見積もりを行うこと。週の終わりまでにデプロイ可能なソフトウェアを作ることが目的である。また、週次サイクルは、実験を行うための手軽で頻繁で予測可能なプラクティスでもある。

四半期サイクル:Quarterly Cycle

四半期分の計画をまとめて立てること。四半期に一度はチーム・プロジェクト・進捗・ゴールについて振り返ること。四半期は自然で広く受け入れられている時間の尺度であり、ビジネス活動ともうまく同期できる。

ゆとり:Slack

どのような計画にも、遅れたときに外せるような重要度の低いタスクを含めること。あとからストーリーを追加したり、多くのストーリーをデリバリーしたりするのは後からでもできる。

10分ビルド:Ten-Minute Build

自動的にシステム全体をビルドして、全てのテストを10分以内に実行させること。時間がかかると使用頻度が減り、フィードバックの機会が失われてしまう。

継続的インテグレーション:Continuous Integration

数時間以内に変更箇所のインテグレーションとテストをすること。インテグレーションのステップはプログラミングよりも時間のかかることが多い。インテグレーションに時間がかかれば、その分だけコストが上がり、予期しないコストも増えてしまう。

テストファーストプログラミング:Test-First Programming

コードを変更する前に、失敗する自動テストを書くこと。これにより、不要なコードの追加を防いだり、疎結合で凝集度の高いコードを書いたり、自然で効率的な開発のリズムを得ることができる。

インクリメンタルな設計:Incrementel Design

システムの設計に毎日手を入れること。システムの設計は、その日のシステムのニーズにうまく合致させること。小さくて安全なステップで稼働中のシステムを変更する経験を積み重ねていくと、設計にかける労力を遅延させることができるようになる。そうすれば、システムはシンプルになり、進捗は早くなり、テストが書きやすくなる。

まとめ

XPの目的は、圧倒的なソフトウェア開発の実現である。ソフトウェアはもっと安いコストで、もっと少ない欠陥数で、もっと高い生産性で、もっと高い投資効率で、開発することができる。

ソフトウェア開発において、良いチームの間には、異なるところよりも似ているところが多い。そのようなチームに共通していることを「エクストリーム」な形で抽出したのがXPである。

XPは、今日よりも素晴らしい明日を作り出す手助けをしてくれるだろう。

参考

アジャイルプラクティス

アジャイルラクティス

効果的なプラクティス無しにプロジェクトを進めてしまうと、問題が多発し品質の悪いソフトウェアを作り続ける羽目になってしまうことだろう。

そのような苦い経験をすると、同じ過ちを繰り返さないよう過去の経験を元に成功したプロセスを採用してしまいがちである。気づいたときにはプロセスが大きくなりすぎて身動きが取れなくなってしまう。そのような状況でさえチームはプロセスが足りないのが良くないのだと思いこんでしまい悪循環に陥ってしまう。

この状況を改善しようと業界のエキスパートが集まり、開発チームが迅速に作業し変化に対応できるよう「価値と原則」をまとめたのである。

アジャイルソフトウェア開発宣言 - 価値

  • プロセスやツールよりも、人と人同士の交流を重視する
  • 包括的なドキュメントよりも、動作するソフトウェアを重視する
  • 契約上の交渉よりも、顧客との強調を重視する
  • 計画に従うことよりも、変化に対応することを重視する

プロセスやツールよりも、人と人同士の交流を重視する

良いプロセスを使っていても、優秀な人材がいなければプロジェクトはうまくいかない。また、優秀な人材が揃っていたとしても、協調して仕事を行うことができなければ失敗する。協調性のないスーパースター集団よりも、うまくコミュニケーションを取れる平均的な人材を集めたほうがうまくいきやすい。

仕事を進める上で適切なツールを使うことはとても大切である。しかし、ツールに頼りすぎたり、大きくて複雑なツールを過度に導入することは状況を悪化させる。まずはホワイトボードや紙などのシンプルな道具を使ってみて、どうしてもうまくいかないのであれば他のツールを導入してみよう。

大切なのは、開発環境を整えるよりも開発チームを形成することのほうが重要だということである。

包括的なドキュメントよりも、動作するソフトウェアを重視する

ドキュメントを用意することは人間に情報を伝える手段としては適切であり、重要なものである。しかし、ドキュメントがたくさん作られてしまうことは不足するよりもタチが悪い。必要なのは、シンプルで重要な事柄のみを記載したドキュメントである。

新メンバーへ仕事の内容を教えるときに重要となるドキュメントは「コード」と「チーム」である。コードの処理内容に嘘はなく確実な情報源である。また、常に変化するロードマップなどは「チーム」の頭の中にある。これらを伝える最も良い方法は、人間同士で直接伝えることである。

契約上の交渉よりも、顧客との強調を重視する

ソフトウェアを開発する上で前もってプロジェクトの納期や条件を契約書で定めること自体が、そもそも無茶である。契約書で定めた内容の大半は途中で意味をなさなくなってしまう。

プロジェクトを成功させるためには、顧客からのフィードバックが欠かせない。顧客は契約書にすべてを任せず、開発チームに対して頻繁にフィードバックを行い密接にプロジェクトに関わるべきである。

計画に従うことよりも、変化に対応することを重視する

ソフトウェア開発において、遠い未来の予定を立ててもあまり意味がない。理由としては以下の3つであり、変化に対応する能力があるか否かが、プロジェクトの成否を分けるのである。 - ビジネスの状況が変わりやすい - 顧客は動いているシステムを見ると、要求を変更してくる - 開発時間を正確に見積もることは難しい

詳細な予定を2週間程度、おおまかな予定を3ヶ月程度作り、その先に関しては大雑把にしておくのが上手な計画の建て方である。こうすれば、綿密な計画を練っている時間は小さくなり、変化に対応しやすくなる。

アジャイルソフトウェアの12の原則 - 原則

  • 再優先事項は顧客を満足させることであり、価値あるソフトウェアを早い段階から継続的に届けることでこれを実現する
  • 要求変更を歓迎し、たとえ開発過程の後半であってもそれを受け入れる。アジャイルプロセスは、変化に対応することで顧客の競争上の優位性を確保する。
  • 実動可能なソフトウェアの納品を頻繁に行う。できるだけ短い期間で納品することを旨とし、数週間から数ヶ月間隔で納品する。
  • 顧客と開発者はプロジェクト全般を通じて日々ともに働かなければならない。
  • やる気のある開発者をプロジェクトの中心に据え、彼らが必要とする環境とサポートを与え、信頼して仕事の完遂を任せる。
  • 開発チームで情報を伝達する最も効果的な方法は、直接話し合うことである。
  • 実動するソフトウェアこそが進歩状況の尺度である。
  • 持続できるペースで開発する。そうすれば、スポンサーも開発者もユーザーもずっと一定のペースを確保できる。
  • 高度な技術と優れた設計への配慮は、アジャイル性を高める。
  • シンプルさが肝心 - やらなくていいことはしない。
  • 最高のアーキテクチャ、仕様要求、設計は自己管理能力のあるチームから生まれる。
  • チームは定期的にプロジェクトを見直し、より効果的な方法を考え、対応方法の変更や調整を行う。

再優先事項は顧客を満足させることであり、価値あるソフトウェアを早い段階から継続的に届けることでこれを実現する

ソフトウェア開発プラクティスに関する調査により、以下の調査結果が出ている。 - 最初に納品される機能が少なくて乏しいほど、最後の納品での品質はより高いものになる。 - 頻繁に納品すればするほど、最終的な品質はより高いものになる。

要求変更を歓迎し、たとえ開発過程の後半であってもそれを受け入れる。アジャイルプロセスは、変化に対応することで顧客の競争上の優位性を確保する。

開発チームは変化を恐れない。変化によって市場が求めているものをより深く学ぶのである。そして、変化に柔軟に対応できるようソフトウェア構造を柔軟に保つ努力を怠らない。

実動可能なソフトウェアの納品を頻繁に行う。できるだけ短い期間で納品することを旨とし、数週間から数ヶ月間隔で納品する。

最初の段階から実動可能なソフトウェアを頻繁に納品する。ドキュメントやプランを提示するだけでは不十分であり、実質的に意味はない。

顧客と開発者はプロジェクト全般を通じて日々ともに働かなければならない。

アジャイルな状態を保つために、顧客と開発チームやステークホルダーは密接かつ頻繁にやり取りを行うべきである。

やる気のある開発者をプロジェクトの中心に据え、彼らが必要とする環境とサポートを与え、信頼して仕事の完遂を任せる。

プロジェクトで重要なのは「人」である。プロセス、環境、管理などは二次的なものであり、それらが「人」に悪影響を及ぼすのであれば変更を必要とする。

開発チームで情報を伝達する最も効果的な方法は、直接話し合うことである。

プロジェクトの主要なコミュニケーションは「会話」である。仕様、計画、設計をドキュメントにすることを求めない。ドキュメントを作るのは重要かつ緊急な場合である。

実動するソフトウェアこそが進歩状況の尺度である。

進捗状況は実働可能なソフトウェアの量で判断する。ロードマップ上でのフェーズやドキュメントの枚数などで判断はしない。

持続できるペースで開発する。そうすれば、スポンサーも開発者もユーザーもずっと一定のペースを確保できる。

ペースを上げすぎると途中で燃え尽きたり手を抜いて大失敗することになる。明日できることは無理して今日中にやらない、常に最高の品質が保てるペースで仕事を進める。

高度な技術と優れた設計への配慮は、アジャイル性を高める。

プロジェクトの進行を早くさせたければ、ソフトウェアを常に健全な状態に保っておく必要がある。そのために、乱雑なコードを書いてしまったら、後回しにはせずその日のうちに書き直すのである。

シンプルさが肝心 - やらなくていいことはしない。

いきなりシステム全体を作ろうとせず、目的を達成するために必要なシンプルな方法を段階的に導入していく。予め必要となりそうなことを予想してもあまり意味はない。必要な作業だけシンプルに行えば、問題が発生しても柔軟に対処できるのである。

最高のアーキテクチャ、仕様要求、設計は自己管理能力のあるチームから生まれる。

チームは自己管理能力があり、一丸となって課題に取り組む。各個人がアーキテクチャ、仕様要求、設計に意見することはあるが、各個人が責任を負うことはない。責任はチーム全体にあるのである。

チームは定期的にプロジェクトを見直し、より効果的な方法を考え、対応方法の変更や調整を行う。

チームは、ルールや習慣などを常に見直す。環境は常に変化しそれに合わせて自分たちも変化しないとアジャイル性を保てないからである。

参考文献

アジャイル設計

アジャイル設計

アジャイル設計とはプロセスやイベント等の事ではない。ソフトウェアの構造や読みやすさを向上させるため、原則・パターン・プラクティスを継続的に適用する行為である。

ソフトウェアは時間が経つにつれ腐敗していく。汚いコードが溜まっていき、どんどんメンテナンスしづらくなってしまう。そうなると、ちょっとした変更でさえも大変な労力を必要としてしまう。

アジャイル開発者は可能な限りソフトウェアをきれいな状態に保ち、変更しやすい状態にしておく必要がある。だから、毎日、毎時間、毎分、ソフトウェアをリファクタリングする努力を怠ってはいけない。

設計の悪臭 - 腐敗するソフトウェアの兆候

次に示す傾向が1つでも現れたら、ソフトウェアが腐敗し始めた兆候である。

  1. 硬さ
  2. もろさ
  3. 移植性のなさ
  4. 扱いにくさ
  5. 不必要な複雑さ
  6. 不必要な繰り返し
  7. 不透明さ

硬さ

変更によって他の部分にまで影響が出てしまい、他の部分も変更を必要とする状態。
1つの変更で、依存しているモジュールを芋づる式に次々と変更しなければならないような場合を、設計が「硬い」という。

もろさ

変更によって他の部分が壊れてしまう状態。
モジュールのもろさが増すと、1つの変更でさえも予期せぬ自体を招くようになる。
もろいモジュールは、常にリファクタリングが必要とされるモジュールを探すと良い。

移植性のなさ

他のシステムでも役に立つ部分があった場合に、その部分を切り出すことが困難な状態。

扱いにくさ

ソフトウェアの扱いにくさと開発環境の扱いにくさの2種類がある。
変更をするときに、設計構造を維持しないやり方を選んだほうがやりやすいソフトウェアは「扱いにくい」。
設計構造を維持できるかどうかではなく、開発環境の非効率差を増やさないような変更をしたくなるソフトウェアは「扱いにくい」。

不必要な複雑さ

設計している時点で必要のない要素が含まれている設計は「不必要な複雑さ」である。
あとで必要になると思い、予め処理しやすい仕組みを取り入れておいた場合などに発生する。

不必要な繰り返し

同じようなコードが何度も現れるような状態。
システム内に重複しているコードがあると変更が大変になる。重複している箇所からバグが見つかった場合に同じような箇所を全て修正しなくてはならないのである。

不透明さ

わかりにくいモジュールは「不透明」である。
時間が経つにつれ、コードはどんどんと不透明になっていく。そういった不透明さを最小限に抑えるために常にリファクタリングする努力を怠ってはいけない。

設計を行う

  1. アジャイルのプラクティスに従って問題を発見する
  2. 設計の原則を適用して問題を分析する
  3. 適切なデザインパターン(設計パターン)を適用して問題を解決する

参考文献

SOLID原則 - the SOLID principles

SOLID原則 - the SOLID principles

ソフトウェア設計をより理解しやすく、柔軟で、メンテナンスしやすくする為の5つの原則である。
SOLIDとは以下の5つの原則の頭文字を取ったものである。

  • Single Responsibility Principle | 単一責任の原則
  • Open Closed Principle | オープン・クローズドの原則
  • Liskov Substitution Principle | リスコフの置換原則
  • Dependency Inversion Principle | 依存関係逆転の法則
  • Interface Segregation Principle | インターフェース分離の原則

Single Responsibility Principle - 単一責任の原則

クラスを変更する理由は1つ以上存在してはならない。

概要

1つのクラスは1つの役割(=変更理由)だけ持つべきである。
複数の役割を持っていると、何かを変更した際にそのクラスが持っている他の役割にも影響を与えてしまう可能性があるので良くない。

悪い例

Rectangleクラスが数学的な値の計算と図形の描画という2つの役割を持ってしまっている。

SRP_BAD

改善例

SRP_GOOD

Open Closed Principle - オープン・クローズドの原則

ソフトウェアの構成要素(クラス、モジュール、関数など)は拡張に対して開いていて、修正に対して閉じていなければならない。

概要

アプリケーションの仕様が変更されても新たな振る舞いを追加すれば対処できて、既存のソースコードは影響が出ないようにするべきである。
容易に機能を追加でき、既存のコードを修正する必要性がない状態。

悪い例

ClientクラスはServerクラスを利用しているので、Clientオブジェクトが別のServerオブジェクトを利用する場合は、Clientクラスを変更する必要がある。

OPEN_CLOSED_BAD

改善例

Interfaceに依存している場合は、Clientオブジェクトが別のServerクラスを使いたくなったら、Client Interfaceの派生クラスを追加すればよい。

OPEN_CLOSED_GOOD

Liskov Substitution Principle - リスコフの置換原則

派生型はその基本形と置換可能でなければならない。

概要

基底クラスのオブジェクトを使った処理があり、派生クラスのオブジェクトと入れ替えても同様に動作するべきである。
派生クラスは基底クラスの振る舞いに影響を与えるべきではない。

Dependency Inversion Principle - 依存関係逆転の法則

  1. 上位のモジュールは下位のモジュールに依存してはならいない。どちらのモジュールも「抽象」に依存すべきである。
  2. 「抽象」は実装の詳細に依存してはならない。実装の詳細が「抽象」に依存すべきである。

概要

下位モジュールは上位モジュールの方針に従うべきであり、各モジュールは抽象に依存するべきである。
上位モジュールが下位モジュールに依存すると、下位モジュールの変更が上位モジュールに影響を与えてしまうことになる。

悪い例

DIP_BAD

改善例

DIP_GOOD

Interface Segregation Principle - インターフェース分離の

クライアントに、クライアントが利用しないメソッドへの依存を強制してはならない。

概要

必要のないメソッドに依存してしまうと不用意に変更の影響を受けやすくなってしまう。
そういった影響を避けるために、インターフェースは分離しておくべきである。

参考文献

React + Redux 入門

Introduction

React・Reduxの基本的な仕組み・使い方ついて解説します。

Attention
自分もまだ使い始めたばかりなので、 間違っているところなどあればご指摘お願いします :bow:


React

A JavaScript library for building user interfaces

  • あくまでViewを作るためのライブラリ
  • Angular, Vue.jsなどのフレームワークとも組み合わせ可能
    • ex. Viewの所だけReactにする

https://facebook.github.io/react/

f:id:umatomakun:20161112212553p:plain

Pros

  • VirtualDOMという仕組みによりUnitTestを書くことが容易になる
    • Debugもしやすくなります
  • Componentを組み合わせることで複雑なUIを簡単に構築可能
    • また、Componentはアプリケーション内で再利用可能
  • 他のJSライブラリと組み合わせることが出来る
    • ex. Angular + React
  • パフォーマンス向上が望める
    • Angular, VueJS 辺りもVirtualDOM採用している筈なので差は縮まってきているかも?

Cons

  • 比較的学習コストが高い
    • 個人的にはAngularの方がやってること多いので難しい気が...
  • 依存ツールが多い
    • Babel, Webpack, CommonJS, ...
  • 記述が冗長になりがち
  • フルスタックなフレームワークではない
    • View以外の必要な処理は別途実装する必要がある
  • デザイナーとの役割分担が難しい
    • JSXを覚えてもらえれば何とか...

Features

Declarative

あるべきViewを宣言するように記述できる

Component-Based

Componentを組み合わせることでViewを構築

Learn Once, Write Anywhere

Rectを覚えれば様々な環境で使用可能 (ex. React Native)


Declarative

命令的

  • :scream: JSだけで完結せず、様々なものにViewが依存
  • :scream: テスト・デバッグが困難
$('button').on('click', () => {
  $('h1').text('hello');
});

宣言的

  • :smile: Viewがstateのみに依存
  • :smile: テスト・デバッグが容易
render() {
  return (
    <div>
      <h1>{this.state.title}</h1>
      <button onClick={() => {
        this.setState({ title: 'hello' });
      }}>
        Click
      </button>
    </div>
  );
}

Component-Based

ReactではComponentを組み合わせてViewを構築します。
これにより、複雑なUIを簡単に作ることが可能となります。

複数Component組み合わせイメージ

const Header = () => (
  <header>
    // This is header...
  </header>
);

const SearchForm = () => (
  <form>
    // This is search form...
  </form>
);

const MainContent = () => (
  <div>
    <LeftNav /> // 左ナビComponent
    <TopMenu /> // メニューComponent
    <Ad /> // 広告Component
  </div>
);

class App extends Component {
  render() {
    return (
      <div>
        <Header /> // ヘッダーComponent
        <SearchForm /> // 検索フォームComponent
        <MainContent /> // メインComponent
      </div>
    );
  }
}

f:id:umatomakun:20161112211217p:plain

※ 実際にReactは使われていません


Reactを使ってみよう!

Hello World

render 関数に表示したいElementと、表示先のDOMを指定すればOK!

ReactDOM.render(
  <h1>Hello World!!</h1>,
  document.getElementById('root')
);

この様なHTMLが出力されるはずです。

<body>
  <div id="root">
    <h1 data-reactroot="">Hello World!!</h1>
  </div>
</body>

CodePen

今すぐにReactを動かしてみたい方はこちらからどうぞ
既に実行できる環境が整っています!

http://codepen.io/gaearon/pen/ZpvBNJ?editors=0010


DOM描画

ReactではComponentを組み合わせてUIを構築していきます。

このComponentを元にインスタンスであるElementを生成し、
これらのVirtualDOMを通して実際のDOMに描画していきます。

// Component定義
var Hello = function Hello() {
  return React.createElement(
    "h1",
    null,
    "Hello World!!"
  );
};
// Element生成 & 描画
ReactDOM.render(
  React.createElement(Hello, null),
  document.getElementById('root')
);

// JSX ver.
const Hello = () => (
  <h1>Hello World!!</h1>
);
ReactDOM.render(
  <Hello />,
  document.getElementById('root')
);

JSX

Elementを生成する、HTML風の独自syntax (※ HTMLではないです)
JSXを使うことにより面倒なElement作成を簡単に行うことができます。

公式でも利用が推奨されているので、積極的に使っていきましょう!

<h1 id="test" hoge={false}>Hello World!!</h1> // JSX
React.createElement(
  "h1",
  { id: "test", hoge: false },
  "Hello World!!"
);

Babel Online Compiler

リアルタイムでBabelによるトランスパイルを行ってくれる環境があるので、
今すぐJSXを試してみたい方はこちらからどうぞ!

https://babeljs.io/repl/


Props & State

ReactではProps・Stateと呼ばれるデータによって状態を管理していきます。

Props State
データの更新 NG OK
定義元 親Component 自Commponent
イメージ Constructorから渡された引数 Privateなインスタンス変数

Props

Componentは外部からデータを受け取ることが可能となっていて、
その入力データの事をPropsと呼びます。

受け取ったPropsの値に応じて表示する内容を変化させたり、
クリック時のCallback等にも使用することができます。

class Welcome extends Component {
  render() { // propsを元にViewを宣言
    return <h1>Hello, {this.props.name}</h1>;
  }
}

class App extends Component {
  render() { // props指定
    return <Welcome name="Tom">;
  }
}

ただし、同じPropsが渡されたら同じViewが表示されるべきであるため、
値を更新しない ように注意してください。

State

Propsは親コンポーネントなどの外部から渡されるデータでしたが、
StateはComponent自身が保持しているローカルなデータになります。

また、Propsとは異なりデータの更新が可能となっています。
ただし、更新を行う際は必ずsetState関数を使用してください。
直接値を更新してしまうと更新時のイベントが呼ばれずViewが再描画されなくなります。:scream:

class Counter extends React.Component {
  constructor() {
    super();
    this.state = { count: 0 }; // stateの初期値を定義
  }
  render() {
    return (
      // クリックしたらインクリメント
      // ※ this.state.count++だと元の値を書き換えるのでNG
      <button
        onClick={() => {
          this.setState({ count: this.state.count + 1 });
        }}
      >
        {this.state.count}
      </button>
    );
  }
}

Lifecycle

ComponentにはLifecycle関連のメソッドが幾つか用意されていますが、
それらがどの様に呼ばれていくのか確認していきたいと思います。

Mounting

  • constructor()
    • state, propsなどの初期化処理
  • componentWillMount()
    • mountされる前に1回だけ呼ばれる
    • SSR (Server-Side-Renderring) 時にも使用可能
    • 主にSSR用の処理を記述 (Client側での初期化処理はconstructor)
  • render()
    • DOM描画
    • 単一のElementを返す必要がある
    • null, false 等を返した場合は何も描画されない
  • componentDidMount()
    • mountされた後に1回だけ呼ばれる
    • Ajaxでの初期データ読み込みや、描画したDOMへのイベント追加に使える

Updating

  • componentWillReceiveProps(nextProps)
    • Propsが更新されたときに呼ばれる
    • 変更後のPropsに応じてStateも変更するときなどに使用可能
  • shouldComponentUpdate(nextProps, nextState)
    • 返り値によって再描画する・しないを制御可能
    • true: 描画する、false: 描画しない
  • componentnWillUpdate(nextProps, nextState)
    • DOMの更新前に呼ばれる
    • ここではStateの更新ができない
  • render()
  • componentDidUpdate(prevProps, prevState)
    • DOMが更新された後に呼ばれる
    • DOMの更新に応じて処理をしたい場合などに使用可能

Unmounting

  • componentWillUnmount()
    • unmountされる前に呼ばれる
    • Component内で使用していたDOMイベントなどを解除するのに使用可能

f:id:umatomakun:20161112212348p:plain


Event Handler

"ボタンをクリックしたとき"・"フォームをSubmitした時"等の
イベント処理に関して見ていきたいと思います。

クリックしたら何かしてみる

下記のようにonClickイベントハンドラーを渡すだけで、
クリック時の挙動を制御することが可能となります。

const Hello = () => (
  <button onClick={() => { alert('Hello!!'); }}>
    Click Me
  </button>
);

サポートされているイベント一覧

  • Clipboard Events
  • Composition Events
  • Keyboard Events
  • Focus Events
  • Form Events
  • Mouse Events
  • Selection Events
  • Touch Events
  • UI Events
  • Wheel Events
  • Media Events
  • Image Events
  • Animation Events
  • Transition Events

https://facebook.github.io/react/docs/events.html


Unit Test

公式からUnitTest用のサポートツールが提供されていますが、
今回はairbnbから提供されているenzymeを使ったテストを紹介したいと思います。

https://github.com/airbnb/enzyme

import React from 'react';
import { shallow, mount } from 'enzyme';
import { expect } from 'chai';
import sinon from 'sinon';

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: props.initCount }; // stateの初期値を定義
  }
  render() {
    return (
      // クリックしたらインクリメント
      // ※ this.state.count++だと元の値を書き換えるのでNG
      <button
        onClick={() => {
          this.setState({ count: this.state.count + 1 });
        }}
      >
        {this.state.count}
      </button>
    );
  }
}

const App = ({ onClick }) => (
  <div>
    <h1>Hello World!!</h1>
    <div className="container">
      <Counter initCount={1} />
    </div>
    <button className="btn" onClick={onClick}>
      Click Me
    </button>
  </div>
);

describe('<App />', () => {
  it('タイトルにHello Worldが表示される', () => {
    // タグ名で検索
    const wrapper = shallow(<App />);
    expect(wrapper.find('h1')).to.have.length(1);
    expect(wrapper.find('h1').text()).to.equal('Hello World!!');
  });

  it('containerクラスが描画される', () => {
    // タグのクラス名で検索
    const wrapper = shallow(<App />);
    expect(wrapper.find('.container')).to.have.length(1);
  });

  it('Counterが描画される', () => {
    // Containerで検索
    const wrapper = shallow(<App />);
    expect(wrapper.find(Counter)).to.have.length(1);
  });

  it('Counterの初期値は1に設定される', () => {
    // Containerで検索
    const wrapper = shallow(<App />);
    expect(
      wrapper.find('Counter').prop('initCount')
    ).to.equal(1);
  });

  it('.container内にbuttonが描画される', () => {
    // Full DOM Rendering であれば依存Componentの内部まで認識可能
    const mountWrapper = mount(<App />);
    expect(
      mountWrapper.find('.container button')
    ).to.have.length(1);

    // Shallow Rendering した場合はCounterの中身を認識できない
    const shallowWrapper = shallow(<App />);
    expect(
      shallowWrapper.find('.container button')
    ).to.have.length(1); // 通らない
  });

  it('ボタンをクリックしたらCallbackが呼ばれる', () => {
    // クリックイベントをシミュレーション
    const onClick = sinon.spy();
    const wrapper = shallow(<App onClick={onClick} />);
    wrapper.find('.btn').simulate('click');
    expect(onClick.calledOnce).to.be.true;
  });
});

Redux

ReactではStateにより状態を管理することが可能となっています。

ですが、各Componentでバラバラに状態管理してしまうと、
アプリケーションが巨大になるに連れてあっという間に複雑化してしまいます。

では、 単一の親コンポーネントで全ての状態を管理してみる のはどうでしょうか?
良さそうに見えますが、一箇所にロジックが集中してしまい、いずれは管理しきれなくなりそうです :scream:

f:id:umatomakun:20161112211415p:plain

https://html5experts.jp/koba04/20839/

そこで、この様な問題に対処するため、
アプリケーション内のデータフローを一方向のみで扱う Fluxと呼ばれる
新しいアーキテクチャが考案されました。

f:id:umatomakun:20161112211346p:plain

Fluxから派生して作られた新たなアーキテクチャにReduxと呼ばれるものがあります。
Fluxでは複数のStoreを持つことを許容していましたが、
Reduxにおいては単一のStoreで全ての状態を管理していきます。

f:id:umatomakun:20161112211425p:plain

https://html5experts.jp/koba04/20839/


Three Principles

Single source of truth

The state of your whole application is stored in an object tree within a single store.

State is read-only

The only way to change the state is to emit an action, an object describing what happened.

Changes are made with pure functions

To specify how the state tree is transformed by actions, you write pure reducers.


構成

  • Actions
  • Action Creators
  • Reducers
  • Store

Reduxにおいて全ての状態はStoreと呼ばれる単一のStateで管理されています。
この、 Storeを更新する際は必ずActionを発行 し、
Reducerと呼ばれる純粋な関数 によって新しいStateを生成する必要があります。
また、 Actionを発行する役割を持っているのがAction Creator となります。

f:id:umatomakun:20161112211425p:plain

https://html5experts.jp/koba04/20839/

Actions

ActionはStoreを更新する際に必要となる情報であり、Storeから見た情報源は全てここに集約されています。

下記が簡単なActionの例です。

{ type: 'ADD_TODO', text: 'Do something...' }

Actionは純粋なJavaScriptのObjectであり、typeプロパティを持っている必要があります。
この、typeに応じてどの様な処理を行うべきかを判断します。

Action Creators

Action CreatorはActionを生成するただの関数です。

function addTodo() {
  return { type: 'ADD_TODO', text: 'Do something...' };
}

Reducer

Actionからはどの様な行動が発生したのか認識できますが、どの様にStateを変更すべきかは明示していません。
この役割を担うのがReducerとなります。

Reducerは純粋な関数であり、現在のStateとActionから新たなStateを作成します。

function todoApp(state = initialState, action) {
  switch (action.type) {
    case 'ADD_TODO':
      return Object.assign({}, state, {
        todos: [...state.todos, action.text]
      });
    default:
      return state;
  }
}

また、Reducer内で次のような処理を含めてはいけません。

  • 引数を編集する
  • API呼び出しやページ遷移など、副作用のある処理
  • Date.now()Math.random()などpureでない関数を呼ぶ

同じ引数が与えられたら、同じStateが返ってくるべきです。
そのためには、引数の編集や副作用のある処理は行わない必要があります。

Store

Storeは次の役割を担っています。 - アプリケーションのStateを保持 - getState()経由でのアクセスを許可 - dispatch(action)経由での更新を許可 - subscribe(listener)経由でlistenerを登録 - subscribe(listener)からreturnされたfunction経由でlistenerを解除

アプリケーション内のStoreはただ一つです。
データの扱いを分割したい場合は複数のStoreを作成するのではなく、reducerを組み合わせてください。

Storeを作成するのは簡単で、下記のようなコードで実現することができます。

import { createStore } from 'redux';
import todoApp from './reducers';
const store = createStore(todoApp);

Dispatching Actions

Storeを更新したい場合にはstore.dispatch()にActionを渡せばOKです。
現在のStateと渡されたActionを元にReducerが新しいStateを作成します。

import { createStore } from 'redux';
import todoApp from './reducers';
import { addTodo } from './actions';
const store = createStore(todoApp);
store.dispatch(addTodo('Learn about Redux'));

Redux with React

React用のRedux実装ライブラリとしてreact-reduxが提供されています。

$ npm install react-redux --save

Presentational and Container Components

Presentational Components Container Components
目的 見た目を定義 (markup, styles) ビジネスロジックを定義 (data fetching, state updates)
Redux依存 依存しない 依存する
データの参照方法 props経由で参照 Redux Stateを参照
データの更新方法 propsのCallbackを呼び出す ActionをDispatch

DevTools

Babel

https://babeljs.io/

Webpack

https://webpack.github.io/


参考文献

MeCabで形態素解析 Node.jsバインディングを作ってみた

概要

Node.jsを使ったアプリケーションで形態素解析を行う必要があるので、MeCabを勉強してみようと思いました。 しかし、公式で提供されている言語バインディングにNode.jsは含まれていません(;´∀`)

公式以外で提供されているOSSも幾つかあるようですが、 外部コマンドを実行していたり、最終更新が5年前だったりと中々良さそうなものが見つかりませんでした...

www.npmjs.com

www.npmjs.com

そこで、今回は勉強ついでにMeCabのNode.jsバインディングを作ってみたいと思います。

環境

What is MeCab?

MeCab: Yet Another Part-of-Speech and Morphological Analyzer

MeCab京都大学情報学研究科−日本電信電話株式会社コミュニケーション科学基礎研究所 共同研究ユニットプロジェクトを通じて開発されたオープンソース 形態素解析エンジンです。 言語, 辞書,コーパスに依存しない汎用的な設計を 基本方針としています。 パラメータの推定に Conditional Random Fields (CRF) を用 いており, ChaSenが採用している 隠れマルコフモデルに比べ性能が向上しています。また、平均的に ChaSen, Juman, KAKASIより高速に動作します。 ちなみに和布蕪(めかぶ)は, 作者の好物です。

Node.js Native Addon (V8 Addon)

Node.jsではnode-gypというツールを使うことによって簡単にNative Addonを作ることが可能となっています。
www.npmjs.com

MeCab公式から提供されているC/C++ライブラリをこれでラッピングしてあげれば、Node.jsからMeCabが使えるようになります。
MeCab C/C++ ライブラリの使い方はこちら http://taku910.github.io/mecab/libmecab.html

node-mecab-native

実際に作ってみたNode.jsバインディングはこちらとなります。

github.com

C/C++の知識は殆どないので、色々とイケてない部分はあるかと思います...
間違ってる部分などありましたらご指摘いただけると助かります...

使い方

var MeCab = require('node-mecab-native');
var tagger = MeCab.Tagger('-Owakati');

// Parse text
tagger.parse('太郎は次郎が持っている本を花子に渡した。', function(err, res) {
  console.log(res);
});

// Parse text to Nodes
tagger.parseToNode('太郎は次郎が持っている本を花子に渡した。', function(err, res) {
  console.log(res);
});

// Get Dictionary Info
tagger.dictionaryInfo(function(err, res) {
  console.log(res);
});

まとめ

初めてNode.js Native Addonを作ってみましたが、思いの外簡単に作成することができました。
今まで、どの様にしてNative Addonが作られているのか分からなかったですが、
大まかな仕組みは理解できたので良かったかと思います。

ですが、やはりC/C++が分からないと出来ることが限られるので
勉強し直したほうが良さそうです(;・∀・)

Node.jsでリトライ処理を実装してみる

概要

HTTPリクエストが失敗した場合や、DBへの接続に失敗した場合など
何回かリトライ処理を含めたい場合ってありますよね?
そこで、Node.jsを使ったリトライ処理用モジュールを試しに作ってみようと思います。

環境

Node.js v4.4.7 OS Mac OS X 10.11.5

リトライ処理用モジュール

特に深い理由は無いですが、
Promiseベースのリトライ処理が出来るようなものを作ってみたいと思います。

作るもの

  • Promiseで結果が受け取れる
  • リトライ数が設定できる
  • リトライ間隔が設定できる
  • デバッグ用のログが出せる

promise-retry.js

'use strict';

class Retry {
  constructor() {
    this._maxTimes = 3;
    this._interval = 100; // ms
    this._debug = false;
  }

  /**
   * @public
   */
  maxTimes(count) {
    this._maxTimes = count;
    return this;
  }

  /**
   * @public
   */
  interval(ms) {
    this._interval = ms;
    return this;
  }

  /**
   * @public
   */
  debug(flag) {
    this._debug = flag;
    return this;
  }

  /**
   * @public
   */
  execute(fn) {
    let retryCount = 0;
    let maxTimes = this._maxTimes;
    return new Promise((resolve, reject) => {
      this.doRetry(fn, retryCount, resolve, reject);
    });
  }

  /**
   * @private
   */
  doRetry(fn, retryCount, resolve, reject) {
    let maxTimes = this._maxTimes;
    fn(retryCount)
      .then(function() {
        // Don't use arrow function.
        // In that case, `arguments` will be global scope variable.
        resolve.apply(Promise, arguments);
      })
      .catch((err) => {
        if (retryCount >= maxTimes) {
          reject(err);
        } else {
          retryCount++;
          setTimeout(() => {
            this.log(retryCount, err);
            this.doRetry(fn, retryCount, resolve, reject);
          }, this._interval);
        }
      });
  }

  /**
   * @private
   */
  log(retryCount, err) {
    if (this._debug) {
      let hrtime = process.hrtime();
      console.log(
        'retry cnt: %d, at: %d.%d, prev_err: %s',
        retryCount,
        hrtime[0],
        hrtime[1],
        err.message
      );
    }
  }
}

module.exports = () => new Retry();

リトライ処理をやってみる

では、作ったモジュールを使ってリトライ処理を実装してみたいと思います。

  • 最大リトライ数:5
  • リトライ間隔:20ms
  • デバッグモード:ON

index.js

'use strict';

const knex = require('knex')({ ... });
const retry = require('./promise-retry');

retry.debug(true)
  .maxTimes(5)
  .interval(20)
  .execute(retryCount => {
    return knex.first('*')
      .from('users')
      .where('id', 'umatoma');
  })
  .then(user => console.log(user))
  .catch(err => console.log(err));

地味にハマったポイント

ES6から導入された アロー関数 を使うと this と同様に arguments も束縛されてしまうんですね。
知りませんでした...

developer.mozilla.org

まとめ

単純な処理ではありますが、複数箇所で同じようなことをやる可能性も高いと思うので、
モジュール化して、使いまわすといいかもしれないですね。