OIDCへの攻撃とパラメータの役割の整理

OAuth・OIDCへの攻撃と対策を整理して理解できる本で攻撃方法や各種パラメータの役割が理解できた。

ただ、上記の本の切り口としては、攻撃方法、フローごとで説明されており、パラメータでの切り口がなかったので改めて整理してみる。

パラメータ 付与する主体 検証する主体 付与するタイミング 検証するタイミング 対応可能なフロー 対応可能な攻撃 概要 メモ
state RP RP 認可リクエス 認可レスポンス - code flow
- implicit flow
- CSRF 認可リクエストの際にstate(ランダムな文字列)を付与し、セッションに紐づけておく
認可レスポンスの際にセッションに紐づいたstateが返ってくることを検証
nonce(ナンス) RP RP 認可リクエス 認可レスポンス / トークンレスポンス
(ID Tokenを受け取ったタイミング)
- implicit flow
- code flow
- リプレイアタック
- CSRF
- コードインジェクション
認可リクエストの際にnonce(ランダムな文字列)を付与し、認証リクエストに紐づけておく
認可レスポンスの際に認証リクエストに紐づいたnonceが返ってくること、nonceが一度も使われていないことを検証
PKCE RP IdP 認可リクエス トークンリクエス - code flow - 認可コード横取り攻撃
- CSRF
- コードインジェクション
認可リクエストの際にcode_challenge(ランダムな文字列)とcode_challenge_method(plainまたはS256)を付与
トークンリクエストにcode_challenge_methodで指定した方法にしたがってcode_verifierを決定して付与
IdPはトークンリクエストに付与されたcode_verifierが正しいことを検証
at_hash IdP RP 認可レスポンス 認可レスポンス - implicit flow
- hybrid flow
- トークンインジェクション ID Tokenの署名と同じハッシュアルゴリズムでアクセストークンのハッシュ値を算出し、その左半分を Base64URL エンコードした文字列 をID Tokenのペイロードに含める
RPがコールバックを受け取った際にアクセストークンがID Tokenに紐づくものかを検証
OAuthにはID Tokenがないので使えない
c_hash IdP RP(アプリ) 認可レスポンス 認可レスポンス - hybrid flow(response_typeにid_tokenとcodeを含む) - コードインジェクション ID Tokenの署名と同じハッシュアルゴリズムで認可コードのハッシュ値を算出し、その左半分を Base64URL エンコードした文字列 をID Tokenのペイロードに含める
認可レスポンスを受け取ったアプリがcodeがID Tokenに紐づくかを検証
OAuthにはID Tokenがないので使えない

代表的な攻撃

代表的な攻撃の概要についてもメモしておく

CSRF

Authorization Code Flow, Implicit Flow, Hybrid Flowで可能

攻撃者の認可コード・アクセストークンを第三者の管理下にあるアプリに適用 する攻撃

リプレイ攻撃

response_typeid_token を含むフローで可能

不正に入手したID Tokenを使って第三者になりすます攻撃

認可コード横取り

response_typecode を含むフローで可能

不正に入手した認可コードを使ってログインしたり、第三者のアクセストークンを取得してしまう攻撃

主にネイティブアプリで悪意のあるアプリがアプリへのリダイレクトで認可コードを横取りするようなケースを想定している

トークンインジェクション

response_typetoken を含むフローで可能

三者の有効なアクセストークンを攻撃者自身の管理下にあるアプリに適用する攻撃

コードインジェクション

response_typecode を含むフローで可能

OAuth・OIDC のリダイレクト部分で認可コードを他者のものに入れ替える攻撃

nodeでバッチスクリプトを書くときの道具箱

お久しぶりです。
業務をしているとちょっとしたバッチスクリプトを書くことがあると思います。 最近たまたまnodeでCSVを使ったバッチ処理を書くことがあったので、その際のtipsをまとめてみます。 nodeは最近標準ライブラリが少しずつ充実してきましたが、普段からゴリゴリnode書いてない身からするとGoやRubyといった標準ライブラリやノウハウが浸透している言語の方がやりやすい部分はあるなと感じた今日この頃

紹介するtips

  • CSVから行を逐次読み込みする
  • コマンドライン引数を取得する
  • タイムスタンプのついた構造化ログを吐く
  • 非同期処理の並列数を制限しつつ並列で実行する
  • jest, vitestを使わずにテストコードを書く

CSVから行を逐次読み込みする

バッチ処理などでは巨大なファイルを扱うことも多いでしょう。
ファイルを扱う場合は、ファイル全体を読み込むのではなく1行ずつ読み込みながら処理を進めていくことで少ないメモリ使用量でサイズの大きなファイルも扱うことができます。
そのため、あらかじめファイルサイズが限定されている場合を除いて出来るだけ逐次行を読み込んで処理する書き方をすることが一般的でしょう。

nodeではcsvというライブラリを使うことでCSVの生成・変換・読み込み・書き出しをすることができます。 CSVをパースする方法は同期的にパースする方法やcallback関数を使う方法もありますが、今回はstreamを使う方法を紹介します。 fs.createReadStrem を使ってファイルのストリームを取得しpipeでparserを渡してあげることで、CSVの一行ずつイテレートする AsyncIterator を取得することができます。

import * as fs from 'node:fs';
import { parse } from 'csv';


const fileStream = fs.createReadStream('items.csv');

const rows = fileStream.pipe(parse({ trim: true, skip_empty_lines: true }));

for await (const row of rows) {
    console.log(row); // ['xxxx', 'yyyy', 'zzzz']
}

CSV Parse - Async iterator API

コマンドライン引数を取得する

これまでも process.argv を使うことでコマンドライン引数を扱うことができましたが、最近のバージョンでより便利なutil関数が標準ライブラリに追加されました。 util.parseArgs はv18.3でexperimentalとして追加されv20.0から正式なAPIになりました。

import * as util from 'node:util';

const { values, positionals } = util.parseArgs({
    allowPositionals: true, // デフォルトでは、オプション指定のない引数は指定できない
    options: {
        env: {
            type: 'string',
            short: 'e',
        },
        target: {
            type: 'string',
            short: 't',
            multiple: true,
        },
    },
});

console.log(values)
console.log(positionals)
$ node parse-args.mjs items.csv -e local -t foo --target bar
> [Object: null prototype] { env: 'local', target: [ 'foo', 'bar' ] }
> [ 'items.csv' ]

Util | Node.js v21.6.2 Documentation

タイムスタンプのついた構造化ログを吐く

バッチ処理ではいつどこまで処理が進んだか・いつどんなエラーが出たかを記録しておくことが重要です。
その際には、ログにタイムスタンプが必要になりますし、またログが構造化されていると後々解析するのに便利です。
また、スクリプトを実行中ログをターミナルに出力しつつもファイルにも保存しておきたいということもあるでしょう。今回は、pinoというライブラリを使ってこの辺りを実現してみます。

import { pino, stdTimeFunctions } from 'pino';

const transport = pino.transport({
  dedupe: true,
  targets: [
    {
      level: 'info',
      target: 'pino/file',
      options: { destination: '1' }, // stdout
    },
    {
      level: 'error',
      target: 'pino/file',
      options: { destination: '2' }, // stderr
    },
    {
      level: 'info',
      target: 'pino/file',
      options: { destination: './logs/info.log', mkdir: true },
    },
    {
      level: 'error',
      target: 'pino/file',
      options: { destination: './logs/error.log', mkdir: true },
    }
  ]
});

const logger = pino({
  timestamp: stdTimeFunctions.isoTime,
}, transport);

logger.info('info'); // written to stdout and info.log
logger.warn('warn'); // written to stdout nad info.log
logger.error('error'); // written to stderr and error.log
logger.fatal('fatal'); // written to stderr and error.log

上記のようにするとターミナルとそれぞれのファイルに以下のようなログを出力することができます。

// info.log
{"level":30,"time":"2023-04-23T03:23:47.960Z","pid":79891,"hostname":"Ayatos-MBP","id":1,"msg":"info"}
{"level":50,"time":"2023-04-23T03:23:47.960Z","pid":79891,"hostname":"Ayatos-MBP","id":3,"msg":"error"}

// error.log
{"level":40,"time":"2023-04-23T03:23:47.960Z","pid":79891,"hostname":"Ayatos-MBP","id":2,"msg":"warn"}
{"level":60,"time":"2023-04-23T03:23:47.960Z","pid":79891,"hostname":"Ayatos-MBP","id":4,"msg":"fatal"}

また、構造化されたログは人間には読みづらいため開発環境では pino-pretty を使うとヒューマンリーダブルなログをターミナルに表示することができます。

pino-pretty

GitHub - pinojs/pino: 🌲 super fast, all natural json logger

非同期処理の並列数を制限しつつ並列で実行する

バッチスクリプトでは、多くのケースでhttpリクエストやDBアクセスなどのIO処理を利用するでしょう。
その際、出来るだけ効率的に処理するために処理をasync関数としてIOを並列化して処理することが考えられます。 しかし、単に全ての処理を非同期実行して Promise.all するようなやり方ではリクエストの並列数をコントロールすることができません。
この解決には複数の方法が考えられ、タスクをチャンクしチャンクごとに Promise.all するようなやりかたなども考えられますが、今回のような AsyncIterator を使うような場合はchunkしづらいため別の方法を取ります。

今回は、iteratorを消費するワーカーを複数作ることで、並列に実行しつつ並列数を制御します。
このやり方の場合、 for await ... of を使うことで簡単に AsyncIterator に対応することができますね。

const iterator = Array.from('abcdefghi').values();

async function doWork(iterator) {
  for await (const value of iterator) {
    await setTimeout(1000);
    console.log(value);
  }
}

// イテレータを消費するワーカーを3つ作る
const workers = new Array(3).fill(iterator).map(doWork);

// 全てのワーカーの処理完了を待つ
await Promise.allSettled(workers);
a
b
c
# 1秒後
d
e
f
# ...

Run Concurrent Tasks With a Limit Using Pure JavaScript - Maxim Orlov

jest, vitestを使わずにテストコードを書く

書き捨てのスクリプトだがテストは書いておきたいという場合にわざわざTesting Libraryを導入して設定するのは面倒ですよね。
v18からexperimentalで導入されていたnode標準のTest Runnerがv20で無事stableになりました。
サクッとテストを書きたい場合にライブラリをインストールする必要がないのでありがたいですね。

非同期処理を含むテストも書けますし、テストの構造化もできます。describe,it を使って書き方や before , afterEach といったフックもできるみたいなので、ちょっとテスト書いて動作確認したいといった場合にライブラリが必要になる場面がかなり減りそうです。

import { test, describe, it } from 'node:test'
import * as assert from 'node:assert';


test('first test', (t) => {
  assert.strictEqual(1, 1);
});

test('asynchronous passing test', async (t) => {
  assert.strictEqual(1, 1);
});

test('top level test', async (t) => {
  await t.test('subtest', (t) => {
    assert.strictEqual(1, 1);
  });
}); 

describe('A thing', () => {
  it('should work', () => {
    assert.strictEqual(1, 1);
  });
}); 

実行結果もそこそこ見やすそうでいい感じです。

node:testの実行結果

Test runner | Node.js v21.6.2 Documentation

感想

node自体は慣れているつもりでしたが、普段書かないような処理を書こうと思うと意外に標準ライブラリの使い方やライブラリの選定が必要になりますね。
近年nodeの標準ライブラリが便利になってきているのでさらに今後に期待です。

intellijでGPGが使えなくなった件の備忘

yubikeyにGPGの鍵を取り込んで運用してます。 コミットの署名やGitHubへのSSHはyubikeyに取り込んでいる鍵を使うようになっているのですが、一時期からintellijでうまくコミットやfetchができなくなってしまいました。

恐らくこの問題が発生したのはIDEのインストールを個別に行わずToolbox経由に切り替えたあたりからだった気がします。 ターミナルでの運用は問題なくintellijの問題かと思ってしばらくアップデートを待っていましたが一向に直らない。

intellijのSHELLが /bin/sh になっていた

Mac使っているのでデフォルトのシェルは zsh ですが、気づいたらintellijの内部で使われているシェルが sh になってました。 intellij上で使うターミナルエミュレーターには、明示的に zsh を指定しているので気がつきませんでしたが、IDEが内部でgitなどのCLIを動かすときに使われているシェルが sh になってました。
ただ、これは社用Macで起きていたのですが、私用のMacでToolbox経由でintellijインストールしてもプロジェクトに設定されている SHELL/bin/zsh だったのでなぜそうなってしまったのか。。
intellijで有効になっているシェルはTools > Terminal > Project settings > Environment Variablesのダイアログを開くと確認できるっぽいです。

intellijで有効になっている環境変数
.profile でもGPGのパスと環境変数を設定してあげることでintellijで問題なくGPGを使えるようになりました。

React Routerのルーティング可能なモーダルのアイデア

ようやくReact Router v6が正式リリースされましたね。 1年以上のベータ期間では、GitHub上のドキュメントを読むしかありませんでしたが、公式のページもリニューアルされました。
今回は、SPAで一度は悩むであろうルーティング可能なモーダルの実装のアイデアを紹介します。

公式ドキュメントでもパスに対応したモーダルを表示するexampleが紹介されていますが、公式の実装は直接コンテンツにアクセスした場合にはモーダルではないページを表示する実装になっています。 特定のパスにアクセスした際にモーダルを表示したいという場合にはこのexampleは参考になりません。

コンポーネントベースのルーティングとオブジェクトベースのルーティングを組み合わせる

v6では、バンドルサイズが大幅に改善されたのに加えhooksの充実やインターフェースの変更が行われました。 その中で、useRoutesというオブジェクトスタイルでルーティングを定義できるhooksが追加されています。
v5では、恐らくパスがモーダルのパスかどうかをどうにかして判別してハンドリングする必要がありましたが、v6ではuseRoutesRoutesコンポーネントを組み合わせることでシンプルにルーティング可能なモーダルを実装することができます。
この実装では、特定のパスでモーダルを直接開くことができると同時に、任意のページを表示している際にリンクから任意のモーダルを表示することもできます。 ただし、モーダルを直接開く場合のバックグラウンドのページはルートになります。

reference

React Router - useRoutes

rechartsを使ってちょっと複雑なグラフを描いてみる

今回は、rechartsというReact向けのグラフ描画ライブラリを使って少し複雑なグラフを作ってみます。
rechartsはexampleやAPIドキュメントが整備されており、比較的はじめやすいライブラリではありますが、ドキュメントに記載されていない機能もちょこちょこあります。

今回やってみる内容

  • グリッドをストライプにする
  • グラフ内に複数のデータソースから複数の種類のグラフを表示する

作ってみたグラフは、生後0 ~ 24ヶ月の乳幼児の体重、身長の平均値から算出したガイドとその上に実際の子供のデータをプロットするというようなことをイメージしています。

グラフのグリッドをストライプにする

まずは、簡単な方から

グラフに表示されるグリッドの帯に互い違いに色をつけてストライプにすることができます。

<CartesianGrid
  horizontalFill={['#C0C0C0', '#FFFFFF']}
  fillOpacity={0.2}
  stroke="lightgrey"
/>

グラフにグリッドを表示させるには、CartesianGrid を用います。
このコンポーネントには、 horizontalFill , verticalFill という属性が用意されており、ここに配列で希望する色を配列で指定することでグリッドをストライプにすることができます。
ただ、その際何もしないとグリッドのストロークが消えてしまうので fillOpacity を使って調整する必要があります。

github.com

複数のデータソースから複数の種類のグラフを表示する

ということでここで具体的に実現する内容は以下になります。

  • y軸をふたつ表示する
  • AreaChartとLineChartを両方表示する
  • AreaChartとLineChartで別々のデータソースを使用する

y軸をふたつ表示する

y軸をふたつ表示するには、まさに YAxis をふたつ用意すれば良いのですが、その際 orientation 属性で表示位置を指定した上で yAxisId 属性でそれぞれの軸の識別子を定義します。
その後、 それぞれのコンポーネントでも明示的に yAxisId を指定することで軸とデータを紐付けます。

<YAxis
  yAxisId="weight" 
  orientation="left"
/>
<YAxis
  yAxisId="height"
  orientation="right"
/>
<Area
  dataKey="weight"
  yAxisId="weight"
/>
<Area
  dataKey="height" 
  yAxisId="height" 
/>

AreaChartとLineChartを両方表示する

ComposedChart を使うと複数の種類のチャートを同時に表示することができます。
ただ、今回の場合ガイドである AreaChartツールチップや凡例の表示から除外したいですね。
その際、 tooltipType="none" , legendType="none" を指定することでツールチップ、凡例を非表示にすることができます。

<ComposedChart
  data={guides}
>
  <Tooltip />
  <Legend />
  ...
  <Area
    type="monotone"
    dataKey="weight"
    yAxisId="weight"
    tooltipType="none"
    legendType="none"
  />
  <Area
    type="monotone"
    dataKey="height"
    yAxisId="height"
    tooltipType="none"
    legendType="none"
  />
 ...
</ComposedChart>

AreaChartとLineChartで別々のデータソースを使用する

グラフのガイドは事前に定義したものを使い、実際のデータと別々の状態で扱いたいですよね。
データソースがひとつに限定される場合、ふたつのデータをうまくマージしなければいけません。

recharts では、基本的に AraaChart などの基本的なラッパーコンポーネントの方にデータを指定して各コンポーネントで参照するキーを指定する形なので複数のデータソースが利用できないように見えますが、 data 属性を使って Line などのコンポーネントに直接データを入れることもできます。 今回の例では、ガイドのデータは Chart コンポーネントに指定し実際のデータを Line コンポーネントに直接入れています。

<Line
  type="linear"
  data={records}
  dataKey="weight"
/>

終わりに

今回は、rechartsを使って公式のexamplesにもないようなグラフを作ってみました。 rechartsは、高機能で使いやすくドキュメントもある程度整備されていますが、凝った機能はドキュメントにも載っていなかったりするのでstackoverfllowや実際のソースから自分の要求する機能が存在するかどうか確認する必要がありますね。

2020年の学習と2021年やりたいこと

年末年始あたりに投稿しようと思っていましたが、大分遅れてしまいました、供養。

2020年に取り組んだもの

  • React - The Complete Guide (incl Hooks, React Router, Redux) - Udemy
    主にサーバーサイド、インフラをメインにしていますが、フロントもある程度できるようになっておきたいというのと、近い将来業務でもフロントの開発をすることになりそうということでやってみました。
    Vueに関しても少々触ったことがあるのですが、Reactの方がTypeScriptとの相性がよい点、Vueと比べてプリミティブなためJSやライブラリの仕組みを理解する必要があり結果的に勉強になる点からReactを集中的に勉強することにしました。
    ただ、当初自分にとってReactの仕組みやReduxの考え方にはハードルが高くQiitaの記事などをいくつ読んでもわかったようなわからないような感じでした。
    そのため、Udemyの動画を見ながら写経することにしたのですが、日本語だと選択肢があまり多くないため英語のコースも含めて検討したところ、このMaximilianさんのコースは評価が高く、Reactを網羅的に学習できそうだったので選びました。
    モダンJSの記法からReactの仕組み、Reduxの考え方を解説しながら最終的にReact Hooks, React Sagaまでカバーしてアプリケーションを開発することで網羅的な学習をすることができました。
    ビデオ自体が全部で約40時間ほどあり、写経・デバック等含めて100時間くらいは使った気がしますが、かなりReactに慣れました。

  • Understanding TypeScript - Udemy
    上記と同様でUdemyのMaxilimianさんのコースです。 こちらもわかりやすかったですが、ボリュームはReactほどなく、TypeScriptを使いこなすにはやはり実践で型システムをガンガン使っていくしかないのかなという印象

  • りあクト! TypeScriptで始めるつらくないReact開発
    Udemyのコースでは、valina jsでのコースだったため、TSとReactの感触をつかむために読みました。 ただ、React, Redux, JS/TS自体の話はUdemyでほとんど網羅できていたと思う。

  • DNSをはじめよう ~基礎からトラブルシューティングまで
    DNSの基本的な仕組みから、代表的なレコードの役割を学ぶことが出来た。 雰囲気でRoute 53を触っていたが、これを読んだおかげで大分自信をもってRoute 53をいじれるようになった。

  • SSLをはじめよう ~「なんとなく」から「ちゃんとわかる!」へ~
    httpsの重要性やSSL証明書の仕組みを学ぶことができた。 重要な技術だがDNSほど普段細々とした作業もないので、実作業などではまだ活きる場面に出会ってない。

  • Goプログラミング実践入門 標準ライブラリでゼロからWebアプリを作る
    社内の一部でGoを採用するため、こちらで勉強させていただきました。 シンプルな文法と標準ライブラリの機能が充実していること、非同期処理が手軽にかけること、コンパイルが早くテスト、ベンチが外部依存なしにできることなどなどかなり魅力的な言語でした。 RubyRailsデファクトになっていることとは対照的にGoでは、あまり厚いフレームワークを使わない傾向があると思います。 こちらの書籍では基本的に標準ライブラリを利用してWeb アプリケーションの構築工程が解説されおり、標準ライブラリの仕組みやインターフェースになれることが出来ます。 外部のライブラリを使う場合も標準ライブラリでのインターフェースが踏襲されていることも多いので、標準ライブラリになれておくことは重要なようです。

  • Pragmatic Terraform on AWS
    実務でインフラを0から組むことがあり、IaSを一気に推し進めるためterraformの導入を決めました。 使い方やディレクトリ構成の指針として活用させていただきました。 AWSのサンプルコードが豊富でかなり助かりました。

  • 低レイヤを知りたい人のためのCコンパイラ作成入門
    これはまだ「電卓レベルの言語の作成」までしか終わってませんが、写経して実際に手元で動かしながら取り組んでいます。 この教材に出会うまでコンパイラがどうゆう仕組みで動いているか、ほとんど想像できない状態でしたが、構文解析アセンブリの出力がどうゆうものなのかある程度理解することが出来ました。 これまで、アセンブリを触ることは全くなかったのですが、低レイヤへのとっかかり、Cやmakeに慣れる機会としてかなり良かったです。 今後も時間があれば続きをやりたいと思います。

  • HTML5&CSS3デザイン 現場の新標準ガイド【第2版】
    上述のUdemyのReactの教材では、HTML/CSSに関しては突っ込んだ話はないので、現状を把握するために読みました。 どちらかというと辞書的な使い方が想定されるような書籍ですが、読み切れないボリュームではないのです。 これまでなんとなく使っていたHTMLタグの使用や把握していなかったCSSプロパティを頭にいれることで、フロントエンドの実務でキャッチアップがかなりスムーズになったと思います。

  • Web API: The Good Parts
    大量のAPIを開発する機会があり、体系的な教材が欲しかったので読みました。 Web APIを設計する上での、エンドポイントの考え方、レスポンスのスキーマの考え方などいくつかの指針を得ることが出来た。 また、HTTPにおいてのセキュリティについても記載があり、特にHTTPのセキュリティヘッダに関してはこれまで意識する機会がなかったので役に立ちました。

  • ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本
    これまでDDDについては、Evans本を読んだり(途中まで)、記事などで断片的に情報を仕入れていましたが、こちらの書籍は読みやすく具体的なサンプルコードが豊富で DDDのとっかかりとしてかなりよいと思いました。 ただ、DDDの本質的な部分は深く説明されているわけではないので、やはり概念的な部分を深く理解するにはEvans本やその他の本を参照しながら実際にトライアンドエラーを繰り返していかないといけないという印象

2021年やりたいこと

今年は、今のところ「オブジェクト指向のこころ」、「体系的に学ぶ 安全なWebアプリケーションの作り方」を読みました。 あと前々から興味のあったRustに入門するため「実践Rustプログラミング入門」を読み進めてます。 公式のドキュメントある程度読んだことがあるのですが、こうゆう書き方・ライブラリがよく使われますみたいなのが書かれていて結構よいです。 今後は「Linuxの仕組み」、「マスタリングTCP/IP」、「Real Wold HTTP」あたりを読みたい、普段使っている技術の中身を知るのが面白いというのと、ブラックボックスにしておきたくないというのがある。 競プロやってみないのでその勉強もしたいなーという感じです。

オブジェクト指向のこころを読んだ

TD;DR

雰囲気でオブジェクト指向言語を使っていた

今まで、オブジェクト指向言語を日常的に利用し、プログラミング言語の書籍に書いてある「オブジェクト指向」の使い方やインターネット上の断片的な情報を元に『オブジェクト指向』を使ってきました。

エンジニアにとってアプリケーションの設計は永遠のテーマであり、私もドメイン駆動設計関連の記事や書籍をいくつか読んでは完全に消化仕切れずにいました。 段々わかってきたのがドメイン駆動設計はオブジェクト指向の延長線上にあるっぽいということ。

そもそも自分は現状もっとも広く利用されているオブジェクト指向というパラダイムをを中途半端にしか理解できていないのだからドメイン駆動設計を学ぶ段階になかったのです。

ということで、オブジェクト指向の書籍を探したのですが、複数の知人がおすすめしていたためこちらの書籍を選びました。 電子版がなく大型本なので物理的に読みやすくはなかったのですが、内容は非常に良かったので満足しています。

本書のながれ

まず、とあるシステムを題材に継承を多用した従来のオブジェクト指向の手法で設計をし、要求の変更があった際の保守性や柔軟性といった継承の問題を示します。
その後、いくつかのデザインパターンを紹介し、先ほどのシステムをデザインパターンを利用した形で再度設計します。
これによって、特殊化と再利用目的とした継承ではなく、デザインパターンを利用することでより堅牢で柔軟な設計が可能であることが説明されています。
また、既存のデザインパターンはそれぞれ特定の状況で適用するものですが、インターフェースを用いて設計し、集約を用いて流動的要素をカプセル化するというデザインパターンの教訓はどのような設計でも適用可能であり、それに従うことで柔軟な設計が可能なことが語られ、オブジェクト指向の原則をより深く解説されています。

プログラミング言語入門本では、継承などの機能的な側面は説明されてもなかなかクラス・オブジェクトの責務や実装、設計のカプセル化といったトピックは深く説明されないことが多いと思います、本書では実際にパターンを利用して設計するプロセスを見せることでオブジェクト指向の本質を実感できるつくりになっています。
本書を読んだことで、オブジェクト指向の階段を確実に進んだ実感がありますし、本書で紹介されていないデザインパターンについてもひと通り理解しておきたいというモチベーションも生まれました。

関数型言語への興味

オブジェクト指向をある程度「うまくやる」方法は理解したものの、そもそもオブジェクト指向で語られる適切な抽象化というものが非常に難しいことは日々実感しているところである。

そんな中ふと、オブジェクト指向関数型プログラミングを検討した記事を読んだことでオブジェクト指向以外のパラダイムも勉強してみたいなーと思ってしまった。
Elixir から Elm の流れで、いよいよオブジェクト指向に対する懐疑心が無視できないレベルに達した2017年冬。

近年おおくのOOP言語は関数型言語から機能を輸入はしているものの、やはり純粋関数型言語をやらないと関数型プログラミングのなんたるかはわからないだろうなーと思い とりあえずすごいHな本を買ってしまったので、地道に読んでいきます。。

オブジェクト指向のこころ