うぃろぅ.log

140字で綴りきれない日々の徒然備忘録

【備忘録】 「SQLアンチパターン」を読む

うぃろぅです。

今日も本を読みます。目指せ技術書の虫。

今日の本

SQLアンチパターン

SQLに関係ある人、恐らくWeb関係者の大半が読むべき本として有名な本。
「こんなDBはイヤだ」を集めた本。歴史に学ぼう。

構成

アンチパターンの種類を4つに大別している。

DB自体の設計、SQL発行の仕方、多言語からSQLを発行する際、と徐々に本体から遠ざかっていく形になっている。DBが良くても呼び出し方がね、とならないための構成。

1部 データベース論理設計のアンチパターン

1章 ジェイウォーク(信号無視)

カンマ区切りで複数データを持ってしまうやつ。
交差テーブル(交差点)を避ける行為であることから。

外部キー設定するべき箇所をVARCHAR型にしてカンマを許容してしまったらさあ大変。
その外部キーを用いて検索、なんてことが非常に困難になる。

商品ID 商品名 倉庫ID
1 声優パラダイス 1,2
2 声優アニメディア 2,3
3 声優グランプリ 1,4
倉庫ID 倉庫名
1 大宮
2 浦和
3 与野
4 岩槻

例えばこう。申し訳程度の声優要素。倉庫名がローカル。
「大宮倉庫に保管している商品」を引っ張ってきたい場合が地獄。
さらにはカンマを許容するために文字列型にしているため、倉庫IDに「春日部」みたいな文字列を入れることもできてしまってもうそれは阿鼻。

解決策は簡単で、「交差テーブルを避けてないでちゃんと作れ」ということである。
上記の例であれば保管テーブルでも作ればいいんじゃないかな。

保管ID 商品ID 倉庫ID
1 1 1
2 1 2
3 2 2
4 2 3
5 3 1
6 3 4

こんな感じ。保管日等は直接関係していないため省略している。

2章 ナイーブツリー(素朴な木)

掲示板のコメント欄のような「木コメ」に「枝コメ」や「葉コメ」がぶら下がる場合のコメントテーブルがあるとして、各コメントが「親コメント」しか持っていないやつ。

「葉コメ」から「木コメ」のような2つ以上離れた関連を辿って集計する際の処理が煩雑になってしまう。

例として適当なコメントを探そうかと考えたときに思い浮かんだのが某スマホゲーWiki某サーヴァントのコメント欄。少し前まで荒らしが横行していたが最近落ち着きを取り戻したらしい。落ち着きの定義が問われる。

それっぽい箇所を抜粋する。

コメントID 親コメントID コメント内容 コメント日時
1 (null) やっと、本当に久々に、平和なナーサリー板が戻ってきた(感涙)。記念にナーサリーちゃんprpr 2019-07-01 03:32:33
2 1 真ナが妙な変異起こしてて不安だけどな… 2019-07-01 07:38:12
3 2 インド帰りなんだろ 2019-07-02 09:44:10
4 2 じゃあ、いつもみたいに消しちゃう?不出来で不要になった真ナーサリーも? 2019-07-02 09:56:47
5 4 真ナーサリー「やってみろ」(ニチャア 2019-07-02 11:30:03
6 2 真ナが変なのって荒しなんじゃねえの? 2019-07-02 19:34:37
7 6 わざわざ管理人さんの規正報告に枝付けして意味不明なこと書いているあたりそれっぽいよな… 2019-07-03 10:16:42
8 1 (誰か木主に、お前がprprしてるのはナーサリーではなく叔父貴だという事を教えてあげて…) 2019-07-01 09:43:00

隣接リスト(Adjacency List)と呼ばれる設計。
コメントID2の枝を削除しようとする場合、外部キー参照の整合性制約を満たすため、2の子孫を辿っていって終端から逆順に削除していかなければならない。2を最初に削除すると346親コメントIDが不正になってしまうからである。
これはちょっと大変。

解決策はいくつかある。

  • 経路列挙(Path Enumeration)
  • 入れ子集合モデル(Nested Set)
  • 閉包テーブル(Closure Table)

全て詳しく書くのは冗長なため書籍参照。

例として経路列挙だけ書くと

コメントID コメントパス コメント内容 コメント日時
1 1/ やっと、本当に久々に、平和なナーサリー板が戻ってきた(感涙)。記念にナーサリーちゃんprpr 2019-07-01 03:32:33
2 1/2/ 真ナが妙な変異起こしてて不安だけどな… 2019-07-01 07:38:12
3 1/2/3/ インド帰りなんだろ 2019-07-02 09:44:10
4 1/2/4/ じゃあ、いつもみたいに消しちゃう?不出来で不要になった真ナーサリーも? 2019-07-02 09:56:47
5 1/2/4/5/ 真ナーサリー「やってみろ」(ニチャア 2019-07-02 11:30:03
6 1/2/6/ 真ナが変なのって荒しなんじゃねえの? 2019-07-02 19:34:37
7 1/2/6/7/ わざわざ管理人さんの規正報告に枝付けして意味不明なこと書いているあたりそれっぽいよな… 2019-07-03 10:16:42
8 1/8/ (誰か木主に、お前がprprしてるのはナーサリーではなく叔父貴だという事を教えてあげて…) 2019-07-01 09:43:00

こんな感じになる。
文字列になってしまうため、ジェイウォークと同じような扱いづらさがあることが欠点と言える。

閉包テーブルがおしゃれでいい感じ。深さも付与した閉包テーブルを作成すればノードの追加や移動があったとしてもソートしやすそう。

「絶対にこれ」ではなく各々の利点と欠点を理解して使い分けることが重要。

3章 IDリクワイアド(とりあえずID)

全てのテーブルに自動生成のIDをつけるやつ。
これは賛否が分かれる。

とりあえずidではなくhoge_idのように意味のあるIDを振るのが大事。命名が大事なのはどの言語でも一緒である。

例はなし。わかるし。

4章 キーレスエントリ(外部キー嫌い)

読んで字のごとく、外部キーを設定するべき箇所に設定していないやつ。
サポートしていないなら仕方ないにしてもあるものは使うべき。

外部キー参照がないとレコードの削除時に不整合が起こる。
外部キー設定時にデータ更新が面倒になることもあるかもしれないが、整合性がないテーブルよりはるかにコストが低い。

解決策としては外部キーを設定する。そりゃそうだ。

5章 EAV(エンティティ・アトリビュート・バリュー)

DBなのに行列でデータを持とうとしてしまうやつ。
文字だけではわかりづらい。

掲載されていた例はこちら

簡単なところでは健康測定のテーブルとして

出席番号 測定対象 測定結果
334 身長 170.2
334 体重 65.5
334 座高 85.6
334 視力(右) 0.8
334 視力(左) 0.4
334 最低血圧 95
334 最高血圧 130

こんな感じにしてしまう。主キーは出席番号測定対象の複合。
NHKとかガチャ目すごいとかちょっと足長めかなとかそういうことではない。

これをしてしまうと、「この測定結果は整数でこの測定結果は少数」のような制約をつけることができない。さらに「医師面談結果」みたいな測定対象があったとして測定結果が「異常なし」だとすると文字列になってくる。考えるだけで地獄が見える。

こういった運用をしたいのであればそれはSQLではなく別のDB管理方法をとった方が良くなる。リレーショナルである意味がなくなるからである。
今時こんなことしないやろって思っていたが以前携わったプロジェクトにこれを採用しているテーブルがあった。うーむ。

解決策としては

  • シングルテーブル継承
    属性名を列にする
  • 具象テーブル継承
    属性ごとにテーブルを作る
  • クラステーブル継承
    オブジェクト指向の継承のようにテーブルを継承する
  • 半構造化データ
    追加や削除が多い属性をXMLJSONで取り扱い、それを格納する列を設ける

といった方法がある。
属性の更新頻度や量などで総合的に判断すべし。

6章 ポリモーフィック関連

子テーブルに対して親テーブルの選択肢が複数ある場合に、親テーブルの名前をレコードとして持つやつ。

解決策の1つとしてポリモーフィック関連があるが、フレームワークでサポートしている等の理由ではない限り避けた方が無難。
Railsではサポートしているが、用法容量を守って正しく使おう。
親のクラスによって出し分ける、なんてあってはならない。

ポリモーフィック関連を避けた解決策として

  1. 参照を逆にする
  2. 複数の交差テーブルを作成する
  3. 交差点に交通信号を設置する
    複合キー+そのうちの1つにユニーク制約を付ける
  4. 両方の「道」を見る
    複数の交差テーブルをjoinする
  5. 「道」を合流させる
    unionで結合してビューにする
  6. 共通の親テーブルを作成する

という手法がある。4以降はどれかを選択する。

共通の親テーブルを作成すれば良さそうに見える。Kernelモジュールみたいな。

7章 マルチカラムアトリビュート(複数列属性)

1つのデータに複数の同系統データが関連するとき、複数の列を作成してしまうやつ。
これも文字だけではわかりづらい。

(私にとって)身近な例で言えばTwitterのアンケート機能。

最近投げたアンケ。シュタゲゼロ今観てるよ。消滅都市は観ると記憶が消滅するから見なくていいよって言われたけれど果たして。
閑話休題、この選択肢は2~4個までの間で可変。

このアンケートテーブルを

アンケートID ツイートID 回答期限 選択肢1 選択肢2 選択肢3 選択肢4
101 123 1:00 消滅都市 五等分の花嫁 シュタインズ・ゲート ゼロ スター☆トゥインクルプリキュア

こうしちゃダメだよということ。
(この例だとやらなさそうだけれど)値が「消滅都市」の選択肢を検索する際、選択肢1~4を全て検索しなければならないため、効率が悪い。
更に選択肢を2つのみにする場合、値が入らない箇所はnull値となる。わかりやすい感想としては気持ち悪い1
また、選択肢を5個まで増やせるように機能が変更されたとしたらテーブルに列を追加する必要が出てくる。これはとてもコストがかかる。

解決策は従属テーブルを作成する。
この例だと選択肢テーブルを作れば良い。
ジェイウォークと同じような対処法となる。

8章 メタデータトリブル(メタデータ大増殖)

メタデータ」はテーブルや列のこと。行はデータ。
これがトリブルのように無限増殖すること。海外のTVドラマに出てくる毛玉みたいなキャラクターの名前らしい。

年ごとや月ごとににテーブルを増やす、列を追加する、とかそういうこと。
テーブルの名前を変えてコピーする場合、コピー元のテーブルにバグがある場合そのバグごとコピーされることになる。列に変更がある場合は全てのコピペ先に適応しなければならない。コピペプログラマみたいだな…。

過去データを分離して保存しておく場合は例外的にこれもあり。変更しないで参照するだけ、とか制約を付けておくとなお良し。

解決策はパーティショニングと正規化。

lets.postgresql.jp

qiita.com

RDBMSがサポートしているパーティショニングを用いてテーブルを分割できる。
物理テーブルが分かれていたとしても内部でうまいこと振り分けてくれる仕組み。

1部は以上となる。プロジェクトで見た覚えのあるアンチパターンがいくつかあって心が痛い…。

2部 データベース物理設計のアンチパターン

9章 ラウンディングエラー(丸め誤差)

小数を扱う導出項目のデータ型をfloatにすると、精度が落ちてしまう。
JavaC#ではDecimal型があるためそれを使えば概ねなんとかなる2

解決策はnumericdecimalを使う。SQLにもあるのね。

正確な計算はどの言語でも気を遣うよね。金融系のシステムなんかでは必須。

10章 サーティワンフレーバー(31のフレーバー)

特定の値しか入れたくない列があるとする。例えば[大宮 浦和 与野 岩槻]のどれかしか入力できないようにしたい、みたいな。
この値を「列を定義するタイミング」で指定してしまうのがアンチパターン
サーティワンフレーバーというのは、バスキン・ロビンス3が当初は「月によって変化する31のフレーバーの中から好きな味を選べる」していたが、現在では31を超えて数多くのフレーバーを同時に取り扱うようになっていることから。

新たに選択肢を追加する場合は列の修正が必要となる。
配列やハッシュ等を使わずにインスタンス変数を新たに宣言するイメージ。
修正や削除のときも同様。

解決策は外部キー制約を付けて選択肢をデータ管理すること。

create table さいたま市構成市名(
  旧市名 varchar(20) primary key
);

insert into さいたま市構成市名 values('大宮');
insert into さいたま市構成市名 values('浦和');
insert into さいたま市構成市名 values('与野');
insert into さいたま市構成市名 values('岩槻');

create table 住所(
  (何か他の列),
  旧市名 varchar(20),
  foreign key(旧市名) references さいたま市構成市名(旧市名) on update cascade
);

こんな感じ。テーブル名とか列名はわかりやすさ重視のため日本語とした。
これなら「やっぱり与野は外そう」と考えてもすぐ外せる^4し、「上尾と蕨も合併しよう」と考えてもすぐに追加できる。

11章 ファントムファイル(幻のファイル)

まず名前がかっこいい物理ファイル自体をレコードとして追加するのか、物理ファイルのパスをレコードとして追加するのか問題。

たびたび議論の対象となるが、この本の筆者の立場は反対。
理由を挙げる。

  • レコード削除時に外部ファイルが残ってしまう
  • 物理ファイルを削除や変更した場合、トランザクション管理できずに変更が他ユーザーに反映される
  • (ファイルも同時に削除するとして)削除処理をロールバックする際に物理ファイルはロルバ対象外のため削除されたままとなってしまう
  • RDBMSのバックアップにDB外の物理ファイルを含めることが困難
  • 外部ファイルに対するアクセス権限はDB内の権限とは別物
  • ファイルはデータ型ではない(パスの場合DB内では文字列型となる)

なるほど。こう見ると結構ある。
逆に言えばこれらへの対応がしっかり練られていればそれはそれで問題ない。

解決策はblob型を採用すること。
最近のDBは容量に余裕があるのである程度のファイルサイズなら許容できるでしょ、という考え方。

ファイルの読み込みをサポートしているRDBMSも多いため、不自由には感じないはず。

12章 インデックスショットガン(闇雲インデックス)

禁書目録さんがショットガン片手に近づいてくる光景が脳裏をよぎったが絶対違う。

DBのパフォーマンスを向上させるために有効な手段の一つとしてインデックスを効果的に使用することが挙げられるが、その効果的な使用法について。
名簿であれば「あ行」「か行」みたいな。

ソート済みでなければインデックスは機能しないため、データ追加や更新のたびにソート処理が走る。そのため定義するとオーバーヘッドは生じる。
そのオーバーヘッドを上回る効用があるよねと。

だからといってたくさん貼ればいいわけではない。これが今回のアンチパターン
例えば以下はバッドプラクティス。

  • IDにインデックス
    ほとんどのRDBMSは主キーのインデックスを自動的に作成するため冗長。
  • 説明文等の長い文章にインデックス
    これを対象にselectする機会があまりなく、オーバーヘッドの方が大きくなりがち。
  • 所要時間などの付帯要素
    こちらも検索条件になりにくい4
  • 複合インデックス
    利点はあるが、定義した順番どおりに使用しなければ意味がなかったり、使用頻度が低くなりがちだったりと欠点も多い。

解決策は適切なインデックスを作成すること。わかりやすい!!
…で、どうすればいいのよと。そりゃそうなる。

適切なインデックス作成のためのチェックリストとして「MENTOR」が挙げられている。

  • Measure(測定)
    ボトルネックを調べる。
  • Explain(解析)
    クエリ実行計画(後述)のレポートを取得して解析する。
  • Nominate(指名)
    解析結果から原因を特定する。
  • Test(テスト)
    修正が適当か確認する。
  • Optimize(最適化)
    キャッシュに割り当てるメモリ量を調整し、頻度の高いインデックスをキャッシュにロードする。
  • Rebuild(再構築)
    定期的にメンテナンスする。

の頭文字をとったもの。

クエリ実行計画って?

Query Execution Planの頭文字をとってQEPとも。
クエリ実行にどのインデックスを使うかを判断する計画のこと。

RDBMSによって多少差はあるが、概ね

  • クエリに関連するテーブル
  • インデックスの選択理由
  • テーブルへのアクセス順序

が含まれる。
RDBMSごとに何が吐かれるかを調べるが吉。

2部は以上。インデックス張ってないDBは見たことがある…。

3部 クエリのアンチパターン

13章 フィア・オブ・ジ・アンノウン(恐怖のunknown)

名前がかっこいい。

住所の「マンション名・部屋番号」のように入力必須ではないデータを含む結果を得ようとする際、null値を一般値のように扱うことがアンチパターン。またはその逆。
null値を使うことがアンチパターンというわけではない。扱い方が問題。

SQLではnullを特殊な値として扱う。0でもfalseでも空文字でもない。
上記の例で住所を結合して出力したい際、「番地」と「マンション名」を結合しようとしたときに「マンション名」がnullの場合出力はnullとなってしまう。
いやー困っちゃうね。
だからといって自然数が入ることが想定されている列に-1を入れるようにすると集計処理の際-1を除く処理をしなければならなくなる。うおお面倒…。

null == nullの結果がnullになる理由は「年齢不詳の人と年齢不詳の人が同い年かどうかはわからないでしょ」である。わかりやすい。

解決策はnullを考慮した熟語を用いること。

  • is null
  • is not null
  • is distinct from

is distinct fromはサポートしていないことがあるため注意が必要。
あとはnot null制約をつけることだろうか。無論全てにつくわけではないが。

14章 アンビギュアスグループ(曖昧なグループ)

group byを利用してグループ内の最大値や最小値といった値を取得し、その行の他のデータを取得したい際に、グループ化していない列を参照すること。

以前書いたSQL文で最初そんなことが起きていた。
これはサブクエリを使うのでは?と期待しつつ読み進める。

グループ化していない参照値がユニークでない場合にこういったことがある。
SQLの仕様理解が大事。相手の気持ちを汲み取っていこう。

関数従属性がある列になら使っても良い。この言葉が出てくるのは第二正規系の時だっけ…?
助けて過去の私。

vviilloovv.hatenablog.com

しかも第三だった…。ちなみに第二は複合キーを分ける。

解決策はいくつかある。

  • 関数従属のある列のみにクエリを実行する
  • 相関サブクエリを使用する
  • 導出テーブルを使用する
  • 外部結合を行う
  • 他の列もグループ化する
  • グループごとにすべての値を連結する

いっぱいある。それだけ多くの人がこのアンチパターンにお世話になったということだろう。かく言う私もその1人であることだし。

15章 ランダムセレクション

ランダムな値が必要とされる際、ランダムにソートした最初の行を取得する。

少量のデータをランダムに取得するだけなのに全体をランダムソートする、というだけで大変なことをしていることがわかる。

データが少ないなら問題ない。それが増えないならなおのこと。

解決策はいくつかある。

  • 1から最大値までのランダムなキー値で抽出
    主キーが1から始まり、欠番がないことが条件。論削する方針のDBならいけそう。
  • ランダムなキー値で抽出 + 欠番の場合その次の値で抽出
    物削する方針のDBならこちら。
  • 主キーのリストをアプリ側で取得し、その中からランダムに1つ抽出
    リストが大きくなりすぎないのであれば有用。2回DBとやりとりするので若干冗長かも。
  • オフセットを用いてランダムに行を選択する
    ヒットした件数のうち何番目をとってくるか、をアプリ側でランダムに指定してとってくる。良さそうだがlimitをサポートしていないことがあるので注意。
  • ベンダー依存の解決策
    RDBMSにサンプル機能があるならそれ使えばいいよね。

乱数周りはパフォーマンスに影響が出やすい。よく調べて実装するのが吉。

16章 プアマンズ・サーチエンジン(貧者のサーチエンジン)

貧者と見るとカルナさんが思い出される。インド異聞帯最高だったよ。

全文検索を行う際、パターンマッチ述語を使用する。
正規表現は便利だが、データ全体に対して行うとデータ量に比例してパフォーマンスが低下してしまう。

単純なデータに正規表現を用いる分には問題ない。積極的に利用していこう。

最善の解決策はSQLを使わずに全文検索エンジンを使うこと。
次善の解決策は検索結果を保存して使いまわすこと。

これに関してはRDBMSを頼るのが良い。全文検索することは多いだろうからだいたいあるはず。
全文検索が必要となったときに調べる、ということが必要ということを覚えておくのが大事。

SQLが苦手なことはSQL以外にやってもらう。餅は餅屋精神。

17章 スパゲッティクエリ

SQL世界のスパゲッティプログラム。まぁどこの世界にもあるよね。あってほしいわけではないが。

合言葉は「全部1回で解決しようとするな」で。
とりあえず結合、としてしまうと場合によってはクロス結合することがある。
データ量が多いテーブル同士のクロス結合は地獄。

アンチパターンを用いても良い場合、という箇所で「上司を説得して複数に分けろ」と書いてあったのが面白い。要は使うなと。

解決策は処理を分けること。単一責任。

18章 インプリシットカラム(暗黙の列)

「暗黙の」ってついている時点でいやな予感しかしない。

ワイルドカード指定や暗黙の列指定(insert intoの列指定をしない等)を行うこと。

例えばinsert文の列指定を省略した場合、列追加をするとinsert文がエラーになる。列の数と値の数が合わないからである。列を削除した場合も同様。

怠惰と保守性とのせめぎあい。書き捨てではない場合さぼらず書こう。

解決策は列名を明示的に指定すること。
サンプル以外でSQLを発行する場合はワイルドカードを使用しない、と制約を付けてもいいかもしれない。

クエリのアンチパターンは以上。 スパゲッティクエリだけは絶対にダメ。

4部 アプリケーション開発のアンチパターン

19章 リーダブルパスワード(読み取り可能パスワード)

パスワードを平文で格納する。
絶対ダメ。攻撃された場合はもちろん、悪意のある内部の人間が絶対にいないとは言い切れない。

また、認証時にも平文同士で比較することになり、たとえユーザーが間違ったパスワードを入力していたとしても非常に危険となる。他の認証に使いまわすことがあるからである。

解決策はパスワードのハッシュにソルトを加えて格納すること。
パスワード文字列を不可逆な暗号化文字列に変換して比較するようにすれば良い。

不可逆な変換の簡単なものは剰余の計算である。
(適当な数値) % 3 = 2であるとき、この適当な数値が何かわからない。5でもいいし8でもいい。「結果から初期値がわからない」のが不可逆な変換。

ソルトは適当な文字列のこと。ユーザーのパスワードに適当な文字列を付与してからハッシュ化することで元の文字列がさらに判別しにくくなると言う寸法。

この辺の技術に関しては「体系的に学ぶ安全なWebアプリケーションの作り方」に詳しく載っているらしい。この前買ったから今度読む。

また、ハッシュ化する処理をSQLで行う場合、平文で扱う瞬間が存在してしまうため、アプリ側でハッシュ化する処理を実装したい。
また、パスワードがわからなくなった場合はリカバリーするのではなくリセットするのが絶対。

詳細は上記の本を読むべし。

20章 SQLインジェクション

情報処理試験でもよく見るあれ。ユーザーにSQL文を発行されて実行しちゃうやつ。

select *
from Bugs
where bug_ig = (入力された値)

という動的SQLを発行する際、入力値が1234; delete from Bugsだった場合に

select *
from Bugs
where bug_id = 1234;
delete from Bugs

というSQLが発行され、Bugsテーブルの中が消し飛ぶ。

解決策はいくつかあり、どれか1つで全ての攻撃が防げるわけではない。

  • 値のエスケープ
    文字列型の列には有効。
  • プリペアドステートメント
    絞り込むパラメータが単一の際は非常に有効
    列名や識別子等も入力されたパラメータは文字列として扱うため、select以外で使いたい場合想定した結果が得られない。
  • ストアドプロシージャ
    安全とは言い切れない。
  • データアクセスフレームワーク
    安全とは言い切れない。

状況に応じて適切に使い分けることが重要。
誰も信用してはならない。
「誰も」には自分自身も含まれている。
充分にテストされた関数のみを使い、自分で実装しようとは思わないことを意識するべきである。

21章 シュードキー・ニートフリーク(疑似キー潔癖症)

連番で振っているIDに欠番が出た際に欠番を別のデータで埋めちゃうやつ。

キモチワルイ気分になるのはわかる。わかるがやらない方がいい。

欠番に新しいデータを入れていく場合でも欠番を詰める場合でも、同時に2つの命令があった場合処理ができなくなる。
また、振りなおす前の主キーをどこかが実数値で持っていた場合、そことの整合性がおかしくなる。

解決策は欠番を埋めないこと。そりゃそうだ。

重要なことは上司を説得すること。コミュニケーションだ。

22章 シー・ノー・エビル(臭いものに蓋)

デバッグする際、自分のコードは正しいと思い込んで戻り値やエラーメッセージを無視すること。
「よくわからないけど動かないからバグ」みたいな。

qiita.com

エラーメッセージをよく読もう。
あとは使う前にドキュメントをよく読もう。

解決策は例外と戻り値をしっかりチェックすること。
それとステップ実行。どこからおかしくなるかを特定できれば対応も素早くできる。

23章 ディプロマティック・イミュニティ(外交特権)

アプリのソースコードにおいてはベストプラクティス5を採用することを良しとするが、SQLを特別視してSQLにはベストプラクティスを採用しない。

「DBはアプリとは別」という考え方があることに起因している。
アプリ設計者とDB設計者が別だったり、DB用のIDEが広く普及していなかったり、アプリとインフラの部門が分かれていたり。原因は色々挙げられる。
かく言う私の会社も「アプリ」と「ITインフラ」で部が分かれている。なるほどこれかぁ。

書き捨てならこの限りではない。このパターンいくつかあるな。

解決策はDBも品質管理の対象に入れること。

  • 文書化
  • バージョン管理
  • テスティング
  • ブランチ管理

SQL以外では常識とも言える。それをSQLに適応するだけ。

24章 マジックビーンズ(魔法の豆)

MVCの全てのモデルクラスがアクティブレコードを継承するべき、という考え方。
便利な機能を全員が使えるようにすることでジャックの魔法の豆のようにいい感じにプロジェクトが育つなんて気のせいだよ、みたいな。

どこからでもDBを触れると逆にどこに任せればいいかがわからなくなっちゃうよ…ということなのかな。ちょっと私の理解が足りていない。

解決策はモデルにアクティブレコードの管理を任せること。

ドメインモデルの責務とすることでDBの取り扱いを単一責任にするのが大事。
シンプルに取り扱う方法を追求していくといいんじゃないかな、といったところ。

25章 砂の城

想定不足。砂上の楼閣。「これが起きたら大変なことになる」に対する対応策がない状態。

対応策は知識を付けてちゃんと検討すること。
例外処理やバックアップ、ベンチマークテストの実施等を徹底しようね。

おわりに

読んでいると割と「あーあったあった」ってなってしまった。

この本を読むことによって「ジェイウォークにあたるからこの設計はやめましょう」のような指摘をしっかりとした根拠の上でできるようになった、ということが一番大きな収穫といえる。
デザインパターン同様、共通認識として会話するためには最高の良書。

逆に一番問題なのは私の周囲の人がこの本を読んでいないこと
会社が買ってくれればいいのだけれど。まぁ無理よね。次は何を読もうかね。

ではまた。


  1. ちゃんとした理由としては更新処理の際にnullチェックをかませなければならないため処理が煩雑になる、というものがある。

  2. 非常に大きな値と非常に小さな値との計算、桁数が多い場合などはDecimalでも無理。

  3. いわゆるサーティワンアイスクリーム。「B」の右側と「R」の左側色を変えて31に見せるロゴは誰でも見たことあるはず。

  4. ただの例であってバッドイメージを持っているわけではないことを明記しておく。

  5. バージョン管理やテストの自動化、ドキュメントの整備等。

一夜明けて

うぃろぅです。
何かを書かずにはいられない気分なので書きます。
推敲はせず、起承転結も意識しません。

昨日はあえて情報を入れないようにしていた。

人の命は平等、それはそうかもしれない。だが、人が生み出すものの価値は平等ではないのだ。
私や私の友人が共有できる、その未来の可能性の一部が焼却されてしまった。

アニメではなくドラマでもいい。演劇でもいい。もちろん映画でも、音楽でもいい。
誰かが生み出したものを私は享受し、そこから何かを得ている。
人が情熱をかけて生み出したものは、私の何かを変えるのだ。

高校の時は、「昨日のドラマ観た?」と同じノリで「昨日のけいおん観た?」という会話をしていた。
けいおんOP曲の歌詞がなんと言っているかを議論して、歌いながら外周走をした。
体育祭の余興でハレ晴レダンスを踊り、放課後にらき☆すたを観ながら工芸の提出物を作った。
物理のレポートを書きながらCLANNADの話をして、AFTER STORYの終盤では放送の次の日に体調不良で休んだ友人を笑った。休んだ友人は真っ赤な目をしていた。
浪人中、友人にシュタゲを薦められるまま観て、その息抜きに日常を観た。予備校では焼きサバと囲碁サッカー部の話で盛り上がった。
氷菓の原作を買い、そこから派生してインシテミルも読んだ。ChouChoさんは誕生日が同じ日で、勝手に親近感を覚えている。

こんなことばかり思い出していた。京アニの作品は私の人生に寄り添っていた。
私の人生に影響を与える作品を生み出した人たちが失われてしまったのだ。
今はただ、虚無感を抱えている。

私の通っていた高校は関東にあり、修学旅行先は海外や沖縄ではなく京都である。
「夕食の時間までに帰ってくればUSJ以外ならどこに行ってもいい」というルールのもと、各班は自由に行動計画を立てられた。
大阪に食い倒れに行った班があった。新幹線で広島に遊びに行った班があった。明石海峡大橋を渡って四国に行った班があった。

私の班は京都アニメーションの第一スタジオにアポを取り、職場見学をさせてもらった。

礼儀作法もなっていなかったであろう高校生の私たちに職員のかたは丁寧に対応してくださり、「他言無用」としてペン入れの様子を見せてくださった。「涼宮ハルヒの消失」だった。
応接室で質問を受けてくださり、ここでは書けないような内容から些細な内容まで、社内機密に当たらない質問には全て答えてくださった。

「どこかの会社の知らない人が亡くなった」では、ないのだ。
あの時に会話してくれた人たちが、昨日のあの場所に、いたかもしれないのだ。
悲しいや悔しいではなく、ただただ何もない。

この前のセールでPS4GOD OF WARを購入し、エンディングまで進めた。
主人公のクレイトスが息子のアトレウスとともに亡くなった妻を弔うための旅をする、という物語だ。
クレイトスは不器用な父親で、アトレウスに「母上が死んで悲しんでいるように見えない」と言われてしまう。
クレイトスはアキレウスに「生意気な口をきくな」と怒鳴り、叱咤するのだ。

「大声で嘆くだけが弔いだと思うな」
「私には私の弔い方がある」

今回の件に関してブログに書くのはこれが最初で最後とする。

ヴァイオレット・エヴァーガーデンの円盤を買おう。放送から10周年を迎えたけいおんへの愛を大いに語ろう。こなたのことを思い出しながらカラオケで木枯らしに抱かれてを歌おう。
元バイト先の仲間たちに「I'm proud歌って」と言われることがある。「メガネから声が出ている」と言われる。らき☆すたのEDで知ったこの曲をこれからも歌おう。
京アニの作品とともに、これまでとこれからを楽しく過ごそう。

私の弔い方をしよう。

ではまた。

【備忘録】 「オブジェクト指向設計実践ガイド」を読む

うぃろぅです。

今日は読書の方。学びを書いていきます。

今日の本

オブジェクト指向設計実践ガイド

Java版が有名だが、そのRuby版が出版されている。
Rubyの環境は会社のPCにも入っている(こそっと入れた、ただしirbが安定して動かない)ためお試しもできる。

第1章 オブジェクト指向設計

オブジェクト指向設計の理論。

少し読めば気がつくが、本文に意訳が少なく、英語で書かれた説明文っぽさが前面に押し出されている。
恐らく意識して訳者の意思を排しているのだと思われる。

この章は図説もソースコードもないため、大判の本に文字がこれでもかと並んでいて少し圧倒される。我輩が猫であるを思い出したりなどしていた。

第2章 単一責任のクラスを設計する

将来何が起こるかわからないため、とりあえず機能を分離する。
決定を遅らせることが重要で、そのために1つの処理で1つの機能、としておくと対応しやすくなる。

この本では自転車の例を挙げて説明している。オブジェクト指向界隈、自転車だったり車だったり乗り物と縁が深め。多分例に挙げやすいだけだと思うが。

それと52ページに誤植がある。

def diameter(wheel)
  wheel.rim + (wheel.tire * 2))
end

閉じ括弧が1つ多い。恐らく53ページ目のソースをコピペして消し忘れかなと。

第3章 依存関係を管理する

どうでもいいけれど「いそん」って読む人少ないよね。辞書的にはどちらでもいいしNHKも「イゾン」と読むように変更したとのこと。でも「いそん」の方がなんとなく響きが綺麗。それだけ。

GearクラスがWheelクラスの存在を知らなければならないソースになっているため、その依存関係を修正する。いわゆる疎結合

これはテストコードに関してもいえることで、コードを修正するたびにテストコードも修正しなければならないようなテスト設計はNGとなる。
言われてみればそのとおりだが、これまであまり意識してこなかった。

知識を減らすことで、より賢くなったのです。

「依存オブジェクトの注入」についての説明箇所だが、引用箇所の言葉回し好き。めっちゃお洒落。

依存関係に関しては

  • 変更の起こりやすさ
    自分のコードより開発の活発なフレームワークの方が変わりやすい等。
  • 具体クラス vs 抽象クラス
  • 依存関係が多いクラス

の大きく3点に気をつける必要がある。
上手いこと自分より変更が少ないクラスを見つけ、そこに依存しよう。

第4章 柔軟なインターフェースをつくる

クラス内をパブリックとプライベートに分ける重要性について。

さらには設計のポイントについて。
クラスを中心に考えるのではなく、メッセージを中心に考えられるようになることが設計者としてのキャリアの第一歩。

オブジェクト指向設計の要は手放しの信頼である。
「詳細はわからないけれど呼べばよしなにやってくれるはず」を繋げることで機能が成り立っていくことが拡張性のあるプログラムだという話。確かに単一責任であるということは他の機能に対して責任を負う必要がないということで非常に理にかなっている。

パブリックインターフェースに含まれるメソッドの条件は以下のとおり。

  • 明示的に特定できる
  • 「どのように」ではなく「何を」になっている
  • 名前は恒久的に変わらないものである
  • オプション引数としてハッシュをとる

「明確に」を合言葉に。複雑さはどこでも求められていない。

ここまでの説明を下地に「デメテルの法則」について検討。

ja.wikipedia.org

Wikiの説明でもがわかりやすかったため貼ってしまうが、要は「友達の友達にものを頼むな」ということである。

第5章 ダックタイピングでコストを削減する

「ダックのように鳴き、ダックのように歩くのであれば、それはダックである」というやつ。

共通して行っている機能を抽象化して切り出してあげることで、「何やっているかわからないけれどこのメソッドは実装しているはず」と信頼して処理を投げられる。
抽象メソッドみたいなものかな、という認識。ちょっと違うとは思うが。

じゃあ何がダックで置き換えられるのよ、ということで以下が置き換えられる例。

  • クラスで分岐するcase文
  • kind_of?is_a?
  • respond_to?

詳しい説明は書籍参照のこと。

また、ダックタイピングに関連して静的型付け言語対動的型付け言語の比較。
私はJavaC#を少し触ったことがあるが、Rubyの方がコーディングしていて楽しいから動的型付けでも全然問題ないと思っている。速度に対してシビアな状況に直面していないからかもしれないが。

第6章 継承によって振る舞いを獲得する

これまではコードの最小化がテーマ、ここからはコードの共有がテーマ。

継承を利用する際には「is-a関係」を意識してあげると上手くいくことが多い。もちろん全てにおいて有効というわけではないが、判断基準のひとつとしては有効なものである。

Javaではabstractキーワードをつけることでスーパークラスインスタンス生成を抑止できるが、Rubyにそのような制限をつけるキーワードはない。プログラマを信頼しているからね。

継承を実装するときは実際に継承が有効になってからで充分。YAGNIでいこう。

リファクタリングのポイントとして、既存のクラスをスーパークラスとサブクラスに分けたら、まずはサブクラスに全てのコードを移動してしまうということが挙げられる。
別のサブクラスを宣言したとして、エラーメッセージを見て「何を共有するか」を判断しやすいからである。なるほど。結果的にスーパークラスに余計な処理を入れずにすむと。

通化したからといってそれでいい、というものではなく。
superの呼び忘れは誰しも起こりえるのでそれに対する対策も取り込むことでより堅牢になる。そのための手段がフックメッセージであると。
これは知らなかった。積極的に取り入れていきたい。

第7章 モジュールでロールの振る舞いを共有する

継承可能なコードの書き方に続いて継承の使いどころさんを解説。

モジュールをmix inする関係の詳しい解説はチェリー本を読むといいと思う。

いつもお世話になっております。

継承との使い分けとして「である(is-a)」か「のように振る舞う(behaves-like-a)」かが1つの基準。
機能を共有することは継承と似ているので、フックメソッドを実装するのが吉。

第8章 コンポジションでオブジェクトを組み合わせる

オブジェクト指向コンポジションのテクニックについての解説。

パーツを組み合わせて全体を組み合わせることをコンポジションと呼んでいる。

クラスを構成する要素をパーツごとに分解して…となると知識量が多くなるため、ファクトリーパターンを活用する。

gihyo.jp

あとソースコードに誤植あり。ミスは誰にでもある。

コンポジションか継承かで迷った場合、明確なメリットがないのであればコンポジションを優先するべき。掘り下げるなら継承、パーツを集めるならコンポジション、といったところか。

第9章 費用対効果の高いテストを設計する

上手なリファクタリングのためには価値の高いテストが必須。
テストを書き直すことなくリファクタリングを進めていけるテストコードについての解説。

テストの利点として挙げられているのは以下。

  • バグ発見
  • 仕様書になる
  • 決定を遅らせる
  • 抽象を支える
  • 設計の欠点の明確化

詳細は書籍参照。

使われていないコードでも存在するのであればテストをしなければならないため、コードを削除することも大事。

受信メッセージをテストするためのテストダブルがスタブ、送信メッセージをテストするためのテストダブルがモックという認識。

抽象スーパークラスの機能をテストするために、テスト用のサブクラスを作ることも有効。

とりあえず手は動かしたが完全に理解できたかというと完全ではない。
引き続き手を動かしていきたいところ。

おわりに

オブジェクト指向わかってきたわ」と思ってこの本を読んだわけだが、わかった気になっていただけだったということが身にしみてわかった。

オブジェクト指向マスターへの道は遠く険しい。
まだまだ精進ですなぁ。

今回の手を動かした証跡は以下。

github.com

いい本だった。きっとまた読み直す日が来る。

ではまた。

○○Pay(主に7Pay)をとりまく四方山話

うぃろぅです。

電子マネー、便利ですよね。

f:id:vviilloovv:20190704125812p:plain

PayPayしかり、LINE Payしかり。

電子マネースマホ払いという点でのメリットとして

  • 財布を持たずに買い物ができる
    スマホを持ち歩かない人の方が財布を持ち歩かない人より現代日本であれば少ないはず。
  • お金を触らずに済むので衛生的
    スマホの画面が衛生的か、と訊かれるとそうではないですが、他人が触れやすいか否か、という点では差別化できそう。
  • お釣りが出ない
    小銭の管理、地味に重いし計算面倒だしで煩わしい。
  • 待ち時間が減る
    貴重なお昼休みをコンビニの行列に費やしたくはない。

といったことが挙げられると思います。
一方で

  • 対応していない店舗では使えない
    対応状況を調べるのも面倒なことがある。
  • 支払い方法を発声する必要がある
    声の出せない事情のある方はもちろん、人見知りをこじらせている方もあまり発声したくないはず。もしくは聞き取ってもらいにくいという要素も。(「iD」と「Edy」とか)
  • 見えないところで不正利用されるリスクがある
    現金は「窃盗」という目に見える形の不安はありますが、充分注意していればそこまで遭遇する機会は多くないはず。

といったデメリットも挙げることができます。

さて、今日の本題。
7月の頭からはセブン-イレブンファミリーマートといったコンビニで独自サービスの電子マネー決済が始まりました。
なるほど時代はどんどんキャッシュレスに向けて動いているんだな、と感じていたのも束の間。
このエントリーを書いている2019/7/4現在、セブン-イレブンで使える「7pay」が不正利用の報告多数で大変なことになっています。

で、今ホットなこの話題について、面白そうな話題をいくつか(いくつも?)見つけたので思ったことを書いていきます。
モバイル決済の環境から7payのことまで雑多に書いていくので話題につながりがなさそうな気配がします。あしからず。

以下目次。

世はモバイル決済時代

大手各社は独自モバイル決済の新規顧客獲得のため、しのぎを削っていた…。
ということでまずはモバイル決済を取り巻く環境から。

モバイル決済乱立のワケ

いくつか理由があると思われます。すぐに思いつくだけでも大きく分けて2つ。

消費税増税対策

2019年10月には消費税率が10%に引き上げられます。
計算が楽になるのは確かに(脳のワーキングメモリ的には)楽だけれど、少し前まで5%だった記憶が残っている身としては2倍は結構大きいです。

政府はキャッシュレス決済を推奨しており、クレジットカードやモバイル決済等のキャッシュレス決済であればポイント還元により実質8%程度になるよう軽減税率の整備を進めているとか。

ユーザーとしては「ポイント還元」というワンクッション置く形であれ、多少の導入 / 学習コストで税率が安くなるのであればキャッシュレス決済を試してみるかという気持ちになるというもの。

外国人旅行者の増大

外国からの旅行者、昨今だと中国人ですね。こちらが年々増大していっています。
詳細な資料は観光庁を参照してもらうとして、2020年にはオリンピックも控えているため今後も訪日旅行者数が増大することが予想できるわけです。

そうなったときに問題となってくるのは買い物時の決済方法。
よく話題になるのが五円玉。

f:id:vviilloovv:20190704142314p:plain

漢数字しか使われていないため、何円なのかわからないケースがあるんですよね。

そうなってきたときに便利なのがモバイル決済。
モバイル決済先進国といっても過言ではない中国においては、「微信支付(WeChat Pay)」か「支付宝(Alipay)」があればほぼ何でも支払いができます。

AlipayかWeChat Payで日本でも買い物できるよ」と宣伝すれば、使えないお店と比較して確実に中国人の購入機会が増えます。

日本における中国モバイル決済対応状況

www.nikkei.com

techwave.jp

上記の事情を受け、PayPayとLINE Payはそれぞれ中国の2大モバイル決済との連携対応を発表。海外旅行者の日本におけるモバイル決済対応は進行しつつあります。

日本におけるモバイル決済はこの2種類のどちらかでいいじゃない、話はここでおしまい!!
…とはならないのが日本という国なわけで。

2019年7月時点のモバイル決済いろいろ

news.cardmics.com

こちらを読めば概ねわかりますが、とにかく数が多い
クレジットカード等を用いた入金機能対応のICカード決済もモバイル決済として含めてしまうのであれば、支払いの手段は数十種類にも及びます。

クレカ決済じゃだめなの??

ダメというわけではもちろんありませんが、クレジットカードはモバイル決済と比較して決済手数料が高く(クレカは8~10%程度、モバイル決済は3~5%程度)、「クレカ決済は費用がかかるから」と現金会計に絞っている個人事業主も少なくありません。

モバイル決済は日本においては現在発展途中と言うこともあり、「決済手数料無料キャンペーン」なるものを実施していることもあります。
それであれば、とモバイル決済を導入するケースが増えつつあるようです。

コンビニ別対応状況

現代の若者がよく使うお店と言えばコンビ二です。
その時々のニーズに合わせて事業展開を行う必要があるコンビ二でもモバイル決済は導入されています。

japanese.engadget.com

上記サイトから引用させていただくのですが、こちらをご覧いただきたい。

f:id:vviilloovv:20190704132542j:plain

下4段を見ましたね?
もはや何がなんだかですよ。斜めにならんでいて綺麗。いやそうではなく。 これなら「PayPay」か「LINE Pay」でいいやってなりますよね。

なぜ独自モバイル決済を導入するのかの考察

そもそも「PayPay」か「LINE Pay」だけ使っていれば問題なさそうなのになぜ各社独自のモバイル決済を開発するのでしょう?
私は経営層にはおらず、ざっと調べてもあまり有益な情報が得られなかったのですが、考えられる理由としていくつか挙げられます。

リピート客を増やすため?

「7Payだと今ポイントお得だからすぐ近くにファミマあるけど2分歩いてセブン行くか」というアクションを起こさせることができるとは考えられます。あれもこれも、使い分けるのはユーザーにとって負担が大きいですし。

手数料削減のため?

クレジットカードもそうなのですが、電子決済には手数料がつきものです。

paypay.ne.jp

PayPayは決済手数料0円を謳っていますが、保証されているのは3年間。3年経過後はどうなるかわかりません。

そうした場合に、自社開発の決済システムを使ってもらえれば損をすることがなくなると、そういう発想になるのも理解はできます。
コンビ二としては「フランチャイズ展開の看板使用料」と「モバイル決済手数料」が2重に課せられるようにもなる…のかもしれません。

キャンペーン等の表示、販売促進のため?

独自のモバイル決済を利用してもらうということは、独自のキャンペーンをスマホに表示させやすいということになります。

QRコードを読み取るにしろ、表示するにしろ、ユーザーはスマホの画面を見ることになるわけです。
その画面に(決済後にでも)「今これを買うと抽選で賞品が当たるよ!」だとか「この飲み物が安くなってるよ!」だとかの表示をすればユーザーはそこに意識が向きます。
そうした宣伝により、「そういえば今あれが安いって広告あったな」と購入機会が増大するのです。

7Payをとりまく環境

ここまでがなんとなくの前提知識です。長い。

流れをまとめてみましょう。

  1. 海外旅行者が増えた
  2. モバイル決済の需要が高まった
  3. Apple Payが日本でもできるようになった
    ちなみにこれよりも前(2年程度前)からLINE Payは利用可能でした。
  4. PayPayが「100億円キャンペーン」で有名になった
  5. LINE Payも対抗してキャッシュバックキャンペーンを実施した
  6. ソフトバンク(PayPay)に対抗して、ドコモau電子マネー決済を宣伝し始めた
  7. 増税に対する軽減税率適応対象としてキャッシュレス決済が推奨された
  8. 共通ポイントの先駆けとなったTポイントが衰退し、各社独自のポイントサービスを始めるようになった
    セブン-イレブンは「nanaco」を使って新規顧客獲得のためにいろいろなキャンペーンを打ち出している。Omni7と連携して適切なサジェストをし、さらに購入機会を増大させようという試み。
  9. Tポイント離れの煽りを受けファミリーマートも独自ポイントサービスを画策した
  10. ファミリーマート、「ファミペイ」を7月から開始することを発表
  11. セブン-イレブンも(ファミリーマートに対抗して?)「7Pay」を7月から開始することを発表

こんな感じかなと。

Tポイント云々の話はここでは初出ですが、少し前に話題になっていました。

www.mag2.com

ここで問題になってくるのが「後発なのに納期が短い」ということかなと。
再利用可能なコードがあるならまだしも、これまで手をつけていない分野の開発でこれは…厳しいですね。 Omni7の時も「プレスリリースしているから納期はずらせない」を体現したかのような詰め込み作業感がリリース時に話題になっていました。
まさか経験から学習していないのか…??

今回の事象

よくある(よくあっていいことではない)不正利用ですね。
7Payに紐付けられたクレジットカードから不正に入金され、7Pay利用可能サービスを通して不正に購入された。
PayPayのときにもこういった報告ありませんでしたか…?
まさか経験からだけではなく歴史からも学習していないのか…??

piyolog.hatenadiary.jp

セブン-イレブンの発表内容や被害状況についてはこちらに詳しくまとめられています。

直接原因

何が起こっているかというと

  • 7PayはOmni7のサインインに利用可能な「7iD」でサインインを行う
  • 「7iD」のパスワード再設定サイトはOmni7側に配置されている
    パスワード再設定サイトはこちら
  • 今は表示されなくなってるがパスワード再設定サイトの入力項目に「送付先メールアドレス」という項目が存在した
  • 「送付先メールアドレス」欄に任意のメールアドレスを入力すると
    • 「7iD」登録時のメールアドレス
    • 「送付先メールアドレス」
      両方にパスワード変更用メールが送付される(!)ようになっていた
  • iPhoneから登録した場合生年月日を未入力にすることが可能で、その場合は誕生日が「2019/1/1」に設定される(!?)
  • パスワードを任意の文字列に変更すればアカウント乗っ取りが可能

ということです。
「7iD」はメールアドレスのため、メールアドレスさえわかればアカウントが乗っ取れる状況になっていたというわけですね。えぇ…。

ちなみに今(2019/7/4 17:00ごろ)はどうなっているかというと

f:id:vviilloovv:20190704172512p:plain

項目としては存在しているものの非表示となっています。
これはダメなのでは…?
恐らく送信時に「送付先メールアドレス」のvalueが空白なのは許されていますが、keyが空白なのは許されていないのではないか…と推測できます。上記サイトをのぞけばわかりますがname="sendMailAddress"ってところですね。うーん。

すり抜け原因

セブン-イレブンは「セキュリティチェックに問題はなかった」としています。

恐らくこのセキュリティチェックは「7payへの不正な操作(決済時の情報の抜き取りが考えられますね)」に対するチェックだったのかな、と。
今回は7Payに問題があったのではなく、7Payを使用するときに紐付ける「7iD」側に問題があったように見受けられます。

なのですり抜け原因としては「Omni7側のセキュリティ対策が万全ではなかった」ということでしょうか。

根本原因

そもそもの問題としては

  • 短納期のため考慮が行き届かなかった
  • Omni7の開発が不十分だった

なのではないでしょうか。
要件定義が不十分だったのか、開発者の技術力が足りていなかったのか…多分両方でしょうね。

もうちょっと突っ込んだ話もできなくはないですがオープンにしてはいけなさそうな話なのでオフラインででも訊いてください。

これから

www.fnn.jp

一旦新規登録とチャージ機能を停止しているようですね。
信用を取り戻せるかは非常に厳しい話となりそうですがなるようにしかならないでしょう。

面白かったツイート

拡張子が.doなのはJavaフレームワークであるApache Struts 1の可能性が高いです。

https://struts.apache.org/struts1eol-announcement.htmlstruts.apache.org

Struts 1は2008年に最終リリースがあり、現在はサポートされていません。

多分開発期間があまりにも短かったからどこか別のアプリで利用した機能を再利用したのではなかろうか…と推測できます。

ちなみにStruts 2も存在していますが、脆弱性が見つかったためアップデートがなされています。2で発見されたってことは1には潜んだままなのではなかろうか。Struts1とは無関係です。

struts1とstruts2は名前は同じですが中身は全くの別物です。 もともと違う名前で開発していたものを名前だけstrutsにしたのがstruts2です。 なんの関係もないです。 なのでstruts2の問題がstruts1で起きるということはありえません。 まあサポート切れのものを使っている時点でアウトですが。

コメントにてご指摘いただきました。なんにせよ2013年にサポートが切れているフレームワークを使うな、という話ではありますね。

2019/7/31 追記

[https://twitter.com/togetter_jp/status/1156184176896274433:embed#「7Payの不正利用を受け、7iDのパスワードを全員リセットに…→再設定するとクーポンや7Payの残高が消えたとの報告が相次ぐ」https://t.co/jhJJC2SMm0 が伸びてるみたい。私も読みに行かないと! 作成者:@Ma__anal]

てんやわんや。一回サービスとめた方が信用面で良いのでは…?もう地に落ちてる?そうねぇ…。

「アプリからの登録は少しでも簡単に」という方針なのでしょうか。そこは一番サボっちゃいけない場所だと思うのだけれど…。

2019/8/1 追記

headlines.yahoo.co.jp

サービス終了が発表されましたね。
やはり短納期でこの規模の電子決済サービスというのは無理があったんでしょうね。

これで電子決済関係ではファミリーマートと差がつく形になりました。
セブン-イレブンがトップに立ち続けているコンビ二業界の売り上げに何か変化は訪れるのでしょうか。

セブン-イレブンの明日はどっちだ。私はSuicaを使います。

間違っている情報を載せている恐れもあるため、ご指摘等あったらお知らせください。

ではまた。

【セトリ】 水瀬いのり LIVE TOUR 2019 「Catch the Rainbow!」 東京公演 1日目 セットリスト

2019/6/28に日本武道館にて開催された
Inori Minase LIVE TOUR 2019
Catch the Rainbow!
東京公演 1日目のセットリストです。

1: Step Up!
2: ピュアフレーム
3: Ready Steady Go!
4: Kitty Cat Adventure
5: Wonder Caravan!
6: コイセヨオトメ
7: ココロはMerry-Go-Round
8: Future Seeker
9: 君色プロローグ
10: 水彩メモリー
11: 約束のアステリズム
12: brave climber
13: 三月と群青
14: 今を僕らしく生きてくために
15: TRUST IN ETERNITY
16: Starry Wish
17: My Graffiti
18: harmony ribbon
アンコール
19: Dreaming Girls
20: Million Futures
21: Catch the Rainbow!

f:id:vviilloovv:20190628214102j:plain

【FjordBootCamp】 「Webを支える技術」を読む

うぃろぅです。

ありがたいことにプロジェクトの隙間時間に技術書を読むことが許可されているため、フィヨルドブートキャンプの課題の一環である本を読んでいきます。

今回の本

Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)

Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)

Webの基礎を学ぶ本。詳細に内容を書いてしまことは問題があるため、理解したことをまとめていく。

第1部 Web概論

  • Webの歴史について
    歴史の授業が苦手だったことを思い出すなど。
  • URIってなんなのさ
    リソースを一意に特定する識別子のこと。
  • RESTってなんなのさ
    • クライアント / サーバ
    • ステートレスサーバ
    • キャッシュ
    • 統一インターフェース
    • 階層化システム
    • コードオンデマンド
      以上の6つを組み合わせたアーキテクチャスタイルのこと。
      それぞれについての詳細は書籍参照。
      全てを兼ね備えてなければならない、というわけではなく、これらはRESTの基本となる考え方というだけ。
      そのため、業務内容によっては細かな変更をして設計する、ということも大いにあり。

ここから、「RESTful」な考え方についての説明や設計について語られていくという引き。

歴史が説明されている箇所では細かな用語の説明がなく、順次言葉の解説がされていくスタイル。
そのため、読み始めたときは全く意味が頭に入ってこず、非常につらかった。
この「わけわかんないけれどとりあえず読み進めていく」感じ、学生のときみたいだ…。

第2部 URI

  • URIの意義
    DBの主キーみたいなもの。そりゃ大事だわ。
  • URIの例
    ユーザ情報も含められることは知らなかった。やらないだろうけれど。
  • クールなURIを設計するために
    変化しにくいURI設計のポイントが解説されている。例えばプログラム言語に依存するURIにはしない方がいい等。
    試しに自社のグループサイトを見てみたらURI.../cgi-bin/...とあって悲しい気分になったりなど。というか?_=(謎の文字列)とか?cmd=hogehogeみたいなURIが乱立していてこれは…これは…っ!!

URIはアドレス欄に表示されるものであり、寿命が長いものでもあるため、設計時に強く意識する必要がある。

設計の手法については第5部で解説する。

第3部 HTTP

  • TCP/IPとは何か
    情報試験でよく見る階層化プロトコル
    わかりやすい記事があったため貼っておく。
    www.itbook.info
  • HTTPの歴史
    本書の発売時点では公開されていなかったHTTP/2が今の時代にはある。
    qiita.com
    リクエスト / レスポンスを多重化できることが大きな違い。それとサーバ側から能動的にデータをプッシュする機能があること。
  • HTTPメッセージの内容
  • ステートレス / ステートフル
    サーバがクライアントのアプリケーション情報を保存するか否か。ステートレスの場合やり取りが冗長になるが、サーバにかかる負担が減る。しかし通信エラー時に多重リクエストが送られてしまう危険性もあるため一長一短。
  • HTTPメソッド
    たった8つしかない。あまり使わないものを省くと6つ。これなら覚えられそうじゃない??
    POSTの誤用の最たる例としてXML-RPCが挙げられているが、以前携わったプロジェクトの通信規格がXML-RPCだった。切ない気分になるなあ。
  • ステータスコードについて
    ステータスコードの1文字目を見ると概要がわかるようになっている。
  • HTTPヘッダについて
    キャッシュの扱いや認証など様々なことをヘッダでやりとりする。
    電子メール仕様から拝借したり文字コードの取り扱いがブラウザによって異なったりと魔境。都度都度調べる必要がありそう。

クライアントとサーバでデータをやり取りする際の約束事や歴史について。
多分この部は何度も読み返すことになりそうな予感。

第4部 ハイパーメディアフォーマット

  • HTML
    タグで文書の構造を表現する。出版時はHTML5が仕様策定中とのこと。時代は確実に進んでいっている。
    なので本文中の<object>タグは<video><audio>タグなどで置き換え可能。どんどん利用していこう。
    reference.hyper-text.org
    また、rel属性はHTML5において自由に拡張できなくなった。
    www.htmq.com
  • microformats
    HTMLでは表現できないリンクの細かい意味やイベント情報を表す。
    HTML5においても引き続き利用できるが、いくつか注意点はある。
    rev属性やprofileの削除など。
    http://html5doctor.com/microformats/html5doctor.com
    英語なのでざっくりとしか理解できない(私は英語が得意ではないため。勉強する意思はある)が、いくつかの注意点を意識しておけばHTML5でもmicroformatを利用できる、ということが書いてある。はず。

    So where does that leave us? Well, if you keep the above caveats in mind you can safely use microformats in HTML5.

  • Atom
    エディタの方ではない。ちなみにエディタはこちら。
    atom.io
    Atom Syndication Format、略してAtomWebサービスのWeb APIとして利用できる。 細かく指定できて便利そうなのだが、自分がまだ使っていないためいまひとつ理解しきれていない。
  • JSON
    こちらはよくみるフォーマット。簡単なデータ構造の管理ならDBよりコストがかからない。
    プログラミング言語におけるハッシュやディクショナリのような記法のため直感的に理解しやすい。
  • JSONP
    scriptタグを利用して異なるドメインと通信して情報を取得する仕組み。
    blog.ohgaki.net
    便利だからといって手放しで使えばいいというわけではない。
    簡単な公開情報のやりとりに使うなら良さそう。

第5部 Webサービスの設計

この本ではリソース設計について扱う。
恐らくこの部がこの本の肝。

  • 読み取り専用のWebサービス設計
    「リソース指向アーキテクチャ」の設計手法について。
    • アドレス可能性
      リソースの主キー。最重要。
    • 接続性
      リンクをたどれるかどうか。アドレス可能性の次に重要。
    • 統一インターフェース
      接続性の次に重要。どのメソッドを採用するかよく検討しよう。
    • ステートレス性
      そこまで重要ではない。現実的に考えてCookieによるセッション管理がほぼ必須なため。
  • 書き込み可能なWebサービス設計
    トランザクション処理(いくつかのデータの整合性を保つ処理)やPUTPOSTのどちらを使うか、同時に更新が走った場合の対処など、こちらも悩みの種が多い。
    トランザクション処理を実装する場合はトランザクション処理用のリソースを追加し、そのリソースを使ってやり取りをするのが定石。
  • リソース設計
    リソースはどのように抽出していくべきか、というお話。
    3つの対象が挙げられている。

WebサービスもWeb APIもWeb技術を使ったインターフェースのため、分けて考えないことが大前提である、ということが再三強調されていた。

付録

ステータスコード一覧やヘッダ一覧など。

ぱらぱらと眺めていくと面白い。ここも読み返すことがありそう。

まとめ

毎日触れている「Web」の基幹を支える技術の基礎がこれでもかと詰まっている。
数回読み返すことで定着するタイプの書籍のため、折を見て読み返していきたい。

ちなみに1周目は全て読み終えるまで1週間ほどかかった。
業務の合間にとはいえもう少しペースを上げられたのではないかと見直しつつ次はもう少し効率的に読めたらいいね。

ではまた。

【Ruby】 三目並べのコードを読む

うぃろぅです。

業務時間で暇なとき作業の空き時間が生じたときにCodewars遊ぶ自己研鑽に励むことがたまにあるのですが、その中で「なんやこれ…」ってなったコードがあったので読み解いていきます。

今回の課題

「Tic-Tac-Toe Checker」というKata(問題)。

www.codewars.com

Codewarsに登録済みの方は上記リンクからとべる。

内容は以下。雑に訳すので間違ってたらゴメン。

「Tic-Tac-Toeゲーム(三目並べ / ○×ゲーム)」で遊ぶとき、勝敗を判別する機能が欲しいよね。そのチェックをする機能を作ることが目標だよ。

盤面は3 * 3で、次のように配列で表すこととする。
0は空白、1×2とする。
配列:
[[0, 0, 1],
[0, 1, 2],
[2, 1, 0]]

入力に対して以下のように返したい。
-1: まだ勝負がついておらず、空白(0)が存在する
1: ×の人が勝利している
2: の人が勝利している
0: 引き分け((○×ゲームにおいては?)cat's gameと言うことがある…のかも)

例外処理は考えないものとして進めていく。

自分のコード

def judge(arr)
  arr.uniq.size == 1 && arr.first != 0 ? arr.first : false
end

def is_solved(board)
  if result = (0..2).map { |j| judge((0..2).map { |i| board[i][j] }) }.find { |r| r } ||
  (0..2).map { |j| judge((0..2).map { |i| board[j][i] }) }.find { |r| r } ||
  judge((0..2).map { |i| board[i][i] }) ||
  judge((0..2).map { |i| board[i][2 - i] })
    result
  else
    board.flatten.include?(0) ? -1 : 0
  end
end

特に何も思いつかなかったので特に難しいことはしていなくて、愚直に勝敗に関係する並びのパターンを書いて勝敗チェックをするメソッドを呼ぶ、という流れ。
勝敗がついていたらその値を返し、ついていなかったら0の有無によって出し分ける。

おしゃれなやり方が思いつかなかったのでとりあえず通して先人の知恵を借りよう、という意図がちょっとある。

見直してみると0..2を変数か定数にした方が見やすいなこれ。まぁそれは置いておくとして。

参考コード

で、答えた後に参照できるコードから自分の学びに活かそうとした結果これが最初に出てきた。

def is_solved(board)
  case board.join
  when /1..(1|.1.)..1|1.1.1..$|111(...)*$/ then 1
  when /2..(2|.2.)..2|2.2.2..$|222(...)*$/ then 2
  when /0/ then -1
  else 0
  end
end

なにこれ…さぱらん(さっぱりわからん)…。

ということでこちらを読み解いていくのが今回の目的。

joinの結果を見る

board.joinで何が得られるのか。やってみた。

$ irb
irb(main):001:0> board = [[1, 1, 2], [2, 1, 1], [2, 2, 1]]
=> [[1, 1, 2], [2, 1, 1], [2, 2, 1]]
irb(main):002:0> puts board.join
112211221
=> nil

なるほど単純に全部結合していると。Array#joinなのでstringで得られる。
多次元化していても平滑化する。便利。

ref.xaio.jp

正規表現でチェック

ということは結合した文字列に対して正規表現を使ってチェックを行っているっぽい。

どちらかが勝つパターン

今回の場合は12が勝つパターン。大別すると以下。

  • タテ
  • ヨコ
  • ナナメ

/1..(1|.1.)..1|1.1.1..$|111(...)*$/を解析する。

regular expressionsに貼り付けて挙動を確認してみるとわかりやすい。

ヨコ

ヨコが一番簡単で、/1..(1|.1.)..1|1.1.1..$|111(...)*$/111(...)*の箇所がそれ。

111が1回、任意の3文字が0回以上。正しい入力がここまで来る想定なのでこれで問題なし。

タテ

純化すると1..1..1がどこかにあれば良いということになる。

/1..(1|.1.)..1|1.1.1..$|111(...)*$/1..(1|.1.)..1|の箇所がそれということになる。

ナナメ

^1...1...1$(複数行でないため\A\zでなくても良い認識)か、^..1.1.1..$がナナメ。後者は1.1.1..$でも良い。これのせいで正規表現が若干ややこしくなっている。

タテの1..1..1とナナメの1...1...1が合わさり、1....1を共通化した結果1..(1|.1.)..1となる。
1.1.1..$は上記で書いたパターンそのまま。

ということで

解析した結果

/1..(1|.1.)..1| # タテ / 左上→右下のナナメチェック  
1.1.1..$| # 右上→左下のナナメチェック  
111(...)*$/ # ヨコチェック

ということがわかった。なんてスマート。

勝者がいないパターン

あとはもう消化試合。勝者がいないパターンかつ0があったら-1を返し、どれでもなかったら0を返却する。

今回の学び

正規表現を使うことはもちろんあったし知識として知ってはいるが、この問題に応用できなかった。
正規表現センターを研ぎ澄ませていきたいところ。

自分のコードと参考コード

github.com

main.rbが自分で考えたコード、clever.rbが参考コード。
テストコードつき。

いやーこれは楽しい。まだまだ成長できるなぁ。

ではまた。