Persistence

技術メモなど

GitHub Browser Extensionsのうちいくつかを1ヶ月ほど使ってみた感想

GithubにはCollectionsというリポジトリのキュレーション機能があります。その中に、Collection: GitHub Browser Extensionsという、GitHubを便利にする拡張のCollectionがあります。ここで紹介されている拡張のうちいくつかを実際につかてみたので、その感想を書いていきます。

なお、拡張にプライベートリポジトリのアクセストークンを渡すのはリスクを伴うことは理解して使いましょう。

ovity/octotree: GitHub on steroids

期待度: ★★★ おすすめ度: ★★★

リポジトリの内容をツリー構造で表示してくれます。ディレクトリをリンクを辿って開かなくてもリポジトリのルートから一発で特定のファイルを開くことができます。Pull Requestの画面では、コミットされたファイルのみをツリー構造で表示してくれて、スクロールを移動できるので、コミットされたファイル数が多いときには便利です。

OctoLinker/OctoLinker: OctoLinker — Links together, what belongs together

期待度: ★★★ おすすめ度: ★★

ソースコードを解析して、参照宣言などから参照元のファイルにリンクを生成してくれます。便利ではあるものの、ソースコードを見るときはローカルにあるか、GitHub自体の検索機能が優秀なので余り使う機会はなかったです。

Justineo/github-hovercard: Neat hovercards for GitHub.

期待度: ★★ おすすめ度: ★

リンクにカーソルを当てたときのHoverをリッチにしてくれます。Issueの中身をIssueの一覧から確認できるなど便利かなと思ったのですが、それ以上に邪魔に感じることが多かったので削除しました。

sindresorhus/refined-github: Browser extension that simplifies the GitHub interface and adds useful features

期待度: ★★ おすすめ度: ★★

この拡張は機能が多すぎて把握しきれていないのですが、細かいところをたくさん改善してくれています。私が良いと感じている機能は、コメントのリアクションにリアクションした人のアイコンが表示される(誰がどのリアクションをしたかが一目でわかる)のと、ヘッダー部分にPRが作られていないブランチがシュッと出てくる機能です。これだけ機能が多いと中には使いたくない機能もあるのですがそういうのは無効化できるのも良いです。

マルチテナントサービスの拡張性パターン

本記事は、ゆるWeb勉強会@札幌のアドベントカレンダーの11日目の記事として投稿しました。

コミュニティには参加したことがないのですが、主催者さんが記事を募集していたのと、私自身も期限を設けることで書ききりたかったので参加させてもらいました。

はじめに

私はここ2年ほど、マルチテナントサービスの立ち上げに開発メンバーとして関わってきました。少ない人数での開発だったので、サービスの設計からインフラ構築から実装から運用まですべてを経験しました。そして今また、新しいマルチテナントサービスの立ち上げで設計・実装を行ってる最中です。これらの経験を通じて、マルチテナントサービスで拡張性を実現する方法をある程度パターン化できそうと思ったので、アウトプットしてみようと思いました。ちなみに本記事の内容は、実際に試したものもあれば、妄想段階のものもありますので、その点はご了承ください。

本記事の言葉の定義

  • サービス: サービスと一口に言っても色々な提供形態がありますが、本記事が想定する(私が関わってきた)サービス)は、いわゆるBaaSのようなサービスです。特定の分野のクライアントアプリの開発に必要な機能をAPIやデータベース、管理画面などと共に提供します。
  • マルチテナント: マルチテナントは、複数のテナント(利用者)が1つのリソースを共有する形で提供するサービスのことです。もちろんテナントごとにデータは分離されているので、テナント(利用者)から見ると他のテナント(利用者)の情報は見えません。
  • 拡張性: 拡張性とは、あるテナントが独自の仕様(データのスキーマや機能の振る舞い)を実現することをいいます。

なぜ拡張性が必要なのか?

利用目的が決まっているサービスであれば、基本的にはサービスとしての仕様を決めて利用者はその中の機能を利用することが前提となります。BaaSのような開発者向けのサービスの場合、できることがクライアントアプリでできることと直結するため、機能の拡張性(自由度といったほうがしっくりくるかも)が求められます。例えば、データのスキーマを定義できたり、任意の認証プロバイダと連携できたり、外部のサービスとデータを連動したり、などなど。そういうことができないと、サービスとクライアントアプリの中間にもうひとつProxy的なサービスを置く必要があり、トータルとして利用者側の負担が高くなってしまいます。

拡張性を実現する難しさ

拡張性を内包することは常にトレードオフが付きまといます。できることが増えることで、組み合わせが増えて仕様が複雑になりがちです。また、一度公開したインターフェイスは提供し続けなければなりません。安易な方法で拡張性を実現すると、その部分はすぐに負債となり開発速度や品質の低下を招きます。

また、どのくらい拡張性を見越して設計するかも難しいところです。私は普段はYAGNIの原則を強く意識していますが、こういったサービスではどこまでの拡張性を想定するかを非常によく考え、オープン・クローズドの原則を強く意識します。

パターン

拡張性を実現する部分を、システムのレイヤーごとに分類してみるとこのようになりました。

  • データベースレイヤー
  • アプリケーションレイヤー
  • インターフェイスレイヤー
  • プレゼンテーションレイヤー
  • その他

1つのパターンが複数のレイヤーに跨っている場合もあります。

あと、どのパターンが一番良いというものではありません。それぞれにトレードオフが存在します。そしてベストな選択はサービスの特性やステージによっても異なります。

データベースレイヤー

データベースレイヤーでの拡張性はサービスの利用者の要求を叶えるためにとても重要です。なぜなら利用者は利用者側のドメインモデルをできるだけサービスに正しく反映させたいからです。データベースに拡張性をもたせることで、利用者の独自のデータを管理することができるようになります。データベースレイヤーでは2つの方法を紹介します。どちらも古典的なパターンだと思います。基本的にはRDBを想定していますが、NoSQLについてもサービス側でスキーマ定義を持つのでだいたい同じことが言えるかと思います。

拡張フィールド

  • 利用者が自由に使うことができるフィールドを用意します。フィールドを複数提供したり、JSON型のフィールドを提供することでより自由な使い方をさせることができます。
  • サービスは利用者のフィールドの使い方についての知識を持たないため、サービスとしては維持コストが低いです。
  • フィールドの使い方は利用者のクライアントのみが知識を持つため、フィールドのデータの管理はすべてクライアントが責任を持つことになります。

f:id:bisque3311:20191210225636p:plain

拡張フィールド+メタ情報

  • サービスが拡張フィールドのメタ情報(フィールド名や型など)を管理します。利用者から見るとモデルやエンティティを定義するようなイメージです。
  • サービスとしては抽象的な実装になるため、やや高度な実装になりますが、サービスのインターフェイスAPIや管理画面など)をメタ情報を通じて具体的な情報にすることができるので、クライアントには優しいです。
  • メタ情報を他のレイヤーでも参照することで様々な部分で拡張フィールドの情報を利用することができます。

f:id:bisque3311:20191210225713p:plain

アプリケーションレイヤー

データベースレイヤーの拡張により扱えるデータを増やすことはできましたが、ビジネスロジックを含むことはできません。アプリケーションレイヤーの拡張では、ビジネスロジックの拡張を実現します。

Config

  • いわゆるフラグです。フラグの設定によりロジックを切り替えます。拡張性というよりは、サービスが提供するロジックのパターンを増やして、利用者がそれを選択するというパターンです。
  • サービスとして提供するロジックを増やしているので、安易にパターンを増やしていくとすぐに組合せ爆発を引き起こし手がつけられない状態になります。なので、このパターンを使う場合は、フラグごとにモジュールを切り出すような実装にしたほうが良いです。
  • 例えば、サービスとしていくつかの認証サービスに予め対応しておき、テナントごとにどれを利用するかを選択できるようにする、などといった設定ができます。

f:id:bisque3311:20191210225735p:plain

Plugin

f:id:bisque3311:20191210225759p:plain

ロジックの埋め込み

  • サービスに利用者が独自の任意のロジック(プログラム)を埋め込み、任意のトリガーで実行することでビジネスロジックインターフェイスなどを拡張します。
  • ロジックのインターフェイス(ロジックに与えるパラメータ)や実行リソースはサービスが提供します。インターフェイスが決まっているという点ではPluginの進化系と言えるかもしれません。
  • 例えば「APIの認証で任意のロジックを実行して利用者が管理する認証サービスでトークンを検証する」とか、「特定のイベントが発生したことをトリガーとして任意のロジックを実行して外部サービスと連携する」とか、色々考えられて自由度が高いです。
  • 実際のサービスの例としては、認証基盤サービスのAuth0にRulesという機能があります。Rulesは、特定のイベントが発生したタイミングで(ユーザがログインしたときなど)実行されるロジックで、決められたインターフェイスを通じてパラメータを受け取り、ユーザの属性を操作したり、idTokenの内容を拡張したり、外部のサービスと連携したりすることができます。
  • 実行環境としては、GCPのCloud RunやAWS Lambdaなど、起動時間が短くコンピューティングリソースの分離や制限がしやすいServerless環境との相性が良いと思います。

f:id:bisque3311:20191210225819p:plain

インターフェイスレイヤー

サービスの拡張性をクライアントアプリに連携するには、サービスと通信するインターフェイスの拡張も必要です。

GraphQL

  • 不特定多数のクライアントに利用されるAPIの場合、GraphQLを使うことでクライアントが必要とするクエリを組み立てやすくなります。REST APIではリソースがエンドポイントで分離されているので何度もリクエストしなければならなかったのが、1回のリクエストで取得できるようになったりします。
  • さらにデータベースレイヤーの拡張パターンのメタ情報を用いることで、メタ情報をそのままスキーマに反映することができます。つまりクライアントごとに独自のスキーマを提供することができます。これをいち早く実現したサービスにGraphCMSというサービスがあります。

SearchTemplate

  • Elasticsearchにある機能で、予め検索条件とパラメータ項目を設定したqueryを登録しておき、クライアントからパラメータを与えて検索することができます。任意のqueryを利用者ごとに設定できるようになり、非常に自由度が高いです。
  • AWSであればマネージドサービスがありますのでクラスタの管理コストは低いですが、費用的なコストはやや高めです。瞬時にスケールできないのでバースト対策にはレートリミットを設けるなど注意が必要です。

プレゼンテーションレイヤー

メールやプッシュ通知などのプレゼンテーションレイヤーの拡張性です。

テンプレート

  • テンプレートを用意しておき可変部分を変数化してパラメータを埋め込みます。
  • 細かい話ですが、プッシュ通知の拡張性で大変なのがバッジです。全て一律で1にするのなら問題ないのですが、2以上の数字を扱う場合はサービスとクライアントで数字の同期(整合性合わせ)が必要になるためとても大変です。

その他

サービスの外側で拡張するパターンです。

Webhook

  • サービスは特定のイベントをトリガーとして、データを任意の場所に投げます。
  • 利用者は受け取ったデータを使って任意の処理を行うことができます。

Event Messaging

  • サービスは、特定のイベントをトリガーとして、メッセージをキューやPub/Subなどのサービスに追加します。
  • 利用者はキューやPub/Subから受け取ったデータを使って任意の処理を行うことができます。

Proxy

  • サービスとクライアントアプリの間に位置して、データを変換したり、機能を拡張したり、外部のサービスと連携したりします。

おわりに

  • 目次を書き始めたときは、本ができそうなくらい書けることがたくさんあると思ったのですが、いざ書いてみるとまとまった文章を書くのが難しくて、持っている情報を全然出しきれず薄い内容になってしまいました。
  • 全体的に見ると目新しさは少ないですが、個人的にはロジックの埋め込みはServerlessのパラダイムで出現した新しいパターンかなと思っていますので、今後深堀りしていきたいと思っています。

GraphQL Subscription の調査

GraphQLでSubscriptionを実装する場合、どういう構成になるのかなと思い調べてみました。

GraphQL Subscription Spec

SubscriptionはGraphQL specのJune2018で仕様が策定されています。

https://graphql.github.io/graphql-spec/June2018/#sec-Subscription

仕様で言及されていることはインターフェイスのみで、ネットワークレイヤーの仕様は言及されていません。 GraphQLはあくまでインターフェイスの仕様ということですね。

Implementation

How To GraphQL の Tutorial

https://www.howtographql.com/graphql-js/7-subscriptions/

WebSocketを使っていることが伺えますが、実装はprismaのライブラリにラップされています。 今回はpureなライブラリで実装する方法を知りたかったので深入りはしません。

Tutorial: GraphQL Subscriptions on the Server

Apolloの記事です。

https://blog.apollographql.com/tutorial-graphql-subscriptions-server-side-e51c32dc2951

graphql-subscriptionssubscriptions-transport-ws を使って実装していることがわかります。

こちらもやはりWebSocketです。

他にも色々調べてみましたが、javascriptではこの2つのライブラリがスタンダードと言って良さそうです。

ちなみにこの2つのライブラリはapolloがオーナーですが、apollo専用ではありません。

実装してみる

GraphQLの基本的な部分はサラッと作りたかったので、Apollo Serverを使うことにします。

https://www.apollographql.com/docs/apollo-server/getting-started/

ここまでできたら、 graphql-subscriptions のREADMEを読みながらSubscriptionを組み込みます。

できました。あれ?WebScoketについて何も設定していないのにWebSocketになっている。あとから気づきましたが、 apollo-server を使う場合は、 apollo-server-codegraphql-subscriptionsubscriptions-transport-ws が含まれているので、importは不要なのでした。

まとめ

GraphQL で Subscription を実装する場合、現状ではWebSocketがデファクトスタンダードと言えそうです。

実際にProduction環境で利用するには、以下も必要と思いますのでサンプルを実装してみようと思います。

  • PubSubをスケールさせる方法
  • GraphQLサーバ(WebSocketサーバ)をスケールさせる方法

now.sh 2.0 Introduction

最近注目しているnow.shの2.0のコンセプト・機能について紹介します。

now.shとは

Now – Global Serverless Deployments

アプリケーションをすばやくデプロイするためのプラットフォームサービスです。 ごく小さな設定ファイルを書いて、nowコマンドを実行するだけ(本当にこれだけ!)でデプロイが完了します。 標準でhttpsがサポートされ、CDNも提供されます。 無料プランから始められアクセス数などに応じて従量課金になります。

2.0のコンセプト

2.0以前をあまり知らないのですが、以前はサーバアプリケーション(常時起動のサービス)やBateですがDockerコンテナをサポートしていました。しかし、2.0ではDockerは完全に姿を消し、サーバアプリケーションも以前からの利用者のために一応残したという程度になっています。重厚なサーバアプリケーションをターゲットから外し、フロントエンドアプリケーションとAWS Lambdaのようなファンクションサービスにピボットしたことが伺えます。

2.0の機能

ZEIT – Now 2.0

builder

設定ファイルにアプリケーションに合ったbuilderを指定することで、アプリケーションのビルドやランタイムの提供が行われます。例えば、Next.jsのプロジェクトの場合は @now/next というbuilderが標準であり、これを指定するだけで静的なコンテンツ、フロントエンドアプリケーション、サーバアプリケーションが一気にデプロイされます。builderは自分で作ることもできるので、アプリケーションに合ったbuilderを作ったり、他の人が作ったbuilderを使うこともできます。実行環境はnow.shが提供するランタイムから選択することになります。ランタイムはもう少し数が増えたり新しいバージョンを早くサポートしてくれると良いですね。

Monorepo × Massive build parallelization

1つのプロジェクトに複数のファンクションのエントリーポイントを持つことを、Monorepo(1つのリポジトリでたくさんの機能を管理する)と表現しています。Next.jsの例のように、1つのプロジェクトに実行環境の異なる複数のエントリーポイントを持つことも含まれているかもしれません。Monorepoにすると、エンドポイントごとに並列でビルドが実行されるためビルド時間が早く、実行時にも実行ファイルが小さく、起動時間が早いというメリットがあります。now.shがroutingとdispatchを受け持ってくれる感じですね。こういうMonorepoなFunctionsを作るFrameworkってあったりするんでしょうか。

Universal Cloud

now.shの利用者には見えない部分ですが、ベンダーロックインを避けるため、インフラにはAWSGCPを両方使えるようにしているようです。

誰が使うといいの?

個人的な所感としては以下で使えるかと思いました。

  • フロントエンドアプリケーション
  • 規模の小さなファンクションサービス
  • プロトタイピング

サーバサイドアプリケーション(Webサーバが起動して待機するタイプ)も一応サポートされていて、プロトタイピングレベルならありかと思います。実際にExpressやNestjsのミニマムのアプリケーションなら動かすことができたんですが、いま自分が作っているアプリケーションがまだ動かない(504が返る)状態なので、もう少し調査をしてみようと思います。

vim環境の更新など

ちょっとしたテキストファイル(jsonなど)の編集にいちいちVSCodeを起動するのがだるくなってきたので、vimでサクッとできるように環境を整えることにした。

環境はMacOSです。

neovimから本家のvim

vimはneovimの2.xがインストールされてたんだけど、neovimの活動状況があまり活発でないように見えたので本家のvimに変更した。

$ brew uninstall neovim
$ brew install vim

本家のvimのインストールの際に --with-lua をつけると入力補完の機能が使えるようになるという記事を見たんだけど、いまは --with-lua オプションはなく、標準でインストールされた。

neobundleからdein.vim

neobundleの公式にdein.vimへの移行が推奨されていたので、dein.vimに移行した。

インストール手順はこのあたり。

https://github.com/Shougo/dein.vim#unixlinux-or-mac-os-x

$ curl https://raw.githubusercontent.com/Shougo/dein.vim/master/bin/installer.sh > installer.sh
$ sh ./installer.sh ~/.cache/dein

neobundleでインストールしていたパッケージは、call dein#add('リポジトリ名など') と書く。

  " Add or remove your plugins here like this:
  call dein#add('Shougo/neosnippet.vim')
  call dein#add('Shougo/neosnippet-snippets')
  " ディレクトリツリー
  call dein#add('scrooloose/nerdtree')
  " マルチカーソル
  call dein#add('terryma/vim-multiple-cursors')

そして、vimを起動して :call dein#install() を実行するとパッケージがインストールされる。 以下のように起動時にインストールチェックをするようにしても良い。

if dein#check_install()
  call dein#install()
endif

nerdtree

vimIDEのようなファイルツリーを表示してくれるパッケージ。 操作方法を覚えるのは結構大変だけど慣れるしかない。 キーマップは以下を設定した。

" Toggle NERDTree
map <C-t> :NERDTreeToggle<CR>
" move left Tab
"map <C-[> gT " 有効にすると起動時に置換モードになってしまう
" move right Tab
map <C-]> gt

ところがコメントにもある通り、 map <C-[> gT を設定すると起動時に置換モードになるという事象に遭遇し未解決状態。個人的にタブの切替は [] 以外に使いたくないので解決したい。

vim-multiple-cursors

マルチカーソル機能を提供してくれるパッケージ。 これを使う以前にvimのVisualモードなどを使いこなす必要があり、全く速度が出ない。

Insertモードでdeleteキーが効かない問題

ずっと前からおかしいなと思いつつ放置していたが、vim-multiple-cursorsを使いこなすためにも必要なので調べてみたら一瞬で解決した。

vimで文字が削除出来ないと思ったらバックスペースが効かなくなった - Qiita

gitのcommitコメントを書くときとかもdeleteキーが効かなくて煩わしかったので解決できてよかった。

fish shellでnodeのcompletionエラーが出る問題

vimとは関係ないけど気になっていたので直した。

症状

$ node [ここでTabキーを入力] __fish_not_contains_opt: Unknown option --version
__fish_not_contains_opt: Unknown option --version
__fish_not_contains_opt: Unknown option --version
__fish_not_contains_opt: Unknown option --eval

調べたらだいたいすぐ解決方法は見つかった。

https://fishshell.com/docs/current/commands.html#fish_update_completions

$ fish_update_completions

これだけ。fish shellは ~/.config/fish/completions/ にcompletionファイルを配置するようになっている。 fish_update_completions コマンドは、それを最新にするコマンド。要するにcompletionファイルが古かっただけだった。このコマンドは定期的に実行するように仕組み化しておきたい。自動で差分更新してくれればいいんだけどな。

dotfile

ほとんどいじってないけどdotfileはこちらに公開しています。

GitHub - bisque33/dotfiles

GraphQLキャッチアップ

最近、AWS AppSyncやReactをさわり始めた。AppSyncはまだまだ十分ではないものの、近い将来、APIサーバは必要なくなるという可能性を感じた。API Gateway + Lambdaのときも感じたけど、その時はRESTful脳だったから正直サーバレスにしたところで楽にはならなそうと思ったけど、AppSyncの場合はそれよりもずっと可能性が高そうに感じた。ということでGraphQL界隈を少しだけ探索してみた。ほんの少しだけなのにこんだけたくさん出て、サーバサイドエンジニアとしてはこりゃもうReactなんてやってる場合じゃないわ。

リンクメモ

GRANDstack

GraphQL, React, Apollo, Neo4j Database の頭文字を取ってGRAND stack。 AWSだったらAppSync + Neptuneか。

GRANDstack Starter

サンプルアプリケーションを動画で詳しく説明してくれている。 Neo4jはWeb上のサンドボックスで、アプリケーションの実行はZEITというPaasで行っている。

2018ワールドカップのGraphQL APIを作りました | POSTD

GRAND stack で作られたサンプルの紹介。スキーマ設計の参考になります。

prisma/graphql-playground

GraphiQLの進化版。デスクトップアプリになっている。

serverless/serverless-graphql

AWSで実現するGraphQLアプリケーションのサンプル。 AppSync版とAPI Gateway + Lambda版がある。

GraphCMS

GraphQLを使ったBaas。Typeスキーマを定義するだけでMutationなどは自動生成される。CMSとしてはデータ管理のほか、AssetsやWebhookなどもあり、なかなか良くできている。

GitHub GraphQL API v4 | GitHub Developer Guide

Githubが提供しているGraphQLのドキュメント。 GraphQLの設計の参考になりそう。

サンプル

graphql-playground に Example として紹介されているものたち。

https://graphql-pokemon.now.sh/

ポケモンのデータベース。

https://api.graph.cool/simple/v1/cixne4sn40c7m0122h8fabni1

Instagramっぽいもの。

https://api.graph.cool/simple/v1/cixos23120m0n0173veiiwrjr

映画のデータベース。

オブジェクト指向設計やプログラミング言語の歴史とこれからについて

はじめに

クソコードを生成して迷惑をかけた気がするので、オブジェクト指向設計について学び直すことにした。今振り返ると、当時の自分はオブジェクト指向設計を全く理解せずにコードを書いていた。極端に言ってしまうと、構造化プログラミングにクラスと継承を付け足したもの、といえば伝わるだろうか。

それでもなぜか自分はプログラミングちょっとデキると思っていた。なぜか。まず第一に、コードの綺麗さにはそれなりに自身があった。「リーダブルコード」などを読んで自分なりにきれいに書くように努めていたからだ。第二に、いくつかのWebApplicationフレームワークを使ってプロダクションコードを書いた経験があったからだ。第三に、ドメイン駆動開発(DDD)やソフトウェアアーキテクチャについても学んできたからだ。(ただ今思うと、概念的にわからないことが多くて上滑りしていた気がする)

しかし今回のクソコードはWebApplicationフレームワークRuby on Rails)の土台の上に、かなりの業務ロジックを追加したもので、それらは全くオブジェクト指向設計の要素や原則を無視したもので、残念ながら変更に脆くとても保守性の悪いものだった。(以前、こんな記事を書いているのにもかかわらず、使わずに時間が立ってしまうと全く忘れてしまうという、悲しいことだ)

更に悪いことに、メタプログラミングを多用し、設計ではなく力技で実装を進めてしまった。弱い人間が強い力を手にすると自滅するというセオリーをここまで身をもって体験するとは、自分でも苦笑してしまう。なお、この記事ではメタプログラミングの失敗についてはこれ以上は触れないことにする。

動的型付け言語は保守性が悪い?

少し本題とは逸れるが、はじめにこの疑問についてケリを付けたかった。職場には比較的、静的型付け言語の支持者が多く、動的型付け言語はプロダクションコードとしても使われて入るものの市民権を得られていない雰囲気があった。そんな中でも自分は割と動的型付け言語肯定派(というか適材適所派)で、よほど特殊な要件がない限りはどちらのタイプの言語でも置き換え可能と思っていた。

でも実際のところ、クソコードを書いてしまったわけだし、動的型付け言語でどうやって保守性の高いコードを書くことができるのかを知りたくなった。そこでまず読んだのが「オブジェクト指向設計実践ガイド」だ。オブジェクト指向設計の本の中では比較的新しいし(それでもオリジナルが発行されたのは2012年)、サンプルはすべてRubyで書かれているのでここに求めている答えがある気がした。

とても良本だった。オブジェクト指向設計の本質や、SOLIDの原則(なんとなく知っていたけどオブジェクト指向設計の話だと思っていなかった。)などが詳しく解説されていて、自分の無知を思い知った。オブジェクト指向設計の本質は「要求の変更」に対して「依存関係」を管理することで変更可能なアプリケーションを構築するということだ。そのために、オブジェクト同士がメッセージを通じて協調し合うことで動作する。これがオブジェクト指向プログラミングだ。また変更可能性を高めるために、カプセル化・継承・委譲・ポリモーフィズムなどの機能が提供され、SOLIDの原則やデザインパターンといった設計原則を用いることでさらにそれを高めていくことができる。

しかし、ダックタイピングの章を読んだところで、どうしても動的型付け言語の怖さを克服することができなかった。結局、動的型付け言語で型(やインターフェイス)が動的に決まることについては、「テストで文書化して保証する」「型宣言がなくてもコンテキストがわかるように設計する」のだが、これらをプロジェクトのメンバー全員が達成することはコスト的にもスキル的にもハードルが高いと感じたからだ。静的型付け言語の本質は「設計が型で表されているから保守性が高い」なんだと思う。(まだうまく言語化できない)

静的 vs 動的 vs ???

では2000年台に入り、その当時、Javaや.Netなどの静的型付け言語があったにもかかわらず、動的型付け言語が流行したのはなぜか。一つは、それらの静的型付け言語がまだ発展途上であったこと。もう一つは、動的型付け言語のフレームワークが台頭し生産性が向上したから。それらが重なってPHPRubyなどの動的型付け言語が使われるようになった。しかし、2010年台は静的型付け言語が進化し生産性が向上し、また保守性を取り戻すべく静的型付け言語が再び流行りだすことになる。また、2018年現在は、静的と動的の両方を併せ持つ言語)が開発されていたり、動的型付け言語であるPythonに型アノテーションを導入する動きなども見られるが、どこまで実用的かはまだわからない。

UMLはなぜ廃れたのか

また脇道に逸れるが、「オブジェクト指向設計実践ガイド」の中でオブジェクト同士のメッセージのやり取りを表すためにシーケンス図が用いられていた。シーケンス図といえばシステム同士のAPIコールを表すことにしか使ったことがなかったので、そもそもUMLオブジェクト指向設計を図示するためのツールだということを知らなかった。そこで、UMLを学べばオブジェクト指向設計をもっと知ることができるのではと思い、マーチン・ファウラー先生の著書「UMLモデリングのエッセンス第3版」を読んだ。

UML モデリングのエッセンス 第3版 (Object Oriented SELECTION)

UML モデリングのエッセンス 第3版 (Object Oriented SELECTION)

この本では最初の2つの章で、UMLの使い方と、UML開発プロセスに適応する方法について書かれている。というのも、UMLの標準化団体は、UMLを厳密化しUMLからプログラムを生成できるようにするために、UMLをどんどん複雑な仕様にしていった。しかし設計からコードを生成するということは、コードと同じレベルの設計書を書くということなので、コードを書いたほうが遥かに楽ということは想像できるだろう。また、開発プロセスについてもウォーターフォール開発ではなくアジャイル開発が主流になり、設計書を書くという行為自体が無駄という風潮が高まり、UMLは姿を消していった。

しかし、本書はそういった厳密なUMLの使い方ではなく、スケッチとして、コミュニケーションツールとしての使い方を推奨している。特に知っておいたほうがいいと思ったのは3章から5章までに書かれているクラス図とシーケンス図だ。クラス図を知っておけばオブジェクト指向設計をする上で必要な継承・集約・コンポジット・依存関係などをすばやく組み立てることができる。また、シーケンス図を知っておけば「オブジェクト指向設計実践ガイド」でも使われているようにカプセル化やSOLIDの原則に違反している箇所に気づきやすくなるため有効だと思う。その他、有用そうなものは、上流工程としてユースケース、詳細設計として状態マシン図くらいだ。

プログラミング言語パラダイム

最後にパラダイムについて少しだけ触れて終わりにする。様々なパラダイムがこれまでの歴史上発明されたにもかかわらず、オブジェクト指向設計は20年以上に渡り第一線のプログラミングパラダイムとして使用されている。しかしここ数年の動きとしては、リアクティブプログラミングや関数型プログラミングにも注目が集まっており、それぞれの特性を理解した上で次に乗る船を決めたい。手始めに「関数型リアクティブプログラミング」などを読んでみたいと思っている。

まとめ

オブジェクト指向言語を使うのなら、オブジェクト指向設計はちゃんと学んだほうがいい。オブジェクト指向設計に関する定番的な本は2000年代前半までに書かれたものが多く、いまさら結構な金額の本を読むのは抵抗があると思うが、逆に考えると定番さえ読めば特に重要な先人の知恵をストレートに得ることができるので楽なのかもしれない。