JavaScript Primerを読んでの感想

はじめに

JavaScriptの文法の勉強にJavaScript Primerという本を読みました。
今回はJavaScript Primerを読んでの感想をまとめていきたいと思います。

学んだこと

JavaScriptECMAScript

ECMASctiptはどの実行環境でも共通な動作のみが定義されており、JavaScriptECMAScriptと実行環境の固有機能を含んだものを表す。

変数と宣言

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.hasOwninの違い

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]]内部プロパティまで探索する処理

プロパティの参照とプロトタイプチェーン

オブジェクトがプロパティを探索するときは以下の順番で、オブジェクトを調べる。

  1. instanceオブジェクト自身
  2. instanceオブジェクトの[[Prototype]]の参照先
  3. どこにもなかった場合は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ステップです。

  1. 利用するロケールに対応する翻訳データのymlファイルをconfig/localesの下に配置
  2. 現在のロケールを示すI18n.localeが正しく設定された状態にする
  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 機能を追加してみよう

この章では、機能追加方法について学ぶことができました!

登録や編集の実行前に確認画面をはさむ

画面の遷移は下図の通りです。

手順は以下の通りです。

  1. 確認画面を表示するアクションを追加する
  2. 新規登録画面からの遷移先を変える
  3. 登録アクションで戻るボタンからの遷移に対応する

一覧画面に検索機能を追加する

検索機能を追加する手順は以下の通りです。

  1. Ransackをインストールする
  2. ransackメソッドを用いて、検索機能を追加する
  3. ransackの提供するヘルパーsearch_form_forを用いて、検索用フォームを作成する

メールを送る

メールを送信するためにAction Mailerという仕組みがある。
追加手順は以下の通り。

  1. ジェネレータを用いてメーラーを作成する(rails g mailer TaskMailler)
  2. 通知用メールを送るためのメソッドを追加する
  3. メールのテンプレートを実装する
  4. 用意したメーラーをコントローラから呼び出して、実際にメールを送るよう変更する

Chapter8 RailsJavaScript

この章では、RailsJavaScriptの関係について学ぶことができました!

AjxaとSJR

Ajax(Asynchronous JavaScript And XML)
Webブラウザ上で非同期通信を行い、ページの再読み込みなしにページを更新する

SJR(Server-generated JavaScript Response)
サーバサイドで生成したJavaScriptからなるレスポンスのこと

a要素のクリックイベントをフックとして遷移先のページをAjaxで取得する。
取得したページが要求するJavaScriptCSSが現在のものと同一であれば現在のものをそのまま使用し、 title要素やbody要素のみを置き換える。
→リクエストごとにJavascriptCSSをブラウザが評価しなくなるため、パフォーマンスが向上する。

注意点

  • ブラウザのページ遷移が発生しないことが多くなる
  • <script>はhead要素内に記述する

JavaScript管理

YarnFacebook社によって開発されたJavaScriptのパッケージマネージャ
Webpacker
JavaScriptのビルドツール「Webpack」のラッパーであり、RailsアプリケーションでWebpackを使ってJavaScriptを管理することを簡単にしてくれるGem

Chapter9 複数人でRailsアプリケーションを開発する

この章では、複数人開発でのアプリケーション管理について学ぶことができました!

マイグレーションの管理における注意事項

スキーマキャッシュ

スキーマキャッシュ
テーブルのスキーマを毎回データベースに問い合わせにいくことによるパフォーマンス低下を防ぐための機能。 スキーマキャッシュが古いと反映されず、古いままの値が出力される場合があるので、 手動でキャッシュを更新しなければならないケースがある。

Chapter10 Railsアプリケーションと長く付き合うために

この章では、バージョンアップやリファクタリングについて学ぶことができました!

バージョンアップ

バージョンアップ時に検討すべき内容は以下の通りである。 - 自動テストは十分か - 動作確認方法 - リリース方法 - リリース後の動作確認と監視方法 - 万が一のときのロールバック方法

複雑性への対応

複雑性へ対応するには以下の3つの鍵ある。

  • しかるべきところにコードを書く
    →モデルに書くべきコード(ビジネスロジックなど)をモデルに寄せる

  • 上手に共通化する
    →共通機能のモジュールをMix-inしたり、継承や抽象を用いて共通化を行う
    上記「しかるべきところにコードを書く」を行ってから共通化を行う必要あり

  • 新しい構造を追加して役割分担をする
    →共通処理を担当するオブジェクトを作成したり、外部サービスのロジックをオブジェクトに閉じ込めることで役割を分担する

全体の感想

  • Rubyの言語仕様から実際の運用まで説明されており、実際のアプリケーションの流れをイメージすることができた
  • 随所にコード例やモデル図などが用いられており、比較的理解しやすかった
  • Ruby on Railsのことが広く網羅されているので辞書のような形で使用するのもありだなと感じた

参考

達人に学ぶDB設計徹底指南書を読んだ感想

はじめに

今回は達人に学ぶDB設計徹底指南書を読んだ感想をまとめていきたいと思います。
DB設計未経験の自分でも理解しやすい本でした!

各章の感想

第1章 データベースを制する者はシステムを制す

この章では、システム開発の設計工程とデータベースについて学ぶことが出来ました!

DOAPOA

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. 概念設計
    取り扱うエンティティとその関連を明示する
  2. 論理設計
    キー設計は正規化などを行いRDB用のモデルに変換する
  3. 物理設計
    採用するDBMS製品に依存した詳細な設計を行う

正規化

  1. 第1正規化
    「1対多」を形成する概念は別テーブルとして設計する
  2. 第2正規化
    複合主キーの一部に関数従属する部分を別テーブルに分割する
  3. 第3正規化
    主キーに対して間接的に関数従属する部分を別テーブルに分割する

データベース設計の手順に関して、具体的な例を用いて説明されており、
とてもわかりやすいと感じました!
また、第2正規化と第3正規化に関しては行うことはあまり大差ないと感じました。

全体の感想

良かったと感じた点

  • 図や具体例を用いて丁寧に解説されており、難しい内容でもスムーズに理解できるよう工夫されていて読みやすかったです
  • dokoQLを用いることで手軽にSQL文を実行できため、イメージが湧きやすいと感じました
  • 問題も豊富にあり、インプットした内容をすぐにアウトプットできるため、記憶に定着しやすいと感じました

改善してほしい点

  • 付録のドリルの解答をつけてほしいと思いました(ネットで見ることは可能だが手間になるため)

難しいと感じた点

  • 複数のテーブルの結合や、副問い合わせが入ってくると複雑になり、少し理解するのに苦労した部分がありました
  • テーブルの設計のモデリングに関しては慣れていないこともあり、難しいと感じました

参考

「プロを目指す人のためのRuby入門」を読んだ感想

はじめに

Rubyの勉強のために、プロを目指す人のためのRuby入門 という本を読んでみました。
難しい内容も多く、読むのに時間がかかりましたが、
本を読んでの感想を「良かったと思う点」、「学んだ点」、「難しいと思った点」の
3点に分けて、まとめていきたいと思います。

感想

良かったと思う点

「プロを目指す人のためのRuby入門」を読んで良かったと思った点は以下の3点です。

  1. サンブルコードが多い
  2. 幅広く網羅されている
  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は状態を保持しないため、こちらのステートレスなプロトコルということになります。

ステートフル

ステートレスとは反対で、「フル」→「満ちている」ということなので、
直訳すると「状態を持つ」という意味になります。
もう少し説明を加えると、
状態を保持し、同じ入力でも状態によって
出力が変わる可能性のあるものを指します。
FTPTCPSSH等は状態を保持するため、こちらのステートフルなプロトコルということになります。

リクエス

クライアント(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言語のポインタについてまとめてみました。
この記事を読んでポインタについて少しでも理解を深めていただければ幸いです。

参考文献

【Go】自分が理解に苦しんだ、ポインタとアドレスについてまとめる
Goで学ぶポインタとアドレス