TypeScriptでクラス設計をレベルアップ!PrivateとProtectedで実現するスマートな情報隠蔽
TypeScript における Private と Protected 変数の違い
TypeScript では、スコープ制御と呼ばれる仕組みを用いて、変数や関数のアクセス範囲を制限することができます。スコープ制御によって、コードの読みやすさや保守性を向上させることができます。
Private と Protected は、スコープ制御で使用される 2 つの重要なキーワードです。どちらも変数のアクセス範囲を制限しますが、以下の点で違いがあります。
Private 変数
- インスタンスごとに個別の変数が保持される
- サブクラスからもアクセスできない
- クラス内部でのみアクセス可能
Protected 変数
- クラス内部およびそのサブクラスからのみアクセス可能
例
class Person {
private name: string;
protected age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
getName(): string {
return this.name;
}
getAge(): number {
return this.age;
}
}
class Employee extends Person {
private department: string;
constructor(name: string, age: number, department: string) {
super(name, age);
this.department = department;
}
getDepartment(): string {
return this.department;
}
}
const person = new Person('John Doe', 30);
console.log(person.getName()); // "John Doe"
console.log(person.age); // エラー: age はプライベート変数です
const employee = new Employee('Jane Doe', 25, 'IT');
console.log(employee.getName()); // "Jane Doe"
console.log(employee.getAge()); // 25
console.log(employee.department); // "IT"
Private 変数はクラス内部でのみアクセス可能で、サブクラスからもアクセスできません。Protected 変数はクラス内部およびそのサブクラスからのみアクセス可能です。
class Person {
private name: string;
protected age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
getName(): string {
return this.name;
}
getAge(): number {
return this.age;
}
}
class Employee extends Person {
private department: string;
constructor(name: string, age: number, department: string) {
super(name, age);
this.department = department;
}
getDepartment(): string {
return this.department;
}
calculateRetirementAge(): number {
return this.age + 65; // Protected 変数 age を使用
}
}
const person = new Person('John Doe', 30);
console.log(person.getName()); // "John Doe"
console.log(person.age); // エラー: age はプライベート変数です
const employee = new Employee('Jane Doe', 25, 'IT');
console.log(employee.getName()); // "Jane Doe"
console.log(employee.getAge()); // 25
console.log(employee.department); // "IT"
console.log(employee.calculateRetirementAge()); // 80
解説
Employee
クラス:department
変数はprivate
キーワードで宣言されているため、クラス内部でのみアクセス可能です。calculateRetirementAge()
メソッドはage
変数を使用して、従業員の退職年齢を計算します。これはage
がprotected
変数であるため可能です。
Person
クラス:age
変数はprotected
キーワードで宣言されているため、クラス内部およびサブクラスEmployee
からのみアクセス可能です。
- TypeScript は、コンパイル時に Private と Protected 変数のアクセス違反を検出することができます。
- JavaScript では、Private と Protected の実装は完全ではありません。そのため、これらのキーワードの使用には注意が必要です。
クロージャ
クロージャは、関数とそのスコープをカプセル化する技法です。プライベート変数をエミュレートするために使用することができます。
class Person {
constructor(name, age) {
const _name = name; // プライベート変数をエミュレート
this.getName = () => _name;
this.age = age;
}
}
const person = new Person('John Doe', 30);
console.log(person.getName()); // "John Doe"
console.log(person.age); // エラー: age はアクセスできません
シンボル
シンボルは、ユニークな識別子を生成するための方法です。プライベート変数をエミュレートするために使用することができます。
class Person {
constructor(name, age) {
const _name = Symbol(); // プライベート変数をエミュレート
this.getName = () => this[_name];
this.age = age;
}
}
const person = new Person('John Doe', 30);
console.log(person.getName()); // "John Doe"
console.log(person.age); // エラー: age はアクセスできません
WeakMap
WeakMap は、キーと値のペアを保持するデータ構造ですが、キーがガベージコレクションされると自動的に削除されます。プライベート変数をエミュレートするために使用することができます。
class Person {
constructor(name, age) {
const _privateData = new WeakMap(); // プライベート変数をエミュレート
_privateData.set(this, { name, age });
this.getName = () => _privateData.get(this).name;
this.age = age;
}
}
const person = new Person('John Doe', 30);
console.log(person.getName()); // "John Doe"
console.log(person.age); // エラー: age はアクセスできません
注意点
これらの代替手段は、完全な private
と protected
の実装ではありません。いくつかの制限があり、注意が必要です。
- WeakMap は、キーがガベージコレクションされると自動的に削除されますので、注意が必要です。
- クロージャとシンボルは、意図的にアクセスすれば取得することが可能です。
typescript