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 変数はクラス内部およびそのサブクラスからのみアクセス可能です。
どちらの変数を使うべきかは、その変数の役割によって異なります。一般的に、プライベート変数はクラス内部でのみ使用される内部状態を表すために使用され、保護された変数はサブクラスで共有される基底クラスのメンバーを表すために使用されます。
以下のサンプルコードは、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
解説
Person
クラス:name
変数はprivate
キーワードで宣言されているため、クラス内部でのみアクセス可能です。
Employee
クラス:calculateRetirementAge()
メソッドはage
変数を使用して、従業員の退職年齢を計算します。これはage
がprotected
変数であるため可能です。
補足
- JavaScript では、Private と Protected の実装は完全ではありません。そのため、これらのキーワードの使用には注意が必要です。
- 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 は、キーと値のペアを保持するデータ構造ですが、キーがガベージコレクションされると自動的に削除されます。プライベート変数をエミュレートするために使用することができます。
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 は、キーがガベージコレクションされると自動的に削除されますので、注意が必要です。
JavaScript における Private と Protected の代替手段は、いくつかの制約がありますが、従来の JavaScript でスコープ制御を実現するための有効な方法です。状況に応じて適切な方法を選択することが重要です。
typescript