JavaScript Primerを読んでの感想
はじめに
JavaScriptの文法の勉強にJavaScript Primerという本を読みました。
今回はJavaScript Primerを読んでの感想をまとめていきたいと思います。
学んだこと
JavaScriptとECMAScript
ECMASctiptはどの実行環境でも共通な動作のみが定義されており、JavaScriptはECMAScriptと実行環境の固有機能を含んだものを表す。
変数と宣言
varの問題点
const
,let
→同じ変数名で再定義しようとすると構文エラー
var
→同じ変数名で再定義可能
var
は同じ変数名で再定義可能であるため、意図せずに再定義した場合もエラーとならず、上書きされてしまう点が問題点。
データ型とリテラル
データ型
データ型を大きく分けると、プリミティブ型とオブジェクトの2つに分類される。
プリミティブ型(基本型):真偽値や数値等の基本的な値の型のこと。
オブジェクト型:プリミティブ型以外のデータ。配列や関数も該当。オブジェクトはミュータブル特性をもつ。
typeof
演算子を用いることでデータ型を調べることが可能。
リテラル
プログラム上で数値や文字列等、データ型の値を直接記述できるように構文として定義されたもの。
以下の5つのプリミティブ型はリテラル表現を持っている。
- 真偽値
- 数値
- BigInt
- 文字列
- null
オブジェクトの中でも以下3つはリテラル表現が用意されている。
- オブジェクト
- 配列
- 正規表現
演算子
演算子はよく利用する演算処理を記号などで表現したもの。
比較演算子
// 厳密等価演算子(===) console.log(1 === 1); // 厳密不等価演算子(!==) console.log(1 !== 1); // 等価演算子(==) // 型が同じになるよう暗黙的な型変換を行ってから比較する console.log(1 == "1"); // => true // 不等価演算子(!=) console.log(1 != "1"); // => false
分割代入
分割代入を使うことで、配列やオブジェクトの値を複数の変数に同時代入可能。
const array = [1, 5]; // aとbにそれぞれ代入される const [a, b] = array; const obj = { "hoge": "fuga" }; // hogeに"huga"が代入される const { hoge } = obj;
Nullish coalescing演算子(??)
左辺の値がnullishであれば右辺の評価結果を返す。
ここでnullishとは、評価結果がnull
またはundefined
となることを指す。
const tmp = null; // tmpがnullであれば、resultに1が代入される const result = tmp ?? 1;
暗黙的な型変換と明示的な型変換
暗黙的な型変換
ある処理において、その処理過程で行われる明示的ではない型変換のこと。
1 + "2"; // => "12" 1 - "2"; // => -1
暗黙的な型変換は意図しない結果となりやすいため、比較には、等価演算子ではなく、厳密等価演算子を用いる。
関数と宣言
デフォルト引数
仮引数に対応する引数が渡されていない場合にデフォルトで代入される値を指定可能。
function double(a = 1){ }
可変長引数
任意の個数の引数を受け取ることが可能。
function fn(...args){ // 処理 }
arguments
を使って可変長引数を扱うことも可能。
function fn(){ console.log(arguments[0]); // => "a" } fn("a","b","c");
分割代入
関数においても分割代入可能。
function fn({ id }){ console.log(id); // => 1 } const user = { id: 1 }; fn(user);
アロー関数
以下特徴的なものを記載
const hogeA = arg => { /* 処理 */ }; const hogeB = arg => arg * arg;
アロー関数の特徴
- 名前をつけることができない
this
が静的に決定できる- 短く記述可能
new
できないarguments
を使用できない
コールバック関数
引数として渡される関数。
呼び出し元が用意した別の関数を、呼び出し先の処理の中から、呼び出し返す形になるため「コールバック」と呼ばれる。
高階関数
コールバック関数を引数として受けとる関数やメソッドのこと。
オブジェクト
optional changing演算子(?.)
左辺のオペランドがnullish(null or undefined)の場合に、それ以上評価せずにundefined
を返す
オブジェクトの列挙
const obj = { "one": 1, "two": 2, "three": 3 }; console.log(Object.keys(obj)); // => ["one","two","three"] console.log(Object.values(obj)); // => [1,2,3] console.log(Object.entries(obj)); // => [["one", 1], ["two", 2], ["three", 3]]
プロトタイプオブジェクト
Objectはすべての元
他のオブジェクトは全てObject
を継承している。
prototype:全てのオブジェクトの作成時に自動的に追加される特殊なオブジェクト。
プロトタイプメソッド:prototype
に組み込まれているメソッドをと呼ぶ。
プロトタイプチェーン:インスタンスからprototype
オブジェクト上に定義されたメソッドを参照できる仕組み。
Object.hasOwn
とin
の違い
Object.hasOwn:指定したオブジェクト自体が指定したプロパティを持っているかを判定。
in:オブジェクト自身が持っていなければ、オブジェクトの継承元であるprototype
オブジェクトまで探索して持っているかを判定する。
Object.prototype
を継承しないオブジェクト
Object.create(null)
とすることで、Object.prototype
を継承しないオブジェクトを作成可能。
配列
Array.prototype.at
const array = ["a", "b", "c"]; console.log(array.at(0)); // => "a" console.log(array.at(-1)); // => "c"
配列と分割代入
配列の指定したインデックスの値を変数として定義し直す場合には、分割代入が利用可能。
const array = ["one", "two", "three"]; const [first, second, third] = array; console.log(first); // => "one" console.log(second); // => "two" console.log(thrid); // => "three"
インデックスを取得
インデックス:配列の要素位置のこと
const array = ["Java", "JavaScript", "Ruby", "JavaScript"]; const indexOfJS = array.indexOf("JavaScript"); console.log(indexOfJS); // => 1 const lastindexOfJS = array.lastIndexOf("JavaScript"); console.log(lastIndexOfJS); // => 3 console.log(array.indexOf("JS")); // => -1
異なるオブジェクトだが、値は同じものを見つけたい場合は、ArrayのfindIndex
メソッドを使用する。また、末尾から検索した結果を得る場合にはfindLastIndex
を使用する。
配列から要素自体が欲しい場合は、find
メソッドを使用する。
const colors = [ { "color": "red" } { "color": "blue" }, { "color": "green" }, { "color": "blue" } ]; const indexOfBlue = colors.findIndex((obj) => { return obj.color === "blue"; }); const indexLastOfBlue = colors.findLastIndex((obj) => { return obj.color === "blue"; }); const redColor = colors.find((obj) => { return obj.color === "red"; }); console.log(indexOfBlue); // => 1 console.log(colors[indexOfBlue]); // => { "color": "blue" } console.log(indexLastOfBlue); // => 3 console.log(colors[indexOfBlue]); // => { "color": "blue" } console.log(blueColor); // => { "color": "red" }
指定範囲の要素取得
const array = ["A", "B", "C", "D", "E"]; console.log(array.slice(1, 4)); // => ["B", "C", "D"] console.log(array.slice(1)); // => ["B", "C", "D", "E"]
配列に指定要素が含まれれているかを判定する
includes
メソッドは配列に指定要素が含まれているかを判定する。
const array = ["Java", "JavaScript", "Ruby"]; if (array.includes("JavaScript")) { console.log("配列にJavaScriptが含まれている"); }
追加と削除
push
を使用することで、配列の末尾に要素を追加することが可能。また、pop
を使用することで末尾から要素を削除することが可能。
const array = ["A", "B", "C"]; array.push("D"); console.log(array); // => ["A", "B", "C", "D"] const poppedItem = array.pop(); console.log(poppedItem); // => "D" console.log(array); // => ["A", "B", "C"]
配列同士の結合
concat
メソッドを使用することで配列と配列を結合した新しい配列を作成可能。
const array = ["A", "B", "C"]; const newArray = array.concat("新しい要素"); console.log(newArray); // => ["A", "B", "C", "新しい要素"]
配列から要素を削除
splice
メソッドを使用することで、配列の任意のインデックスの要素を削除可能。
const array = ["a", "b", "c"]; array.splice(1, 1); console.log(array); // => ["a", "c"] array.splice(0, array.length); console.log(array); // => 0
配列の全ての要素を削除するにはlength
プロパティへの代入を利用した方法もある。
配列のlength
プロパティへ要素数を代入すると、その要素数に配列が切り詰められる。
const array = [1, 2, 3]; array.length = 0; console.log(array); // => []
文字列
文字列の分解と結合
split
メソッドを使用することで、文字列を配列へ分解することが可能。
また、join
メソッドを使用することで、配列の要素を結合して文字列にすることが可能。
const strings = "赤・青・緑".split("・"); console.log(strings); // => ["赤", "青", "緑"] const str = "赤・青・緑".split("・").join("、"); console.log(str); // => "赤、青、緑"
文字列の一部を取得
substring
メソッドは、第一引数に開始位置、第二引数に終了位置を指定し、その範囲を取り出して新しい文字列を返す。
const str = "ABCDE"; console.log(str.substring(1)); // => "BCDE" console.log(str.substring(1, 5)); // => "BCDE" console.log(str.substring(1, 4)); // => "BCD"
正規表現オブジェクト
正規表現オブジェクトを作成するには、正規表現リテラルとRegExp
コンストラクタを使用する方法がある。
const patternA = /パターン/フラグ; const patternB = RegExp("パターン文字列", "フラグ");
正規表現による検索
const str = "abc123def"; const searchPattern = /\d+/; const index = str.search(searchPattern); console.log(index); // => 3 const results = str.match(searchPattern); console.log(results[0]); // => 123 console.log(results.index); // => 3
matchメソッドの挙動
- マッチしない場合は、
null
を返す - マッチした場合は、マッチした文字列を含んだ特殊な配列を返す
- 正規表現の
g
フラグがある場合は、マッチした全ての結果を含んだただの配列を返す
文字列の置換/削除
Stringのreplace
メソッドを使用することで、削除したい文字を取り除いた新しい文字列を返し、削除を表現する。
const str = "文字列"; const newStr = str.replace("文字", ""); console.log(newStr); // => "列"
関数とスコープ
スコープチェーン:内側から外側のスコープへと順番に変数が定義されているかを探す仕組みのこと。
変数の隠蔽:内側のスコープで外側のスコープと同じ名前の変数を定義することで、外側の変数を参照できなくなること。
関数スコープとvarの巻き上げ
function fn() { console.log(x); // => undefined { var x = "varのx"; } console.log(x); // => "varのx" } fn();
上記コードは以下のように解釈されて実行されている。
function fn() { var x; console.log(x); { x = "varのx"; } console.log(x); // => "varのx" } fn();
変数の宣言部分が最も近い関数またはグローバルスコープの先頭に移動しているように見える動作のことを変数の巻き上げと呼ぶ。
クロージャー
ガーベージコレクション:どこからも参照されなくなったデータを不要なデータと判断して自動的にメモリ上から解放する仕組み。
クロージャー:「静的スコープ」と「参照され続けている変数のデータが保持される」という2つの性質によって成立する。
関数とthis
アロー関数以外の関数におけるthis
関数におけるthisの基本的な参照先はベースオブジェクトとなる。
ここで、ベースオブジェクトとは「メソッドを呼ぶ際に、そのメソッドのドット演算子またはブラケット演算子のひとつ左にあるオブジェクト」のことをいう。
ベースオブジェクトがない場合のthisはundefinedとなる。
thisが問題となるパターン
アロー関数以外の関数において、thisは実行したときに決定される。
→関数にthisを含んでいる場合、その関数は意図した呼ばれ方がされないと間違った結果となる。
対処法
- メソッドとして定義されている関数はメソッドとして呼ぶ。
- thisの値を指定して関数を実行する
call,apply,bindメソッド
関数オブジェクトには、call,apply,bindといった明示的にthisを指定して関数を実行するメソッドが用意されている。
call
メソッド
関数.call(thisの値, ...関数の引数);
apply
メソッド
関数.apply(thisの値、 [関数の引数1, 関数の引数2]);
bind
メソッド
関数.bind(thisの値, ...関数の引数);
コールバック関数とthis
コールバック関数ではthisの参照先が変わるため、例外が発生してしまう。
対処法
- thisを一時変数へ代入する
- アロー関数でコールバック関数を扱う
→アロー関数内のthisはスコープチェーンの仕組みと同様に外側の関数を探索する。
アロー関数とthis
アロー関数で定義された関数やメソッドにおけるthis→関数定義時に決まる。
アロー関数でない関数におけるthis→呼び出し元に依存するため関数の実行時に決まる。
アロー関数はthisを暗黙的な引数として受け付けないので、外側のスコープのthisを参照する。
クラス
クラスの定義
クラスは必ずコンストラクタをもち、constructor
という名前のメソッドとして定義する。
クラス宣言文による定義方法
class MyClass { constructor() { // コンストラクタ関数の処理。インスタンス化される時に自動的に呼び出される // 省略可能。 } }
クラス式による宣言方法
cosnt MyClass = class MyClass { constructor() {} }; const AnonymousClass = class { constructor() {} }
クラスのアクセッサプロパティの定義
クラスでは、プロパティの参照(getter)、プロパティへの代入(setter)時に呼び出される特殊なメソッドを定義可能。このメソッドはプロパティのように振る舞うためアクセッサプロパティと呼ばれる。
class NumberWrapper { constructor(value) { this._value = value; } get value() { return this._value; } set value(newValue) { this._value = newValue; } } const numberWrapper = new NumberWrapper(1); console.log(numberWrapper.value); // => 1 numberWrapper.value = 42; console.log(numberWrapper.value); // => 42
Publicクラスフィールド
クラス外からアクセスできるプロパティを定義するクラスフィールド。
class クラス { プロパティ名 = プロパティの初期値; }
Privateクラスフィールド
#
をフィールド名の前につけることで、外からアクセスができないPrivateクラスフィールドを定義可能。
定義したPrivateクラスフィールドは、this.#フィールド名
で参照可能。
class クラス { #フィールド名 = プロパティの初期値 }
静的メソッド
静的メソッド(クラスメソッド):クラスをインスタンス化せずに利用できるメソッド
静的メソッドの定義方法はメソッド名の前にstatic
をつける。
class クラス { static メソッド() { // 処理 } } クラス.メソッド();
静的クラスフィールド
静的クラスフィールドは、フィールドの前にstatic
をつける。
静的クラスフィールドで定義したプロパティは、クラス自体のプロパティとして定義される。
clas Colors { static GREEN = "緑"; static RED = "赤"; static BLUE = "青" } console.log(Colors.GREEN); // => "緑"
プロトタイプオブジェクト
プロトタイプオブジェクト:JavaScriptの関数オブジェクトのprototype
プロパティが自動的に作成される特殊なオブジェクト。
プロトタイプチェーン
プロトタイプチェーン:プロパティを参照する際に、オブジェクト自身からPrototype内部プロパティへと順番に探す仕組み。
プロトタイプチェーンは以下2つの処理から成り立つ。
- インスタンス作成時に、インスタンスの
[[Prototype]]
内部プロパティへプロトタイプオブジェクトの参照を保存する処理 - インスタンスからプロパティ(またはメソッド)を参照するときに、
[[Prototype]]
内部プロパティまで探索する処理
プロパティの参照とプロトタイプチェーン
オブジェクトがプロパティを探索するときは以下の順番で、オブジェクトを調べる。
instance
オブジェクト自身instance
オブジェクトの[[Prototype]]
の参照先- どこにもなかった場合は
undefined
継承
継承したクラスの定義
extends
を用いることでクラスを継承することが可能。
class 子クラス extends 親クラス { }
子クラスから親クラスを参照するにはsuper
を使用する。
class Parent { constructor(...args) { console.log("Parent", ...args); } } class Child extends Parents { constructor(...args) { super(...args); console.log("Child", ...args); } } const child = new Child("引数", "引数2");
コンストラクタの処理順は親クラスから子クラスの順番で行う。
クラスフィールドの継承
Publicクラスフィールドは、親クラスで定義したフィールドも子クラスに定義されるが、 Privateクラスフィールドは、親クラスで定義したフィールドは子クラスに定義されない。
例外処理
try...catch構文
tryブロック内で例外が発生すると、tryブロック内のそれ以降の処理は実行されず、catch節に処理が移行する。 catch節は、tryブロック内で例外が発生すると、発生したエラーオブジェクトともに呼び出される。 finally節は、tryブロック内で例外が発生したかどかには関係なく、必ずtry文の最後に実行される。
try { // 処理 } catch (error) { // 処理 } finally { // 処理 例外に関係なく必ず実行される。 }
throw文
throw文を使うとユーザーが例外を投げることができる。
try { throw new Error("例外"); } catch (error) { console.log(error.message); // => "例外" }
非同期処理:Promise/Async関数
同期処理
同期処理:コードを順番に処理していき、ひとつの処理が終わるまで次の処理は行わない。
非同期処理
非同期処理:コードを順番に処理していくが、ひとつの非同期処理が終わるのを待たずに、次の処理を評価する。メインスレッドで実行されることが多い。
並行処理:処理を一定の単位ごとに分けて処理を切り替えながら実行すること。一部の例外を覗き非同期処理は並行処理として扱われる。
Promise
非同期処理の状態や結果を表現するビルトインオブジェクト。
Promiseインスタンスの作成
PrmiseインスタンスのthenメソッドでPromiseがresolve、rejectしたときに呼ばれるコールバック関数を登録する。
const executor = (resolve, reject) => { }; const promise = new Promise(executor); const onFulfilled = () => { console.log("resolveされたときに呼ばれる"); }; const onRejected = () => { console.log("rejectされたときに呼ばれる"); }; promise.then(onFulfilled, onRejected);
Promiseの状態
Promise
インスタンスには、以下3つの状態が存在する。
- Fulfilled:resolveした時の状態。このときonFulfilledが呼ばれる
- Rejected:rejectまたは例外が発生した時の状態。このときonRejectedが呼ばれる
- Pending:FulfilledまたはRejectedではない状態 or
new Promise
でインスタンスを作成した時の初期状態
Async関数
Async関数は通常の関数とは異なり、必ずPromiseインスタンスを返す関数を定義する構文。
async function doAsync() { return "値"; } // doAsync関数はPromiseを返す。 doAsync().then(value => { console.log(value); // => "値" });
await式
await関数は以下の箇所で利用できる式となっている。
- Async Functionの関数の直下
- ECMAScriptモジュールの直下
await式は右辺のPromiseインスタンスがFulfilledまたはRejectedになるまでその場で非同期処理の完了を待つ。Promiseインスタンスの状態が変わると、次の行を再開する。
async function doAsync() { // 非同期処理 } async function asyncMain() { // doAsyncの非同期処理が完了するまで待つ await doAsync(); // 次の行はdoAsyncの非同期処理が完了されるまで、実行されない console.log("この行は非同期処理が完了後に実行される"); }
Map
マップの作成と初期化
コンストラクタに初期値を渡せるが、コンストラクタ引数として渡せるのはエントリーの配列。
エントリー:1つのキーと値の組み合わせを[キー, 値]
という形式の配列で表現したもの。
const map1 = new Map(); console.log(map1.size); // => 0 const map2 = new Map([["key1", "value1"], ["key2", "value2"]]); console.log(map2.size); // => 2
要素の追加と取り出し
メソッド | 内容 |
---|---|
set |
特定のキーと値を持つ要素をマップに追加 |
get |
特定のキーに紐づいた値を取り出す |
has |
特定のキーにひもづいた値を持っているかを確認する |
delete |
マップから要素を削除する |
clear |
マップが持つすべての要素を削除する |
マップの反復処理
const map = new Map([["key1", "value1"], ["key2", "value2"]]); const results = []; map.forEach((value, key) => { results.push(`${key}:${value}`); }); console.log(results); // => ["key1:value1", "key1:value2"] const keys = []; for (const key of map.keys()) { keys.push(key); } console.log(keys); // => ["key1", "key2"] const kyesArray = Array.from(map.keys()); console.log(keysArray); // => ["key1", "key2"] const entries = []; for (const [key, value] of map.entries()) { entries.push(`${key}:${value}`); } console.log(entries); // => ["key1:value1", "key2:value2"]
WeakMap
Mapと同じくマップを扱うためのビルトインオブジェクト。相違点は、キーを弱い参照でもつこと。
ここで、弱い参照とは、がベー時コレクションによるオブジェクトの解放を妨げないための特殊な参照のことである。弱い参照は例外的に、該当するオブジェクトへの弱い参照があったとしても、そのオブジェクトを解放することが可能。
→メモリリークを防ぐために使われる。
Set
Setは重複する値がないことを保証したコレクションを指す。
セットの作成と初期化
const set = new Set(); console.log(set.size); // => 0 const set = new Set(["value1", "value2", "value2"]); // value2が重複するので、片方は無視される。 console.log(set.size); // => 2
値の追加と取り出し
メソッド | 内容 |
---|---|
add |
作成したセットに値を追加する |
has |
セットが特定の値を持っているかどうかを確認する |
delete |
セットから値を削除する |
clear |
セットが持つ全ての値を削除する |
セットの反復処理
const set = new Set(["a", "b"]); const results = []; set.forEach((value) => { results.push(value); }); console.log(results); // => ["a", "b"] cosnt keysResults = []; for (const value of set.keys()){ keysResults.push(value); } console.log(keysResults); const entryResults = []; for (const entry of set.entries()) { entryResults.push(entry); } console.log(entryResults); // => [["a", "a"], ["b", "b"]]
感想
- 非同期処理に関してなんとなく知っている程度であったが、本書を読んで細かい動き等を理解することができた
- 文法に関してもとても細かい部分まで解説されており、より理解を深めることができた
- 第2章で実際にJavaScriptを使ってTodoリストの作成を行い、JavaScriptの実践的な使い方をイメージすることができ良かったと感じた
参考
付録
第2章で使用したDockerfileならびにdocker-compose.yml
Dockerfile
FROM node:lts WORKDIR /usr/src/app
docker-compose.yml
version: "3.9" services: app: container_name: app build: context: . dockerfile: Dockerfile volumes: - type: bind source: ./todoapp target: /usr/src/app command: npx --yes @js-primer/local-server ports: - "3000:3000" tty: true stdin_open: true
Ruby on Rails5 速習実践ガイドを読んでの感想
はじめに
Ruby on Railsの学習のためにRuby on Rails5 速習実践ガイドという本を読んでみました。 この本で学んだことや感想をまとめていきたいと思います。
各章の感想
Chapter1 RailsのためのRuby入門
この章では、Rubyの基本的な文法について学習することができました!
nilガード
number ||= 10
上記コードはnumberに値が設定されていればnumberを、numberがnilであれば10を代入するという意味です。
→変数にnilが入っているかもしれない場面で、nilの代わりにデフォルト値を設定するのに役立ちます。
ぼっち演算子
&.
という演算子を用いてメソッドを呼び出すと、レシーバーがnilであった場合でもエラーが発生しなくなります。
&.
の正式名称は「safe navigation operator」であり、&.
の形がひとりぼっちで座っている形に似ていることから「ぼっち演算子」と呼ばれています。
Chapter2 Railsアプリケーションをのぞいてみよう
この章ではRailsアプリケーションの構造について学習することができました!
MVC(model・View・Controller)
UIに関わる部分(ビュー)とアプリケーション固有のデータ処理の扱いの部分(モデル)、モデルやビューを総合的に制御する部分(コントローラ)の 3つに役割を分けて管理しやすくする。
ヘルパーメソッド
ビューで利用可能な便利な機能のこと。
MVCの関係性
MVCの関係性は以下の通り。
Chapter3 タスク管理アプリケーションを作ろう
この章では、コントローラとビューの関係性について学習することができました!
コントローラとビュー
CRUDのアクション設定は以下のように設定を行う。
URL例 | HTTPメソッド | アクション名 | 機能名 | 役割 |
---|---|---|---|---|
/tasks | GET | index | 一覧表示 | 全タスクを表示する |
/tasks/3 | GET | show | 詳細表示 | 特定のidのタスクを表示する |
/tasks/new | GET | new | 新規登録画面 | 新規登録画面を表示する |
/tasks | POST | create | 登録 | 登録処理を行う |
/tasks/3/edit | GET | edit | 編集画面 | 編集画面を表示する |
/tasks/3 | PATCH,PUT | update | 更新 | 更新処理を行う |
/tasks/3 | DELETE | destroy | 削除 | 削除処理を行う |
レンダー(render):アクションに続けてビューを表示させること
リダイレクト(redirect):アクションを処理した直後にビューを表示せず、別のURLへ案内する動き
Flashメッセージ:主にリダイレクトの際に次のリクエストに対して、データを伝えるために用意されている仕組み
Chaper4 現実の複雑さに対応する
この章では、主にモデルのデータ制約や状態制御に関して学ぶことができました!
マイグレーション
1つのマイグレーションは1つのバージョンとして扱われる。
マイグレーションはバージョンを上げるだけでなく、適用を取り消すこともできるため、
バージョンの上げ下げを意識する必要がある
データ内容の制限
制約 | railsでの記方法 |
---|---|
NOT NULL | null: false |
文字カラムの長さ指定 | limit: 文字数 |
一意制約 | unique: true |
コールバック
登録や削除などの重要なイベントの前後に任意の処理を挟む仕組み。
コールバックの種類 | 代表的な使い道 |
---|---|
before_validation | 検証前の値の正規化 |
after_validation | 検証結果(エラーメッセージ)の加工 |
before_save before_create before_update |
saveのために裏側で行いたいデータ準備を行う。 検証エラーを出してもユーザーにはどうすることもできない状態異常を防ぐために例外を出す |
after_save after_create after_update |
そのモデルの状態に応じて他のモデルの状態を変えるなど連動した挙動を実現する 検証エラーを出してもユーザーにはどうすることもできない状態異常を防ぐために例外を出す |
before_destroy | 削除してOKかをチェックし、ダメなら例外を出すなどして防ぐ |
after_destroy | そのモデルの削除に応じて他のモデルの状態を変えるといった連動した挙動を実現する |
セッションとCookie
セッション:
サーバ側にセッションという仕組みを用意して、1つのブラウザから連続して送られている
一連のリクエストの間で、状態を共有できるようにする。
Cookie:
複数のリクエスト間で共有したい状態をブラウザ側に保持する仕組み。
Railsではセッションは以下のようにCookieによってやりとりされるセッションIDをキーにして保管される。
Chapter5 テストをはじめよう
この章では、テストのやり方を学ぶことができました!
テストを書くメリット
テストを書くメリットは以下の7点。
- テスト全体にかかるコストの削減できる
- 変更をフットワーク軽く行えるようになる
- 環境のバージョンアップやリファクタリングを行いやすくする
- 仕様を記述したドキュメントとしても機能する
- 仕様やインターフェイスを深く考えるきっかけとなる
- 適切な粒度のコードになりやすい
- 確実性が高められ、開発効率を上げることができる
テスト用ライブラリ
RSpec
BDD(Behaviour-Driven Development 振舞駆動開発)のためのデスティングフレームワーク。
動く仕様書として自動テストを書くという発想で作られており、要求仕様をドキュメントに記述するような、感覚でテストケースを記述可能。
Capybara
WebアプリケーションのE2E(End to End)テスト用フレームワーク。
FactoryBot
テスト用データの作成をサポートするgemでテスト用データを簡単に用意し、テストから呼び出して利用することが可能。
Chapter6 Railsの全体像を理解する
この章では、ルーティングに関して学ぶことができました!
ルーティングの構成
ルートは以下の5要素で構成されている。
要素名 | 説明 |
---|---|
HTTPメソッド | サーバーへのリクエストの際に指定する。情報の送信・取得の方法を表現する。 |
URLパターン | URLそのものや、 :id のように一部に任意文字が入るようなパターンを指定する。 |
URLパターン名 | 定義したULRパターンごとに一意な名前をつける。 |
コントローラ | 呼び出したいアクションのコントローラクラスを指定する。 |
アクション | 呼び出したいアクションを指定する。 |
図で表現すると以下の通り。
URLパターンに名前をつけ、その名前をもとにURLを簡単に生成するためのURLヘルパーメソッドを作成する。
routes.rbの構造化
メソッド | 説明 |
---|---|
scope | URLパターン名のプリフィクスなどをオプションに指定することで、ブロック内の定義をまとめて制約をかける |
namespace | URL階層、モジュール、URLパターン名に一括で一定の制約をかける |
controller | コントローラを指定する |
国際化
複数の言語を切り替えるためのステップは以下の3ステップです。
セキュリティ強化
Strong Parameters
Strong Paramerters:想定通りのパラメータかどうかをホワイトリスト方式でチェックする機能
params.require(:task).permit(:name, :description)
上記赤字部分は、paramsの:task
の中身を要求し、(なければ例外を出す)
青字部分は、params[:task]から:name
と:description
を取り出すことを表します。
CSRF対策
CSRF(Cross-site request Forgery):
別のウェブサイト上に用意したコンテンツのリンクを踏んだり、
画像を表示したことをきっかけに、ユーザーがログインしているWebアプリケーションに悪意ある操作を行う攻撃。
対策:
同じWebアプリケーションから生じたリクエストであることを証明するためのセキュリティトークンを発行し、
照合することで対策する。
Chapter7 機能を追加してみよう
この章では、機能追加方法について学ぶことができました!
登録や編集の実行前に確認画面をはさむ
画面の遷移は下図の通りです。
手順は以下の通りです。
- 確認画面を表示するアクションを追加する
- 新規登録画面からの遷移先を変える
- 登録アクションで戻るボタンからの遷移に対応する
一覧画面に検索機能を追加する
検索機能を追加する手順は以下の通りです。
- Ransackをインストールする
- ransackメソッドを用いて、検索機能を追加する
- ransackの提供するヘルパー
search_form_for
を用いて、検索用フォームを作成する
メールを送る
メールを送信するためにAction Mailer
という仕組みがある。
追加手順は以下の通り。
- ジェネレータを用いてメーラーを作成する(rails g mailer TaskMailler)
- 通知用メールを送るためのメソッドを追加する
- メールのテンプレートを実装する
- 用意したメーラーをコントローラから呼び出して、実際にメールを送るよう変更する
Chapter8 RailsとJavaScript
この章では、RailsとJavaScriptの関係について学ぶことができました!
AjxaとSJR
Ajax(Asynchronous JavaScript And XML):
Webブラウザ上で非同期通信を行い、ページの再読み込みなしにページを更新する
SJR(Server-generated JavaScript Response):
サーバサイドで生成したJavaScriptからなるレスポンスのこと
Turbolinks
a要素のクリックイベントをフックとして遷移先のページをAjaxで取得する。
取得したページが要求するJavaScriptやCSSが現在のものと同一であれば現在のものをそのまま使用し、
title要素やbody要素のみを置き換える。
→リクエストごとにJavascriptやCSSをブラウザが評価しなくなるため、パフォーマンスが向上する。
注意点
- ブラウザのページ遷移が発生しないことが多くなる
<script>
はhead要素内に記述する
JavaScript管理
Yarn:Facebook社によって開発されたJavaScriptのパッケージマネージャ
Webpacker:
JavaScriptのビルドツール「Webpack」のラッパーであり、RailsアプリケーションでWebpackを使ってJavaScriptを管理することを簡単にしてくれるGem
Chapter9 複数人でRailsアプリケーションを開発する
この章では、複数人開発でのアプリケーション管理について学ぶことができました!
マイグレーションの管理における注意事項
スキーマキャッシュ
スキーマキャッシュ:
テーブルのスキーマを毎回データベースに問い合わせにいくことによるパフォーマンス低下を防ぐための機能。
スキーマキャッシュが古いと反映されず、古いままの値が出力される場合があるので、
手動でキャッシュを更新しなければならないケースがある。
Chapter10 Railsアプリケーションと長く付き合うために
この章では、バージョンアップやリファクタリングについて学ぶことができました!
バージョンアップ
バージョンアップ時に検討すべき内容は以下の通りである。 - 自動テストは十分か - 動作確認方法 - リリース方法 - リリース後の動作確認と監視方法 - 万が一のときのロールバック方法
複雑性への対応
複雑性へ対応するには以下の3つの鍵ある。
しかるべきところにコードを書く
→モデルに書くべきコード(ビジネスロジックなど)をモデルに寄せる上手に共通化する
→共通機能のモジュールをMix-inしたり、継承や抽象を用いて共通化を行う
上記「しかるべきところにコードを書く」を行ってから共通化を行う必要あり新しい構造を追加して役割分担をする
→共通処理を担当するオブジェクトを作成したり、外部サービスのロジックをオブジェクトに閉じ込めることで役割を分担する
全体の感想
- Rubyの言語仕様から実際の運用まで説明されており、実際のアプリケーションの流れをイメージすることができた
- 随所にコード例やモデル図などが用いられており、比較的理解しやすかった
- Ruby on Railsのことが広く網羅されているので辞書のような形で使用するのもありだなと感じた
参考
達人に学ぶDB設計徹底指南書を読んだ感想
はじめに
今回は達人に学ぶDB設計徹底指南書を読んだ感想をまとめていきたいと思います。
DB設計未経験の自分でも理解しやすい本でした!
各章の感想
第1章 データベースを制する者はシステムを制す
この章では、システム開発の設計工程とデータベースについて学ぶことが出来ました!
DOAとPOA
DOA:データ中心アプローチ(Data Oriented Approach)
POA:プロセス中心アプローチ(Process Oriented Approach)
最近では、プログラム設計に先立ってデータ設計が優先されるDOAが中心となっている
3層スキーマ
外部スキーマ:ユーザーから見てデータベースがどのような機能とインターフェイスを持っているか定義する
概念スキーマ:データベースに保持するデータ要素及びデータ同士の関連を記述する
内部スキーマ:論理データモデルを具体的にどのようにDBMS内部に格納するかを定義する
概念スキーマの存在意義
→外部スキーマと内部スキーマ間で互いに影響し合わないよう、データの独立性を保証する
設計の工程や流れについて詳しく説明されており良いと思いました!
第2章 内部スキーマと物理設計
この章では、物理設計やバックアップについて学ぶことが出来ました!
RAID
RAID:システムの信頼性と性能を共に改善する技術
種類 | 内容 |
---|---|
RAID0(ストライピング) | データを異なるディスクに分散して保持する |
RAID1(ミラーリング) | 2本のディスクに同じデータを保持する |
RAID5(パリティ分散) | 最低3本で構成し、データとパリティ(誤り符号訂正符号)を分散して格納する |
RAID10 | RAID1とRAID0を組み合わせたもの。必要になるディスク数が多くなるためコストが高くなる |
データベースのRAIDは最低限RAID5で構成する。余裕があればRAID10を選択する
バックアップ
フルバックアップ
ある時点でシステムの保持されているすべてのデータをバックアップする方式
利点
欠点
- バックアップ時間が長い
- ハードウェアリソースへの負荷が高い
- サービス停止が必要
差分バックアップ
フルバックアップからの変更分をバックアップする方式
利点
欠点
増分バックアップ
前回おこなわれたバックアップからの変更分をバックアップする方式
利点
- バックアップファイルの大きさが小さくなる
- バックアップ時間を短くできる
欠点
- リカバリ時に全ファイルが必要となる
一般的には、「フルバックアップ+差分バックアップ」または
「フルバックアップ+増分バックアップ」のバックアップ方式をとる
表や図を用いて説明されており、とても理解がしやすかったです!
第3章 論理設計と正規化
この章では、テーブルの構成要素やテーブルの正規化について学ぶことが出来ました!
キー
主キー(プライマリキー):レコードを一意に識別するためのキー
外部キー:二つのテーブル間の列同士で設定するもの
正規化
第一正規形
一つのセルの中には一つの値しか含まない(スカラ値の原則)
主キーは一部であってもNULLを含んではいけない
第二正規形
テーブル内で部分関数従属を解消し、完全関数従属のみのテーブルを作る
部分関数従属;主キーの一部の列に対して従属する列があること
完全関数従属:主キーを構成するすべての列に従属性があること
第三正規形
テーブル内の推移的関数従属を解消する
推移的関数従属:テーブル内部に存在する段階的な従属関係
ボイスーコッド正規形
第三正規形をより厳密にしたもの
すべての列が主キーに完全関数従属で、他に完全関数従属関係がないもの
第四正規形
関連エンティティに含まれる関連を一つだけにする
第五正規形
関連がある場合は、対応する関連エンティティを作成する
SQLの本では出てこなかった、第四正規形や第五正規形についても説明されており
正規形についてより詳しく学ぶことが出来ました!
第4章 ER図〜複数のテーブルの関係を表現する
この章では、ER図の描き方を学びました!
ER図(Entity-Relationship Diagram):実体関連図
IE表記法
図 | 内容 |
---|---|
◯ | 0 |
ー | 1 |
多 |
IDEF1X表記法
図 | 内容 |
---|---|
0以上 | |
1以上 | |
0または1| | |
n(特定の定数) |
多対多エンティティの関係にならないよう、関連実体を用いる
第5章 論理設計とパフォーマンス〜正規化の欠点と非正規化
この章では、正規化とパフォーマンスの関係について学ぶことが出来ました!
正規化の功罪
正規化と検索SQLのパフォーマンスはトレードオフの関係にある
正規化→データの不整合が起きにくいが、パフォーマンスが落ちる
非正規化→データの不整合が起きやすいが、パフォーマンスは上がる
非正規化とパフォーマンス
サマリデータや選択条件を保持すると、正規形に違反する形となるが、検索を高速化可能
→ただし、リアルタイム性という面では問題となる
正規形とパフォーマンスのバランスを判断するのが難しいと感じました!
第6章 データベースとパフォーマンス
この章では、データベースのインデックスと統計情報について学ぶことが出来ました!
インデックス設計
インデックスはパフォーマンス改善にポピュラーな手段。理由は以下の3点。
- アプリケーションのコードへ影響を与えない(アプリケーション透過的)
- テーブルのデータに影響を与えない(データ透過的)
- 性能改善効果が大きい
B-treeインデックスの設計指針
設計指針
- 大規模(レコード数が1万件以上)のテーブルに対して作成
- カーディナリティ(種類の多さ)の高い列に対して作成
- SQL文のWHERE句や結合条件に使用されている列に対して作成
統計情報の設計指針
設計指針
- データが大きく更新された後、なるべく早く更新する
- 統計情報の収集は、使用者の少ない夜間帯に実施する
第7章 論理設計のバッドノウハウ
この章では、やってはいけないデータベース設計について学ぶことが出来ました!
配列型による非スカラ値
- アプリケーションやミドルウェアも配列型に対応しなければならないためハードルが高い
- 第一正規形を優先する
ダブルネーミング
- 列は変数でないため、一度意味を決めると変更が不可
単一参照テーブル
- コード体系によって列長が違うため、余裕を見た大きめの可変長文字列型にする必要がある
- レコード数が多くなり、検索パフォーマンスが悪化する
- コードタイプやコード値を間違えて指定してもエラーにならずバグに気づきにくい
→テーブルにポリモーフィズムは不要
テーブル分割
- 分割する理由がない
- 拡張性に乏しい
- 他の代替手段がある(パーティション等)
不適切なキー
- 可変長文字列は普遍性がないためキーには向かない
- キーには固定長文字列のコード列が望ましい
ダブルマスタ
ダブルマスタ:同じ役割を果たすはずのマスタテーブルが2つ存在すること
2つのマスタテーブルを結合する必要があるため、パフォーマンスが落ちる
バッドノウハウを学ぶことで、どのような設計をすべきかをより理解することができたと感じました!
第8章 論理設計のグレーノウハウ
この章では、データベース設計におけるグレーノウハウについて学ぶことが出来ました!
オートナンバリング
主キーを一意に決められない場合に、オートナンバリングをキーとして扱う場合には注意が必要
- シーケンスとID列では、シーケンスの方が柔軟で拡張性に富む
- アプリケーション側でオートナンバリングを実装すると、排他制御等の考慮が必要となるので複雑となる
列持ちテーブル
利点
- シンプルな設計
- 入出力のフォーマットと合わせやすい
欠点
- 列の増減が難しい
- 無用のNULLを使用しなければならない
多段ビュー
- ビューはビュー定義のSELECT文を実行してオリジナルのテーブルにアクセスしている
- 多段になるとパフォーマンスが落ち、また仕様が複雑となり管理が困難になる
第9章 一歩進んだ論理設計〜SQLで木構造を扱う
この章では、木構造の論理設計手段について学ぶことが出来ました!
隣接リストモデル
ノードのレコードに親ノードの情報を持たせるモデル
特徴
- 木構造を表現する最も古くポピュラーな方法
- 更新や検索のクエリが複雑となり、パフォーマンスが悪い
入れ子集合モデル
ノードを円とみなし、ノード感の階層関係を円の包含関係によって表す
特徴
- 「左端」と「右端」の列によって階層関係を表す
- 親は自分の腹の中に子を抱え込む格好になる
- 更新処理はパフォーマンスが問題となる
入れ子区間モデル
円の左端と右端の座標値として取れる範囲を十数まで広げた集合モデル
特徴
- 理論上無限に追加可能
- 追加ノードの左端座標:
(plft * 2 + prgt) / 3
- 追加ノードの右端座標:
(plft + prgt * 2) / 3
経路列挙モデル
ノードをディレクトリとみなし、各ノードまでの経路を記述する
特徴
- ノード地震に親子関係が含まれているため、経路探索が簡単
- ユニークインデックスによる高速検索が可能
- 経路に主キーを使うと、文字列が長大となる恐れがある
- 経路に主キーを使うと、同階層内のノード同士の順序が把握できない
- パスに番号を使うと、ノードの削除、追加等の更新が複雑になる
全体の感想
- 具体例を用いて説明されており、とても理解がしやすかった
- アンチパターンも説明されているので、やらない方がいい設計への知見が得られ勉強になった
- パフォーマンス等の現実的な問題にも触れられており、より実務に生かしやすいと感じた
参考
スッキリわかるSQL入門を読んだ感想
はじめに
今回はスッキリわかるSQL入門を読んだ感想をまとめてみたいと思います。
この本はSQL初学者の自分でも、簡単に理解でき読み進めることが出来ました!
では、早速感想をまとめていきたいと思います。
各章の感想
第1章 はじめてのSQL
この章で、データベース
やSQL
の概要を知ることが出来ました!
データベース:狭義では、データ管理を目的とした情報そのもの
リレーショナルデータベース:複数の表形式でデータを管理するデータベース
SQL:データベースを操作する専用の言語
またdokoQL
を使用して、実際にデータベースを操作することができるので、
よりデータベースやSQLについてイメージしやすくなっている点がいいと感じました!
第2章 基本文法と4大命令
この章で、SQLの4大命令の記述方法知ることが出来ました!
4大命令
内容 | 命令 |
---|---|
データを抽出 | SELECT カラム名 FROM テーブル名 WHERE 条件式 |
データを挿入 | INSERT INTO テーブル名 VALUES (データ1 , データ2, データ3 ) |
データを更新 | UPDATE テーブル名 SET カラム名 = データ WHERE 条件式 |
データを削除 | DELETE FROM テーブル名 WHERE 条件式 |
4大命令の分類や共通点についての説明もあり、各命令の記述方法を混乱せずに覚えることが出来ました!
第3章 操作する行の絞り込み
この章で、SQLの条件式の書き方について知ることができました!
NULLの判定
演算子 | 意味 |
---|---|
IS NULL | NULLであることを判定する |
IS NOT NULL | NULLでないことを判定する |
パターンマッチング
式 LIKE 文字列
:文字列に一致するか判定する
文字 | 意味 |
---|---|
% | 任意の0以上の文字列 |
_ | 任意の1文字 |
IN,ANY,ALL演算子
演算子 | 意味 |
---|---|
IN | 複数の値のいずれかのデータにマッチするか判定する |
ANY | リストと大小比較し、いずれかが真であるか判定する |
ALL | リストと大小比較し、全てが真であるか判定する |
第4章 検索結果の絞り込み
この章で、検索結果の加工方法やデータの集合方法について知ることができました!
加工内容 | 内容 |
---|---|
DISTINCT | 重複行を場外する |
ORDER BY | 結果を並び替える |
OFFSET - FETCH | 行を指定して取得する |
UNION | 和集合をとる |
EXCEPT | 差集合をとる |
INTERSECT | 積集合をとる |
データベースの種類の違いによって記述方法が異なる点も取り上げられており、
実際使用する際にも役立つ知識を得ることができよかったと感じました!
第5章 式と関数
この章で、データベースの関数について知ることが出来ました!
関数 | 内容 |
---|---|
LENGTH | 文字列の長さを取得できる |
TRIM | 左右の空白を除去した文字列を取得できる |
REPLACE | 文字列の一部を別の文字列に置換する |
SUBSTRING | 文字列の一部を抽出する |
CONCAT | 文字列を連結する |
ROUND | 指定桁で四捨五入する |
TRUNC | 指定桁で切り捨てる |
POWER | べき乗を計算する |
COALESCE | 最初に登場するNULLでない値を返す |
COALESCE
はNULLの場合の代替値で使用するといった実践的な内容が載っており、
とても勉強になりました!
第6章 集計とグループ化
この章で、集計関数の使い方とグループ化の方法について知ることが出来ました!
集計関数
関数 | 内容 |
---|---|
SUM | データの合計 |
MAX | 最大値を取得 |
MIN | 最小値を取得 |
AVG | データの平均を求める |
COUNT | 行数をカウントする |
グループ化
GROUP BY
にグループ分けをするカラム名を指定することでグループ別に集計可能HAVING
で集計処理を行ったあとの結果表に対して絞り込みを行うことが可能
WHERE句で絞り込みを行える場合は、WHERE句で絞り込んだ方が
パフォーマンスが向上するといった、処理速度に関しての記載もあり、
とても勉強になりました。
第7章 副問い合わせ
この章で、副問い合わせについて知ることが出来ました!
SQL文のネスト
副問い合わせ(サブクエリ):SQL文の中に別のSELECT分を記述すること
副問い合わせの種類
単一行副問い合わせ:副問い合わせの結果が1行1列になるもの
複数行副問い合わせ:副問い合わせの結果がn行1 列になるもの
表形式副問い合わせ:副問い合わせの結果がn行m列の表形式になるもの
第8章 複数テーブルの結合
この章では、テーブルの結合方法を知ることが出来ました!
リレーションシップ
リレーションシップ:2つのテーブルの間に情報として関連がある場合、その関連を指す 外部キー:リレーションシップを結ぶ役割を担う列のこと
テーブルが分割されていた方がデータを安全かつ確実、高速に取り扱いやすい。
結合
結合を使用することで、複数のテーブルに格納された関連データを
1つの結果表として取り出せる
JOIN - ON:テーブルを結合(結合相手がいない場合は消滅)
LEFT JOIN - ON:左表については結合相手が見つからなくても出力してテーブルを結合
RIGHT JOIN - ON:右表については結合相手が見つからなくても出力してテーブルを結合
結合の部分は理解するのが難しいと感じたが、図等の豊富な説明のおかげで、 理解することができました!
第9章 トランザクション
この章で、SQLのトランザクションについて知ることが出来ました!
トランザクション
トランザクション:複数のSQL文を不可分な1つ命令として扱うことが可能
原子性
指示 | 内容 |
---|---|
BEGIN | 開始の指示 |
COMMIT | 終了の指示。変更を確定する。 |
ROLLBACK | 開始の指示。変更の取り消しをする。 |
分離性
トランザクションは同時実行中のほかのトランザクションからの影響を受けないよう
分離して実行される。
副作用 | 内容 |
---|---|
ダーティーリード | 未確定の変更を他の人が読めてしまう副作用 |
反復不能読み取り | SELECT文を実行した後に他の人がUPDATE文でデータを書き換えると次のSELECT文実行時に検索結果が異なる副作用 |
ファントムリード | 1回目と2回目のSELECT文の間に他の人がINSERT分で行追加をすると1回目と2回目で取得する結果の行数が変わる副作用 |
ロック
行や表、データベース全体に明示的にロックをかけることが可能
行ロック:SELECT ~ FOR UPDATE (NOWAIT)
表ロック:LOCK TABLE テーブル名 IN モード名 MODE (NOWAIT)
デッドロック:複数のトランザクションが実行されると、トランザクションの処理が
永久に止まってしまう現象
副作用とその対策までしっかりと説明されており、
読んでいてとても理解しやすいと感じました!
第10章 テーブル作成
この章では、テーブルの作成や削除方法について知ることが出来ました!
テーブルの作成と削除
命令 | 内容 |
---|---|
CREATE TABLE | 新規テーブルを作成 |
DROP TABLE | テーブルを削除 |
ALTER TABLE | テーブルの定義を変更 |
制約
テーブル作成時に各列に制約を設定し、
予期しない値が格納されないように安全装置を設けることが可能
制約 | 内容 |
---|---|
NOT NULL | NULLの格納を防止 |
UNIQUE | 重複した値の格納を防ぐ |
CHECK | 格納しようとする値が妥当がどうか確認する |
PRIMARY KEY | 主キー制約 |
REFERENCES | 外部キー制約 |
テーブルにNULLが入ると扱いにくくなる点もあるので、
NOT NULL
制約やDEFALT
設定を用いることで、
NULLが入らないようにテーブル設計できるのは良いと感じました。
第11章 さまざまな支援機能
この章では、ビューやバックアップについて知ることが出来ました!
ビュー
- SELECT文の結果表を仮想テーブルとして扱うことが可能
- ビューによりSQL文はシンプルとなるが、実際はSELECT文を実行しているのと同じであるため、負荷の大きさは変わらない
バックアップ
正確なデータ処理にはACID特性が求められる。
データベースの内容だけでなく、ログファイルもバックアップしておくことで、
障害時にロールフォワードによって障害発生直前までのデータを復元することが可能
種類 | 内容 |
---|---|
原子性 | 処理が中断しても中途半端な状態にならない |
一貫性 | データの内容が矛盾した状態にならない |
分離性 | 複数の処理を同時実行しても副作用がない |
永続性 | 記録した情報は消滅せず保持され続ける |
ロールバックとロールフォワードは名前が似ているが、
処理内容は全く違うので、混同しないよう正しく理解したいと感じました。
第12章 テーブルの設計
この章では、データベース設計方法について知ることが出来ました!
データベース設計の流れ
正規化
- 第1正規化
「1対多」を形成する概念は別テーブルとして設計する - 第2正規化
複合主キーの一部に関数従属する部分を別テーブルに分割する - 第3正規化
主キーに対して間接的に関数従属する部分を別テーブルに分割する
データベース設計の手順に関して、具体的な例を用いて説明されており、
とてもわかりやすいと感じました!
また、第2正規化と第3正規化に関しては行うことはあまり大差ないと感じました。
全体の感想
良かったと感じた点
- 図や具体例を用いて丁寧に解説されており、難しい内容でもスムーズに理解できるよう工夫されていて読みやすかったです
dokoQL
を用いることで手軽にSQL文を実行できため、イメージが湧きやすいと感じました- 問題も豊富にあり、インプットした内容をすぐにアウトプットできるため、記憶に定着しやすいと感じました
改善してほしい点
- 付録のドリルの解答をつけてほしいと思いました(ネットで見ることは可能だが手間になるため)
難しいと感じた点
- 複数のテーブルの結合や、副問い合わせが入ってくると複雑になり、少し理解するのに苦労した部分がありました
- テーブルの設計のモデリングに関しては慣れていないこともあり、難しいと感じました
参考
「プロを目指す人のためのRuby入門」を読んだ感想
はじめに
Rubyの勉強のために、プロを目指す人のためのRuby入門
という本を読んでみました。
難しい内容も多く、読むのに時間がかかりましたが、
本を読んでの感想を「良かったと思う点」、「学んだ点」、「難しいと思った点」の
3点に分けて、まとめていきたいと思います。
感想
良かったと思う点
「プロを目指す人のためのRuby入門」を読んで良かったと思った点は以下の3点です。
- サンブルコードが多い
- 幅広く網羅されている
- テスト(駆動)について触れられている
1. サンプルコードが多い
各説明でサンブルコードが多く用いられており、理解がしやすいと感じました。
また、具体的な使用例が書かれていることで、
実際使用する際に応用しやすいのではないかと感じました。
2. 幅広く網羅されている
入門書等だと基本的な文法にしか触れられていないケースが多いですが、
この書籍では後述するテストやライブラリに関しても触れられており、
幅広く網羅されている印象を持ちました。
3.テスト(駆動)について触れられている
実際ソースコードを書いてテストを行う場面が多いですが、
テストの使い方・考え方の説明が具体的にされており、
とても実用的だと感じました。
学んだ点
書ききれないほど、沢山学ばさせていただきましたが、
ここでは特に印象に残った内容についてまとめていきたいと思います。
%記法について
Rubyには%記法
と呼ばれるものがあります。
他言語ではあまり見ない構文であるため、本を読んでいて印象に残りました。
具体的には以下の通りです。
%q!文字列 !
→'文字列'
(シングルクォートで囲んだものと同等)%Q!文字列!
,%! 文字列 !
→"文字列"
(ダブルクォートで囲んだものと同等)%s!文字列 !
→:文字列
(シンボルと同等)%i(文字列1 文字列2 文字列3) →
[:文字列1, :文字列2, :文字列3] (シンボルの配列と同等)%w!文字列1 文字列2 文字列3!
→ `["文字列1", "文字列2", "文字列3"] (配列と同等)%x{文字列}
→`文字列`
(バッククォートと同等)
範囲を表すオブジェクトについて
Rubyには値の範囲を表すオブジェクトがあります。
以下のように記述します。
最初の値..最後の値(最後の値を含む)
最初の値...最後の値(最後の値を含まない)
可変長引数について
メソッドの引数名の前に*
をつけることで可変長引数となります。
可変長引数は配列として受け取ることができます。
def メソッド名(*可変長引数名) メソッド処理 end
クラスメソッドの定義について
クラスについては、他言語とそこまで大きく差異はないと感じましたが、
クラスメソッドの定義は少し違うかなと感じましたので、
簡単にまとめておきたいと思います。
Rubyでクラスメソッドを定義するには以下のように記述します。
class クラス名 def self.クラスメソッド名 クラスメソッドの処理 end end
また、以下のように記述してもクラスメソッドを定義することができます。
class クラス名 class << self def クラスメソッド名 クラスメソッドの処理 end end end
モジュールについて
モジュールの用途としては以下の5点が挙げられます。
- 継承を使わずにインスタンスメソッドを追加する
- 複数のクラスに対して共通の特異メソッドを追加する
- クラス名や定数名の衝突を防ぐために名前空間を作る
- 関数メソッドを定義する
- シングルトンオブジェクトのように扱って設定値などを保存する
モジュールの定義方法は以下の通りです。
module モジュール名 モジュールの定義 end
また、ミックスイン(モジュールをクラスにincludeする)を使用することで、
多重継承のような形を実現することも可能です。
例外処理について
他言語では、try-catch
を使用して例外処理を定義するものが多いですが、
Rubyで例外処理を定義するには、begin-rescure-end
を使用します。
begin 例外が起きうる処理 rescue 例外が発生した場合の処理 end
難しいと思った点
本を読んで、Rubyで難しいと思った点は以下の2点です。
- 一貫性がない点
- Ruby独特な書き方がある点
一貫性がない点
エイリアスがあったり、別の書き方でも同じ動きを実現できたり自由度が高い反面、
個人によって書き方がばらつく恐れがあるので、読みづらく感じてしまうのではと思いました。
Ruby独特な書き方がある点
例外処理の書き方等、他言語と書き方が違う部分がしばしば見受けられたので、
その点は慣れが必要だなと感じました。
終わりに
Rubyについて深く学ぶことができ良かったと感じました。
少し長くなってしまいましたが、感想は以上となります。
ここまで記事をご覧いただきありがとうございました。
参考
プロを目指す人のためのRuby入門[改訂2版] 言語仕様からテスト駆動開発・デバッグ技法まで (Software Design plus) | 伊藤 淳一 |本 | 通販 | Amazon
HTTPの勉強中に出てきた用語まとめ
はじめに
HTTP通信の勉強をしていて、よく出てきた用語についてまとめたいと思います。
用語まとめ
ステートレス
「ステート」→「状態」、「レス」→「無い」 ということなので、
直訳すると「状態が無い」という意味になります。
もう少し説明を加えると、
状態を保持せず、入力の内容によって出力が決まるものを指します。
もちろん同じ入力の場合は、出力は常に同じとなります。
HTTPは状態を保持しないため、こちらのステートレスなプロトコルということになります。
ステートフル
ステートレスとは反対で、「フル」→「満ちている」ということなので、
直訳すると「状態を持つ」という意味になります。
もう少し説明を加えると、
状態を保持し、同じ入力でも状態によって
出力が変わる可能性のあるものを指します。
FTPやTCP、SSH等は状態を保持するため、こちらのステートフルなプロトコルということになります。
リクエスト
クライアント(Webブラウザ)からWebサーバへ要求を伝えるメッセージを指します。
具体的には、Webコンテンツを表示するために必要なファイル(HTML等)を要求するのに使用します。
レスポンス
Webサーバがクライアント(Webブラウザ)へ応答を伝えるメッセージを指します。
具体的には、ステータスコードと呼ばれる要求に対する応答の種類を表す番号や、
クライアントから要求されていたファイル等を返答するのに使用します。
代表的なステータスコードは下記表の通りです。
ステータスコード | 意味 | 内容 |
---|---|---|
200 | OK | リクエストが成功 |
302 | Found | リクエストしたリソースが一時的に移動している |
401 | Unauthorized | 認証が必要 |
403 | Forbidden | アクセスが拒否されている |
404 | Not Found | ページが見つからない |
500 | Internal Server Error | サーバ内部エラー |
クッキー
HTTPの仕様を拡張し、クライアント(Webブラウザ)とWebサーバ間で
状態を維持・管理する仕組みで、その通信の際にWebブラウザに保存された情報を指します。
WebサーバからWebブラウザへHTTPレスポンスのヘッダを利用してクッキーを送り、
そのクッキーをHTTPリクエスト時に一緒に送ることで、ユーザ識別や状態管理を行うことが可能となります。
プロトコル
データのやり取りするために定められた手順や規約を指します。
HTTPやFTP等は通信を行うための規約であるため、
通信プロトコルと呼ばれています。
ポート番号
インターネット上の通信において、複数の相手と同時接続するために
IPアドレスの下に用意されたサブアドレスのことを指します。
各プロトコルのポート番号のデフォルト値は下記表のように設定されています。
プロトコル | ポート番号 |
---|---|
FTP | 20,21 |
SSH | 22 |
Telnet | 23 |
SMTP | 25 |
DNS | 53 |
HTTP | 80 |
POP3 | 110 |
HTTPS | 443 |
終わりに
いかがだったでしょうか。
自分自身知っていた用語も多くありましたが、
改めて用語の意味を調べてみることで、より理解を深めることができたと感じました。
皆さんも普段使っている用語について調べてみてはいかがでしょうか。
参考文献
Go言語ポインタまとめ【備忘録】
はじめに
Go言語の文法について調べていたら、ポインタというものが気になりました。
そこで今回の記事ではGo言語のポインタについて、
備忘録を兼ねて簡単にまとめていきたいと思います。
事前知識
事前知識として、変数、メモリ、アドレスの関係について以下にまとめていきたいと思います。
- コンピュータにはメモリと呼ばれるデータを記憶する領域がある
- メモリは1バイト毎に番号が付けられ、区別されている
- メモリに付けられている番号がアドレスとなる
- 変数のデータはメモリ上に格納され、データが保持される
要はデータを保管するための箱がメモリであり、
箱を区別するための一意の番号がアドレス、
箱の中身が変数のデータといった具合です。
ポインタについて
ポインタとは
それでは、本題のポインタについて説明していきます。
単刀直入に言うと、ポインタはメモリのアドレスのことを指します。
アドレスは16進数で表されます。
ポインタの取得方法
- Go言語では変数の前に
&
をつけることで、変数のポインタを取得することができます。 - 取得したポインタを変数へ代入するには、ポインタ型変数を使用する必要があります。
*
を変数宣言時の型の前に付けることでポインタ型変数の宣言が可能です。
package main import "fmt" func main() { // int型変数ageに20を代入 var age int = 20 // int型へのポインタ型変数pointerに変数ageのアドレスを代入 var pointer *int = &age }
ポインタ型変数の中身へのアクセス方法
- ポインタ型変数の前に
*
を付けることで、ポインタ型変数の中身にアクセス可能です。 *
を付けずに出力すると、ポインタ(アドレス)が出力されます。
package main import "fmt" func main() { // int型変数ageに20を代入 var age int = 20 // int型へのポインタ型変数pointerに変数ageのアドレスを代入 var pointer *int = &age // 20が出力される fmt.Println(*pointer) // 0xc0000a0000が出力される fmt.Println(pointer) }
出力結果
20 0xc0000a2000
ポインタ型引数
- ポインタ型は引数に使用することも可能です。
package main import "fmt" func morningToNight(arg *string){ *arg = "こんばんは" } func main(){ // string型変数messageに"おはよう"を代入 var message string = "おはよう" // messageの中身"おはよう"が出力される fmt.Println(message) // messageのポインタを引数に渡し、メッセージを変更 morningToNight(&message) // messageの中身"こんばんは"が出力される fmt.Println(message) }
出力結果
おはよう こんばんは
ポインタと構造体について
構造体のポインタ
- int型やstring型といったプリミティブ型と同様に、
構造体もポインタ型を使用することができます。
package main import "fmt" type Person struct { name string age int } func incremerntAge(arg *Person){ arg.age++ } func main(){ student := Person{ name: "山田太郎", age: 20, } // {山田太郎 20}と出力される fmt.Println(student) // 構造体のポインタを渡し、年齢を変更する incremerntAge(&student) // {山田太郎 21}と出力される fmt.Println(student) }
出力結果
{山田太郎 20} {山田太郎 21}
studentのageの値が変更されましたね。
値渡しと参照渡し
ここまでポインタについてまとめてきました。
ここからは値渡しと参照渡しについて少し触れておきたいと思います。
- 値渡しは、変数の値(実体)をコピーして渡す方法です。
- 参照渡しは、変数のアドレスを渡す方法です。
package main import "fmt" type Person struct { name string age int } func incremerntAge1(arg Person){ arg.age++ } func incremerntAge2(arg *Person){ arg.age++ } func main(){ student := Person{ name: "山田太郎", age: 20, } // studentの値のコピーを渡す incremerntAge1(student) // 値をコピーして渡しているだけなので、student.ageの値は変わらない fmt.Println(student) // studentのポインタを渡す incremerntAge2(&student) // studentの実体を渡しているので、student.ageの値が変わる fmt.Println(student) }
出力結果
{山田太郎 20} {山田太郎 21}
値渡しは変数の値をコピーして渡しているだけなので、
student
の実体へアクセスすることはできません。
一方、参照渡しはstudent
のポインタを渡しているため、
ポインタを通じてstudent
の実体へアクセスすることができます。
※参考
Go言語では、レシーバと呼ばれる機構をもつ関数(メソッド)を作ることができます。
このレシーバをもつ関数(メソッド)にも値渡しと参照渡しがあるため、
使用する際には注意が必要です。
package main import "fmt" type Person struct { name string age int } func (p Person) changeAge1(arg int){ p.age = arg } func (p *Person) changeAge2(arg int){ p.age = arg } func main(){ student := Person{ name: "山田太郎", age: 20, } // studentの値のコピー渡す student.changeAge1(18) // 値をコピーして渡しているだけなので、student.ageの値は変わらない fmt.Println(student) // studentのポインタを渡す student.changeAge2(18) // studentの実体を渡しているので、student.ageの値が変わる fmt.Println(student) }
出力結果
{山田太郎 20} {山田太郎 18}
終わりに
いかがだったでしょうか?
今回の記事ではGo言語のポインタについてまとめてみました。
この記事を読んでポインタについて少しでも理解を深めていただければ幸いです。