クラス(Typescriptを学んでいこう #3)

クラス(Typescriptを学んでいこう #3)

第3回ではクラスについてです。

Class

オブジェクト指向プログラミングの概念をサポートする言語の機能です。クラスは、関連するデータと関数をひとまとめにしたもので、それによってコードをより構造化し、保守性を高めることができます。

他の言語にも存在するもので、new キーワードを使ってクラスをインスタンス化して使用します。

下記のコードが一例です。

class Department {
    name: string;

    constructor(n: string) {
        this.name = n;
    }

    describe() {
        console.log(`Department: ${this.name}`);
    }
}

const accounting = new Department('Accounting');
accounting.describe();

const accountingCopy = { describe: accounting.describe }
accountingCopy.describe(); // `Department: undefined`が出力される

this

this キーワードはそのクラスそのものの参照です。

そのため、上記のコードでは、17行目で describe() を呼んでいますが、describe() の中の console.log(Department: ${this.name})thisaccounting ではなく、accountingCopy を指します。

accountingCopydescribe メソッドを持つただのオブジェクトです。

そのため、accountingCopy には this.name がセットされていないため、Department: undefined が出力されます。

回避するためには呼び出し先の引数に this を定義してあげます。

class Department {
    name: string;

    constructor(n: string) {
        this.name = n;
    }

    describe(this: Department) { // thisを明示する
        console.log(`Department: ${this.name}`);
    }
}

const accounting = new Department('Accounting');
accounting.describe();

const accountingCopy = { describe: accounting.describe }
accountingCopy.describe(); // `Department: Accounting`が出力される

private

メソッドや変数に private をつけると、そのクラスからしか参照ができなくなります。

何もつけない場合は、デフォルトで public が設定されており、どこからでも参照できます。

class Department {
    public name: string; // publicはデフォルトなのでつけなくてもOK
    private employees: string[] = []; //privateをつけると他から参照できなくなる
    
    constructor(lastName: string, employeesList: string[]) { 
        this.name = lastName;
        this. employees = employeeList;
    }
}

コンストラクタでの省略

変数を定義すると、コンストラクタで初期化値を代入する必要がありますが、数が多くなると大変です。

そこでTypescriptでは簡略化して表記する方法があります。

class Department {
    // 変数名を定義する必要はaない
    
    constructor(public name: string, private employees: string[]) { 
        // 何も記述しなく良い
    }
}

上記の記述だけで、this.namethis.employees にアクセスできるようになります。

readonly

readonly はその呼び方の通り readonly をつけた変数が、初期化時以外、読み取り専用となり、再代入できなくなります。

class Department {
    constructor(private readonly id: string, private name: string) { 
        // readonlyをつけると初期化時以外、再代入ができなくなる
    }
}

継承

継承は、オブジェクト指向プログラミングの概念の一つであり、あるクラスが別のクラスのプロパティやメソッドを受け継ぐことを指します。これによって、既存のクラスを拡張して新しいクラスを定義することができます。

別で定義したクラスを継承することで、継承元の変数やメソッドを使うことができます。

extends キーワードを使うことで継承ができます。クラスは1つのクラスしか継承できません。

class ITdepartment extends Department {
    constructor(id: string, public admin: string[]) {
        super(id, 'IT'); // superはthisキーワードの前に呼ばないといけない
    }

    addEmployee(employee: string): void {
        if (employee === 'Max') {
            return ;
        }
        this.employees.push(employee);
    }
}

const IT = new ITdepartment('d2', ['Jhon', 'Max']);
IT.addEmployee('Max'); // 追加されない
IT.addEmployee('Sarah');
console.log(IT);

super

super は親のクラスの constructor を呼び出します。

constructorthis を呼ぶ前に必ず呼び出す必要があります。

override(オーバーライド)

同じメソッド名でメソッドを定義することで、元のメソッドを上書きできます。

上記の例では、6行目で addEmployee メソッドを定義しており、Department クラスにある addEmployee メソッドを上書きしています。

protected

protected キーワードは、private と同じように変数の前につけ、そのクラスと継承先のクラスでのみ変数を参照できるようにします。

class Department {
    protected employees: string[] = []; // protectedにすると継承先でも変数が使えるよーの話

    constructor(private readonly id: string, private name: string) { 
    }
}

getter

getter はプロパティの値を取得する際に使用されるメソッドです。通常のプロパティアクセスとは異なり、ゲッターメソッドを介してプロパティの値を取得することができます。これを使用することで、プロパティの値に対して追加の処理を行うことができます。

setter

setter は、プロパティの値を設定する際に使用されるメソッドです。通常のプロパティ代入とは異なり、セッターメソッドを介してプロパティの値を設定することができます。これを使用することで、プロパティの値に対してバリデーションや変換などの処理を行うことができます。

class AccountingDepartment extends Department {
    private lastReport: string;

    constructor(id: string, private reports: string[]) {
        super(id, 'Accounting');
        this.lastReport = reports[0];
    }

    // getterの定義
    get mostRecentReport() {
        if (this.lastReport) {
            return this.lastReport;
        }
        throw new Error('No report found.');
    }

    // setterの定義
    set mostRecentReport(value) {
        if (!value) {
            throw new Error('Please pass in valid value.');
        }
        this.addReport(value);
    }

    addReport(text: string) {
        this.reports.push(text);
        this.lastReport = text;
    }

    printReports() {
        console.log(this.reports);
    }
}

const accountingDepartment = new AccountingDepartment('d3', []);
accountingDepartment.mostRecentReport = 'Year End Report';
console.log(accountingDepartment.mostRecentReport);
accountingDepartment.addReport('8/12 Something was wrong...');
accountingDepartment.addReport('8/13 Good luck.');
accountingDepartment.printReports();

static

static は、変数やメソッドの前につけ、クラスをインスタンス化せずに使いたい場合に使用します。

class Department {
    protected employees: string[] = [];
    static ficicalYear = 2020; // static変数

    constructor(private readonly id: string, private name: string) { 
        console.log(Department.ficicalYear); // クラス内部でstatic変数を呼び出す場合、thisを使わない
    }
    
    // staticメソッド
    static createEmployee(name: string) {
        return { name: name };
    }
}

const employee1 = Department.createEmployee('John');
console.log(employee1, Department.ficicalYear); // {name: 'John'} 2020 が出力される

注意点としては、静的変数や静的メソッドは基本的に内部から this で呼び出すことはできません。

内部で呼び出す場合は、クラス名そのものを記述する必要があります。

abstract(抽象)

abstract は変数やメソッドの前につけて、継承先でその変数やメソッドをオーバーライドすることを強制するものです。

abstract を使う場合は、クラス名の前にも abstract をつける必要があり、abstract をつけたクラスは抽象クラスとして扱われます。

// クラスの前にabsractをつける
abstract class Department {
    constructor(protected readonly id: string, private name: string) { 
    }

    // メソッド名の前にabstractをつける
    // 抽象メソッドでは具体的な実装をしない
    abstract describe(this: Department): void;
}

// 継承先のクラス
class ITdepartment extends Department {
    constructor(id: string, public admin: string[]) {
        super(id, 'IT');
    }

    // describeメソッドを必ず定義する必要がある
    // 継承先のクラスで具体的な実装をする
    describe(): void {
        console.log(`IT Department - ID: ${this.id}`);
    }
}

private constructorとSingleton(シングルトン)

シングルトンパターンとは、デザインパターンの1つで常に一意のインスタンスしか返却しないようにする手法です。

Typescirptでは、コンストラクタに private をつけ、静的メソッドを呼び出すことで実現できます。

class AccountingDepartment extends Department {
    // 内部からしかアクセスできない静的変数を定義
    private static instance: AccountingDepartment;

    // コンストラクタにprivateをつけて内部からしか呼び出せないようにするu
    private constructor(id: string, private reports: string[]) {
        super(id, 'Accounting');
    }

    // 静的メソッドを定義、中でインスタンス化を行う
    static getInstance() {
        if (!AccountingDepartment.instance) {
            this.instance = new AccountingDepartment('d3', []);
        }
        return this.instance;
    }

    describe(): void {
        console.log(`Accounting Department - ID: ${this.id}`);
    }

// newではなくgetInstance()でインスタンスを作成する
const accountingDepartment = AccountingDepartment.getInstance();

以上です。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です