達人に学ぶDB設計を読んでみた

達人に学ぶDB設計を読んでみたので、そのメモ


第1章

DOA(Data Oriented Approach)
データ中心アプローチ

プログラムよりも先にデータ設計から始めること。
データがあり、プログラムがあること。

昔は逆だった
POA(Process Oriented Approach)プログラムがあってデータがある。という考え方


3層スキーマ

1、外部スキーマ→ビューの世界

ユーザー側からみたデータベース。画面やデータ


2、概念スキーマ→テーブルの世界

開発者からみたデータベース。テーブル定義など
このスキーマの設計を「論理設計」という


3、内部スキーマ→ファイルの世界

DBMSから見たデータベース
論理データモデルを具体的にどのようにDBMS内に格納するかを定義する
このスキーマの設計を「物理設計」という



概念スキーマは外部スキーマと内部スキーマの緩衝剤
概念スキーマが無いと、ユーザー側のデータの見え方を変更する際、内部スキーマも変更する必要が出てくる
間に概念スキーマを挟むことによって、データの独立性を保。



必要性に疑問を抱いたら、
『それがなかったらどうなるだろうか』
を考えること。

第2章

論理設計は物理設計に先立つ。
器に合わせて料理を決めるのではなく、料理に合わせて器を決める。
(なんとなーくわかった)


システムの世界において、論理の意味合いは「物理層の制約に捉われないこと」


データベース性能問題の8割はストレージI/O


外部スキーマと論理設計


論理設計のステップ
1、エンティティの抽出
システムにどのようなデータ(=エンティティ=テーブル)が必要かを洗い出すステップ
要件定義と重なっている部分ではある。


2、エンティティの定義
テーブルにどのような列(属性)を持つか、を定義するステップ。
エンティティはデータを属性(列)という形で保持する。


3、正規化
システムで利用できるようにエンティティ(テーブル)を整えるステップ
エンティティを抽出して属性を定義しただけだとシステムで利用するのは難しいので、
システムがスムーズにデータを利用できるようにこのステップで最適化していく。


4、ER図の作成
正規化を行うとエンティティが増えてデータの関連性が見えにくくなるので、
それを図で表して見えやすくするステップ。


内部スキーマと物理設計

物理設計のステップ

1、テーブル定義
論理設計で定義された概念スキーマをもとに実際のテーブルを作っていくステップ。


2、インデックス定義
データベースのパフォーマンスチューニングのステップ


3、ハードウェアのサイジング
キャパシティとパフォーマンスの観点からサイジングをする。


キャパシティのサイジングの場合、
サービス開始時よりも終了時の方がデータ量が多いことがほとんどなので、それらを見越したキャパを用意する


パフォーマンスのサイジングの場合

・性能要件 - システムの処理時間(どれだけ速いか)
スループット - ある単位時間あたりの処理能力(どれだけ多いか)

こういった指標からパフォーマンス見ていく。


そしてシステムをゼロから構築する場合、どの処理を行うとどれくらいのハードウェアリソースを使うのかを把握したいところ、そこで

・類似しているシステムのデータを見てみる
・プロトタイプを作って性能を検証してみる

などしたい。
が昨今はスケジュールが短期化されてきているため、上記の様なことをしっかりやるのが難しい。


要するにサイジングは難しい。

だからこそ
・実施時の安全性を考慮すること
・スケーラビリティの高い構成にすること

が大事。

クラウドは上記問題を解決しつつある。
必要な時に必要な分だけ借りる、ができる。


ハードウェア、OSがクラウドになったIaas(Infrastructure as a service)
上記に加え、ミドルウェアクラウドになったPaas(Platform as a service)
さらに上記に加え、アプリケーションもクラウドになったSaas(Software as a service)

がある。


4、ストレージの冗長構成決定
ストレージはデータベースのデータを保持する媒体で、一般的にはHDDを使用する。
ストレージがダメになるとおしまいなので、それを避けるために耐障害性をもつ構成にする必要がある。


それを実現するのが「RAID」(独立したディスクの冗長配列)
複数のディスクを束ねて仮想的に1つのストレージとする技術。


何をしたいのか端的に言うと、複数のディスクに同じデータを書き込んで冗長化することで、どこか一つが壊れても、残りのディスクがあるから大丈夫!な状態を維持すること。


RAIDにはレベルがあるが基本的には上記の考え方を実現している。
RAIDの詳しい内容は省略
(どこかのタイミングでまとめたい。。。)


※冗長とは…「同じものを複数の場所に持つ」という意味


要点としては

コストに余裕があればRAID10
それが無理なら少なくともRAID5にすること
RAID0は論外(そもそもRAIDとは言えないという声も。。)


5、ファイルの物理配置決定

データベースのファイルをどのディスク(RAIDグループ)に配置するかを考えるステップ
(昨今はDBMS側で自動化されているので普段は意識しないところ)



データベースに格納されるファイルは5種類

・データファイル
データベースに格納するデータを保持するためのファイル。
システム側からはテーブルという論理単位で見えるのであって、ファイルそのものが直接見えるわけではない。



・インデックスファイル
テーブルとインデックスは異なるファイルで管理される
インデックスをどう使うかはDBMS側が内部で勝手に判断するのでユーザーは意識しない



・システムファイル
DBMSの内部管理用に使われるデータを保持するファイル



・一時ファイル
SQLで使われたサブクエリを展開したデータ、GROUP BY, DISTINCTを利用したときのソートデータ
などを一時的に保持するファイル。
処理が追われば消えるので、継続的にサイズが増加することはない。



・ログファイル
テーブルのデータに対する変更を一旦受け付けるためのファイル。
ある程度溜め込んだら一括してデータファイルに反映する。
Mysqlだと「バイナリログ」がそれにあたる


2-3バックアップ設計
バックアップは基本的にファイルのコピーで行う

フルバックアップ
名前の通り全部バックアップする


シンプルだが欠点がある

ーバックアップの時間が長い
ーハードウェアリソースへの負荷が高い
ーサービス停止が必要

上記理由からフルバックアップだけというのは厳しい



差分バックアップ
毎日フルバックアップではなく、特定のポイントだけでフルバックして残りは差分を積み上げてバックアップすること。
月曜だけフルバックアップし、各曜日ごとに差分を積み重ねていく。

日曜に障害が起きたら、大元である月曜と、土曜(火曜から積み重ねてきた)のバックアップを用いてリカバリする。
バックアップの時間は短くなり、容量も節約できる。

が、リカバリの手順がめんどくさい。




増分バックアップ
月曜日だけフルバックアップし、あとは各曜日ごとにその日のバックアップだけを溜めておくこと。
日曜に障害が起きたら、火曜〜土曜のすべてのバックアップを用いてリカバリする

バックアップデータは最小になり、バックアップ時間も最短、容量も最小で済む。
一方リカバリがしぬほどめんどい


Mysqlフルバックアップと増分バックアップだった。



バックアップファイルを戻す作業を「リストア」
そのファイルに対してトランザクションログを適用して変更分を反映する作業を「リカバリ



毎日22時〜23時にフルバックアップするとして、日曜13時に障害が起きた時、
バックアップファイルを戻るだけだと、土曜の23時~日曜13時までのデータは戻らない(バックアップの範囲外)

そこをカバーするためにはデータベースサーバーに残っている未バックアップのトランザクションログを適用する必要がある(ロールフォワード)



手順まとめ
1、フルバックアップのファイルをデータベースに戻す(リストア)
2、差分or増分バックアップトランザクションログを適用する(リカバリ
3、データベースサーバにに残っている未バックアップのトランザクションログを適用する(ロールフォワード)



3-1テーブルとは
「ある共通点を持ったレコードを集めたもの」
ただデータを集めて表にぶっこむだけではダメ。


デーブル名は全て複数形・複数名詞で書けるもの。
社員の集合ならEmployeesと表現できる


テーブルには重複行は存在できない。


親子関係にあるテーブルで、親テーブルの一部データを削除した時に、
関連する子テーブルのデータもあわせて削除することを「カスケード」という


カスケードをどうするか考えるより、そもそもカスケードがどうのこうの考えなくて済む様に
データの削除は子から行うという設計にすれば良い。


外部キーは固定長文字列を用いるべし(IDとか)


テーブル定義において、なるべくNOT NULL制約をつけるべし


3-3正規化とは?


テーブルの全ての列が関数従属性を満たすように整理していくこと。
リレーショナルデータベースでは「YはXに従属する」と表現し


{X}→{Y}
(X列の値が決まれば、Y列の値が1つに決まる)
となる。


正規化は1~5までのレベルがある。
第3正規形までは理解すべし


正規化のメリットもきちんと理解すること。
そのためには正規化している時としていない時を比較すべし


正規化は可逆的でなければならない。正規化から非正規化に戻せる(JOINがそれ)


情報を完全に保持したままテーブル分割することを無損失分解という


3-4第1正規形
「1つのセルの中には1つの値しか含まない、を実現する」
1セル1値の値を「スカラ値」という

なぜ1セル1値なのか?
→セルに複数の値を許せば、主キーが各列の値を一意に決定できないから(主キーの定義に反する)



3-5第2正規形
「部分関数従属から完全関数従属にしていく」

主キーの一部の列に対して従属する列を部分関数従属という。

この部分関数従属を解消する(第2正規化を行う)方法はテーブルの分割

部分関数従属の関係にあるキー列と従属列を別テーブルに切り分ける


第2正規化は
異なるレベルの実体(エンティティ)をきちんとテーブルとしても分離すること。



3-6第3正規形
「推移的関数従属の解消を行う」

一つのテーブルで
{Y}→{Z}
という関数従属があり、

{X}→{Y}
という関数従属があった場合

{X]→{Y}→{Z}
という二段階の関数従属があることになる。

それが推移的関数従属であり、第3正規形はそれを解消していく作業


やり方としては第2正規化と同じ様にテーブルを分割する


もちろん第3正規化も可逆的であり、無損失分解になる。


第4、5は省略。。。


3-10正規化まとめ

・正規化とは更新時の不都合、不整合を排除するために行う
・正規化は従属性を見抜くことで可能になる
・正規化はいつでも非正規化に戻せる(可逆的である)
・正規化は無損失分解である
・第3正規化までは原則行うべし
・関連エンティティが存在する場合は、1対1になるよう注意する



メリット
・データの冗長性が排除され、更新時の不整合を防げる
・テーブルのデータが明確になり、開発者が理解しやすい

デメリット
・テーブルの数が増えるので、SQLでの結合が増え、パフォーマンスが落ちる。



第5章

データ整合性をパフォーマンスはトレードオフの関係
なので、必ず正規化すべきというわけでもない

ただ、原則としては正規化していくのが良いとのこと。
(高次であればあるほどいい)

まずは正規化していき、パフォーマンスに難が出たときの最終手段として
非正規化を考えるのが良い


第6章

SQL文はインデックスを辿ることで、テーブルの特定レコードを狙い撃ちでアクセスできる
なのでインデックスはSQLのパフォーマンス改善のためのポピュラーな手段

・アプリケーションのコードに影響しない
・テーブルのデータに影響しない
・それでいて性能改善の効果が大きい


インデックスにはいろいろあるが、まずはB-treeインデックス


インデックスを貼る際の指針
・大規模なテーブルに対して作成
・カーディナリティが高い列
・where句の選択条件、あるいは結合条件に使用されている列


※カーディナリティとは特定の列の値が、どれくらの種類の多さを持つか。
「性別」という列(カラム)があった際、種類としては男性、女性、不詳だとすると
カーディナリティは3になる。


データ量が少ない場合はインデックスの効果はないに等しい(目安1万件)


where句の選択条件(カラム)にインデックスをつけても意味がないケース。
・そのカラムに演算を行なっている
SQL関数を適用している
・IS NULLを使っている
・否定形を用いている
・ORを用いている(INでうまく書き換えよう)
・後方一致or中間一致のLIKEを用いている(前方一致ならOK)
・暗黙の型変換を行なっている(明示的にキャストすればOK)



インデックスは更新性能を劣化させる。
なので、定期的なメンテをすると良い(インデックスの再構築)



DBMSSQL文を受け取ってテーブルにアクセスするまでの流れ
1、SQL文がDBMSへ発行される
2、DBMS内のパーサというモジュールが受け取り構文チェック
3、OKならパーサからオプティマイザというモジュールにSQLが送られる
4、DBMSの頭脳であるオプティマイザが実行計画(SQLのアクセスパス)を決定する
5、決定に関して統計情報が必要なので、オプティマイザはカタログマネージャというモジュールに統計情報の照会をかける
6、統計情報を管理するカタログマネージャが統計情報をオプティマイザに渡す
7、オプティマイザはそこから最短経路を探し、SQLを手続きに変更する(その手続きが実行計画)
8、その実行計画によって実データであるテーブルへのアクセスが行われる



このように実行計画はDBMSがいい感じにしてくれるので、エンジニアは下記2点を考えればいい
・統計情報収集のタイミング → データが更新された時(夜間)
・統計情報収集の対象(範囲)→ 変更のあったテーブル(orインデックス)


第7章

アンチパターン

・非スカラ値(配列型でデータを持つなど)

スカラ値は、意味的に分割できる限り、なるべく分割した方がいい、

鈴木太郎→鈴木と太郎にわける(姓と名)
hoge@email.com→testとemail.comにわける(アカウント名とドメイン名)


上記理由としては
分割されているものを結合するのは簡単だが、結合された状態のものを分割するのは難しいから。
例えば名前だと、どこからが姓でどこからが名なのかは判断が難しいケースがある。
なので分けていた方がいい


※ただし、分解する時は意味を壊してはいけない



ダブルミーニング

列の意味を途中で変えること。
(体重として分類していたカラムを後から年齢として使い出した例があるらしい)


・単一参照テーブル
同じ構造を持っているテーブルを一つにまとめる行為


・テーブルの水平分割
レコード単位でテーブルを分割すること
正規化の理論での分割ではなく、拡張性に乏しい(テーブルが徐々に増えていく)
パーティションで実現できるのでそっちがよろしい


・テーブルの垂直分割
カラム(列)を軸にして分割すること

代替案としては「集約」がある
列を絞り込んだ小規模なテーブル(データマート)
サマリーテーブル(事前に集約を行ったテーブル)
など。

どちらも元データとの整合性の取り方に注意する必要がある。



よく似た技術として
・シャーディング
・カラムベースデータベース
がある。(こちらは普通に使われているアーキテクチャ


・不適切なキー
可変長文字列は不変性がないためキーには不向き。
名前などは変わる可能性があるのでキーにはしない。



・ダブルマスタ
マスターテーブルが二つ存在すること

第8章

代理キー(サロゲートキー

・入力データに主キーにできるような一意キーがなかったり
・一意キーはあるけど、途中で値が変更される可能性があるものだったり
・一意キーはあるけど、途中で指し示す対象が変わったりして、
主キーが決められなかったり、不十分だったりすることがある。


その解決策として代理キー(サロゲートキー)というものがある。
一意な連番として代理キーを新しく人工的に追加することで、主キーとしての役割を担ってもらう


今までずっとサロゲートキー のパターンを見てきて、それが当たり前だと思ってたけど、
ここではグレーゾーンなのか、、、他の現場では実際どうなんだろう。。。


KISSの原則(Keep It Simple, Stupid)
過度に複雑な作りはシステムをダメにするという思想



レプリケーションのステップ
1、ユーザがマスタデータベースに対して更新SQLを実行
2、マスタデータベースでトランザクションログが生成される。
3、トランザクションログがマスタデータベースからスレーブデータベースに転送される
4、スレーブデータベースでトランザクションログが適用される


第9章

リレーショナルデータベースは木構造の表現が苦手。


それを表現するための伝統的な解法として
・隣接リストモデル(ポインタ)
がある。


新しい解法として
入れ子集合モデル(包含関係(整数))
がある。



入れ子集合モデルはリーフ配下に新たに要素を追加したい場合に
他の要素の値も変える必要があるのが弱点


例えば、
2⇄3の間に入れるには2⇄3を2⇄5などに拡張して3⇄4という枠を作れるが、
そのときにその他包含関係の要素も変わる可能性がある


ただこれは入れ子集合モデルが整数で表しているから起こることであって
整数じゃなくて、実数にすれば


2⇄3の間もとることができる。→ 2.5⇄2.7とか
そしてこれは無限に作れる。


これを
入れ子区間モデル(包含関係(実数))
という


ファイルシステムの構造で表す
・経路列挙モデル(ディレクトリとパスの関係)
がある。


まとめ

深読みせずさらっと読んだだけだが、
意外だったこと(サロゲートキーを非推奨としていること)や、
いろんな新しい知識(まだまだ表面的だが)
を知ることができた


深く完全に理解したわけではないが、頭のなかにインデックスは作れたので
必要になって読み返した時に、スムーズに理解できると思う。


こういう技術書の読み方も良いと思った。

プロになるためのweb技術入門 殴り書きメモ

プロになるためのweb技術入門をささっと読んだ時の殴り書きメモ。

殴り書きメモ

■Lesson3

受信した情報がどのようなプロトコルで、どのアプリケーションが処理すべきかTCP/IPだけでは判断できない。
そこでポートが出てくる。80番ポートならHTTPプロトコルでのリクエストだとわかる。


アプリケーション側は待ち受けポートとしてTCP/IPからの情報をまつ。

GETリクエストの利点としてURLベースの行動に特化している
・お気に入り登録ができる(
・サイトの紹介ができる
など

■Lesson4


Cookie
サーバからクライアントに送る状態維持の情報
サーバからcookieを受け取ったクライアントは次にまたそのサーバーに
リクエストする際はリクエストヘッダにcookieを持たせる。
だいたい、ログイン時のサーバーからのレスポンスヘッダに入っている


セッション
ステートレスなHTTP通信において、独立したリクエストの間で情報を保持するための仕組み。
同クライアントからの異なるリクエストを、1連の関連するリクエストとして認識して扱える仕組み。
ある処理の一連の流れ
ログイン→注文→注文確認→ログアウト、みたいな
セッションIDは銀行で言う整理券

通常セッション情報はメモリ管理なので、増えるとメモリを圧迫していく
なので一定期間を過ぎたらセッションを自動削除する(セッションタイムアウト
APサーバーは大体セッションを利用した時間を記憶している。
セッションはHTTPの仕組みを利用してサーバー側が実現しているので、クライアント側からはどうこうできない。
ユーザにログアウトを強制できないので、タイムアウトがあるとはいえ、ある程度メモリを圧迫することは把握しておかないといけない


Cookie自体にユーザ状態を持たせるのではなく、整理券であるセッションIDを格納し、
サーバー側でそのセッションIDをもとにクライアントの状態を復元するなどして状態保持をすることがほとんど


セッションは一連の操作を実現する仕様(概念)であり、cookieはそれを実現するための一つの実装


ログインの一連の流れ
1、ユーザー名、パスワードで状態チェック(クライアントからサーバ)
2、セッションIDを発行、セッション情報にログイン中のユーザー情報を記録(サーバ処理)
3、cookieにセッションIDを格納してレスポンス(サーバーからクライアント)
4、セッションIDを格納したクッキーを渡す(クライアントからサーバ)
5、セッションIDからログイン中ユーザーを識別(ユーザー固有の状態を持ったレスポンスを返す)
6、マイページ表示など

■Lesson5


複数のコンピュータを組み合わせて1つのシステムを作る場合、各コンピュータをノードという

複数プロセスを同じコンピュータでうごかく=同一ノード上で動かす
複数プロセスを異なるコンピュータ(サーバ)で動かす=異なるノード上で動かす

webサーバーとAPサーバーは異なるプロセスで動いている
連携方法は色々あるが、一般的にはAPサーバー側が連携用モジュールを用意しており、それをwebサーバーに組み込むことで連携をしている。

Apache(webサーバー)とTomcat(APサーバー)の場合、mod_jkと呼ばれるモジュールがあり、それをApache拡張機能として組み込み連携している

流れとしては
apacheに届いたHTTPリクエストをmod_jkTomcatへ転送、それをTomcatがその上で動作するwebアプリケーション(JSPやSprintとか)に渡す。
webアプリケーションはリクエストの基づいて処理を行い、結果をTomcat(APサーバ)へ返し、Tomcatはリクエストの時とは逆のルートで、mod_jkに返し、ApacheがクライアントへHTTPレスポンスを返す。

ApachとTomcat間の通信はHTTPではなく、ajp13というTomcat独自のプロトコル


・webサーバーがHTMLファイルや画像、動画等の静的コンテンツのみで構成させるページを持ち
・動的コンテンツはAPサーバーが担う
・なので、サーバーサイドでは最低二つのプロセスが連携している。
・じゃあ動的コンテンツは直接APサーバーにリクエストがいくのかというとそうではない
・クライアント的にリクエスト内容によって要求先のプロセスが異なるのは困る
・なので、すべてのリクエストは一旦webサーバーに送る。
・設定ファイルで振り分けるリクエストを決めている。
Tomcatだとworkers.propertiesファイル
Apacheだとhttpd.conf
・http.confのJkMountでTomcatに送るパスを指定している
ApacheTomcatを別のノードにするなら、workers.propertiesにTomcatのIPを設定すればいい
・別のノードにできるので、Apache : Tomcatをn : n の構成で作れる
・でもあまり分離することはないとのこと(中小規模の場合)
・ 分離しすぎると複雑化して運用が大変
・実はほとんどのAPサーバーはwebサーバーとして動作させることもできる
・構成もシンプルで敷居が低くなるが、Apacheほどの機能性はない
・APサーバはwebサーバ機能意外にも、セッション管理、トランザクション管理、データベース接続管理等がある



webサーバーは全てのリクエストを一旦受け取るので、リクエスト回数は多いが、
処理としては静的コンテンツを返すだけなので、軽い。

一方APサーバーは動的コンテンツのリクエストを捌くので、webサーバーほとリクエスト回数は多くないが、
一つ一つの処理は重くなる傾向にある。

Lesson6

webアプリケーションのレイヤーパターン

プレゼンテーション層(コントローラ、ビュー)
システム利用者とアプリケーションのインターフェースとなるレイヤ

ビジネスロジック層(モデル)
アプリケーションが実現すべき固有の処理を実行するレイヤ。

データアクセス層(モデル)
ビジネスロジック層とデータベースの仲介を担うレイヤ

データアクセス層の実現方法としてDAO(Data Access object)がある
データアクセスをDAOクラスに切り分けて、再利用性と保守性を向上させる
DAOは大体テーブルごとに作られる


Lesson7

SQLインジェクション
フォーム などの入力インターフェースにSQLを入れてデータベースを(開発者の意図しないものに)書き換える行為。
ユーザ名にtest, パスワードにpasswordを入力してログインできるとした場合、

select * from user where user_name='test' and password='password'

このようなSQLになる。

そこで、パスワードの入力欄に「'password' of 'or '1'='1'」と入力すると、

select * from user where user_name='test' and password='password' or '1' = '1'

というSQLになり、ログインできてしまう。
これはpasswordというパスワードを知らなくてもログインできる、つまり第三者でもログインできるのでまずい。

防ぐ方法としては
・入力値チェック
・プリアードステートメントの利用
がある

クロスサイトスクリプティング
SQLインジェクションと似ていて、HTMLの中に悪意あるJavascriptを埋め込んで、予期しないデータを表示させたりする行為。
Javascriptインジェクションともいう

防ぐ方法としては「サニタイジング」がある。
HTMLに影響を与える文字列(特殊文字)を文字参照に置き換え、無害化する。

セッションハイジャック
クライアントとサーバ間でのやりとりで利用されているセッションIDを盗むこと。
セッションが乗っ取って、利用者になりすましたりする。

対策として、
クロスサイトスクリプティング対策
・通信経路の暗号化(SSL
・セッションタイムアウトの値を変える
・セッションIDをランダム化する

クロスサイトリクエストフォージェリCSRF
ユーザを攻撃用の(HTMLが埋め込まれた)サイトに誘導、(ユーザが訪れようとしているサイトにリンクを埋め込むなど)
サイトが読み込まれると同時にJavascriptが実行され、
ユーザに意図しないリクエスト(掲示板への書き込みだったり、商品の購入だったり)が行われる。

まとめ

本当にただの殴り書きになってしまった。
間違っている部分もあると思うので、都度修正していこうと思う。

Lesson7のセキュリティに関しては別記事でしっかりまとめていきたい。

おわり。

継承は非推奨

プログラムを書く時、同じようなコードを書くのは非効率。
なので、共通的な部分はまとめてしまって、付け足し方式で機能を拡張していくというような、
効率性を実現できるのが継承。

共通部分をまとめて継承してしまえば、あとは差分のコードを書くだけで済む。
このような手法を差分プログラミングという。

Javaを書いてたら継承は当たり前のように見るし、普通に使われているので、
主流の形だと思っていたが、どうやらそうでもないらしい。

public class hoge{
  //何かしらのコード
}

例えば上記のような、hogeクラスがあった場合、
それ単体でみると、このhogeクラスをどのクラスが継承しているのかは
ここだけでは判断できない。

もしこのhogeクラスに変更を加えたいと思った場合、hogeクラスを継承しているクラスが
たくさんあったら、影響範囲は多岐に渡り、改修は複雑になりうる。

継承の本質は、

交換可能なパーツを作成するために共通点を「規格」としてまとめ上げられるインターフェイス

(誰かのqiitaの記事から引用してきた)

単なる機能の受け継ぎではないとのこと。


なので、継承を単なる機能の受け継ぎとして使うのではなく、
共通規格としてまとめあげたインターフェース、そしてそのインターフェースに対してプログラムするポリモーフィズム
を実現するために使うべき。(抽象クラスとかの話になってくると思う)


まだふわっとしか理解してないので、
次はこのへんを具体的にコードに落とし込んで理解を深めていこうと思う。

javaのパッケージについて

Javaのパッケージの役割は3つ

・名前の衝突を避ける。(名前空間の提供)

・アクセス修飾子を使ってクラスの公開・非公開を制御できる。

・クラスの分類が可能。



名前の衝突を避ける

開発をする際、自分だけでプログラムを全て書くわけではなく、過去に書かれたソースを利用したり、外部のライブラリを利用したりするはず。

その際に、クラス名が被ってしまうとコンパイラJVMがどのクラスを利用するか判断できずにエラーが発生したり、意図した設計が崩れてしまう可能性がある。



それを避けるために、コンパイラJVMFQCN(完全修飾クラス名, 完全限定クラス名)

でクラスを扱う。


パッケージ名.クラス名 ← FQCN(full qualified class name)



被りをふせがないといけないから、パッケージは基本的には一意でなければならない。
それを実現するための慣習として、ドメイン名を逆にしたものをパッケージ名にすることが多い。
(もちろん任意のパッケージ名をつけることはできる)

もしドメイン名が
hogehoge.co.com
だったら、パッケージ名は
com.co.hogehogeになる。



アクセス修飾子を使ってクラスの公開・非公開を制御できる。

先ほどのcom.co.hogehogeをパッケージ名とした二つのクラスを作る。

Aクラス
package com.co.hogehoge;
public class A {
  ~~~~~
}


Bクラス
package com.co.hogehoge;
class B {
  ~~~~~
}

こういうcom.co.hoghogeパッケージに属する二つのクラスが合ったとき
Aはpublicなので、外部パッケージからでも利用できるが、Bはpublicではないので、外部からは使えない。

クラスの分類が可能
Aクラス
package com.co.hogehoge;
public class A {
  ~~~~~
}

Aクラス
package com.co.fugafuga;
public class A {
  ~~~~~
}

この場合、どちらもAクラスで同じ物に見えるが、
クラスはパッケージ名.クラス名で扱う(FQCN)ので
この2つは全く別のものとなる。



※クラスは必ず何かしらのパッケージに属することになる。パッケージ宣言を省いたものは無名パッケージに属する解釈となる。

DIとかDIコンテナとか(WIP)

 

DIとかDIコンテナとか正直全然わかってなかったので、調べてみた。

 

DIとは

デザインパターン(設計思想)の一つ。
Dependency Injectionの略。
依存性の注入とか言われてるが、実際にはオブジェクトの注入。
 

DIは三種類ある

・コンストラクタインジェクション
・セッターインジェクション
・フィールドインジェクション
 

下記はコンストラクタインジェクション

class  A{
    public a_method(){
        ~なんらかの処理~
    }
}

DIじゃない場合

class B {
    private A a;

    public B(){
    this.a = new A();
    } 

    public void do(){
        a.a_method();
    }
} 

DIの場合

class B {
 private A a;

    public B(A a){
    this.a = a
    }

    public void do(){
        a.a_method();
    }
}

 

DIじゃない時はAをnewしてる(BはAに依存している)けど、
DIの時は外部からAのオブジェクト(DIコンテナでnewしている)を注入している。


この場合依存というのは
「クラスA を動かすためにはクラスBが必要」
ということ。

また、もしAのコンストラクタが引数を持っていた場合、

 

DIの何がいいのか

 

 ・単体テストがしやすくなる。
DIをしていると(疎結合状態)、Bクラスの外からAのオブジェクトを注入できるので、Aクラスの実装がされてなくてもそこをモック化してテストが書ける。
DIをしていない場合(密結合)、Bクラスのテストを書くときはAクラスの実装もされていないと書けない。(書けるとおもう)

疎結合により柔軟性が高くなる
外部からオブジェクトを渡すので、環境によって設定値を変えたりできる。
また、DIをしていない場合、Bクラスの実装をいじってなくてもAクラスの実装をいじった際に、Bクラスのテストが壊れてしまう可能性が高い。
結合テストならいいけど、単体テストとしてはNG)

DIコンテナとは

DIを実現するためのフレームワークみたいなもの。
コンテナ内でオブジェクトを生成し(new)、それを注入していく。



疲れたのでここで一旦終わり。
わかったことがあったら随時更新していく。(するかな、、、笑)

 

 

httpsとかSSL/TLSとか

~ メモ ~ 

 

httpsとかssl/tlsとかについて、

sがsecureのsだから、なんか安全なんでしょ?ぐらいにしか把握してなかったので(ひどすぎる)色々調べてまとめてみた。

 

そもそもセキュリティもとい、セキュアな通信を実現することは

・機密性(Confidentiality)

・可用性(Integrity)

・完全性(Availability)

の3つを満たすこと。

 

SSL/TLS

Transport Layer Security(トランスポート・レイヤー・セキュリティ、TLS)は、インターネットなどのコンピュータネットワークにおいてセキュリティを要求される通信を行うためのプロトコルである。主な機能として、通信相手の認証、通信内容の暗号化、改竄の検出を提供する 

 wikipedia参照。

 

・セキュアの通信を行うためのプロトコル

・多くの場合、トランスポート層TCP)とアプリケーション層(HTTP)間で使われる。

・httpなどのアプリケーション層プロトコルと組み合わせることで(https)セキュアな通信を実現する。

・HTTP以外にも適用できる。(httpの利用を想定した設計にはなっているらしい)

ssltlsは実際には別物らしいが、まぁtlsが新しいぐらいに思っておけばおk。

 

HTTPS

HTTPS (Hypertext Transfer Protocol Secure) は、HTTPによる通信をより安全に(セキュアに)行うためのプロトコルおよびURIスキームである。厳密に言えば、HTTPS自体はプロトコルではなく、SSL/TLSプロトコルによって提供されるセキュアな接続の上でHTTP通信を行うことをHTTPSと呼んでいる。

 wikipediaに全て載っていた。

https://milestone-of-se.nesuke.com/nw-basic/tls/https-structure/

この記事にわかりやすいシーケンスがある。

 

これらをふまえて

http通信にssl/tlsを適用してセキュアな通信を行う(https)ということがわかったが、実際になにをしているかというと、先ほど書いたように、

・通信相手の認証

・通信内容の暗号化

・改竄の検出

を行う。

 

通信相手の認証

訪れようとしているwebサイトが本物であることを保証すること。

それを実現するのが証明書(ssl証明書とかsslサーバ証明書とか言ったりする)

三者の保証と考えていい。

 

アップルの公式ストアを例にすると

https://www.apple.com/jp/

というサイトが本当にアップル社が提供しているのかどうかを

DigiCert High Assurance EV Root CA

という証明書(第三者)で保証している。

 

よくきくオレオレ証明書はこの第三者が提供する証明書を自分で作って自分に発行することを言う。

 

通信の暗号化

通信内容を盗聴されないように暗号化する。

 

  1. 認証局から証明書と秘密鍵を取得しサーバーに設置
  2. クライアントからサーバーに接続要求がくる
  3. サーバーが証明書と公開鍵をクライアントに送る
  4. クライアントは認証局で証明書の検証
  5. クライアントは共通鍵を作り公開鍵でそれを暗号化してサーバに送る
  6. サーバは秘密鍵を使って暗号化された共通鍵を複合(どちらも共通鍵を所持)
  7. お互い共通鍵を使用して暗号化通信を行う

ざっくりとこんな感じ(ざっくりしすぎ)

https://qiita.com/kuni-nakaji/items/5118b23bf2ea44fed96e

この人の記事がめちゃくちゃわかりやすかった。

 

改竄の検出

データを書き換えられたら困るので、データの完全性を担保するためにメッセージのダイジェスト(フィンガープリントと言ったりもするらしい)と呼ばれる値を計算して通信に添付する。

 

受信側はダイジェスト再計算してあっているかどうかを確認。

これでデータの完全性が担保できる

 

 

この人の記事もめちゃくちゃわかりやすかった。

というか、こッチの方が断然良い。

https://qiita.com/t_nakayama0714/items/83ac0b12ced9e7083927

 

おわり