コードを再利用してスマート開発:TypeScriptでクラスを継承、ミックスイン、ユーティリティ関数で拡張
TypeScriptで2つのクラスを拡張する方法
TypeScriptでは、継承とミックスインという2つの方法で、既存のクラスを拡張することができます。
継承
継承は、extends キーワードを使用して、既存のクラス(基底クラス)の機能を新しいクラス(派生クラス)に引き継ぐ方法です。派生クラスは、基底クラスのすべてのプロパティとメソッドにアクセスでき、さらに独自のプロパティとメソッドを追加することができます。
継承の例:
class Person {
constructor(public name: string) {}
greet() {
console.log(`こんにちは、${this.name}です!`);
}
}
class Student extends Person {
constructor(public name: string, public major: string) {
super(name);
}
study() {
console.log(`${this.major}を勉強しています!`);
}
}
const student = new Student("田中", "コンピュータサイエンス");
student.greet(); // こんにちは、田中です!
student.study(); // コンピュータサイエンスを勉強しています!
継承の利点
- コードの再利用性を高めることができる
- コードを保守しやすくなる
- 既存のクラスの機能を拡張しやすい
- TypeScriptは単一継承のみ許可しているため、1つのクラスは複数の基底クラスを継承することはできない
- 継承関係が複雑になると、コードが分かりにくくなる
ミックスイン
ミックスインは、インターフェースまたはユーティリティ関数を使用して、既存のクラスに機能を追加する方法です。継承とは異なり、ミックスインを使用すると、クラスの階層構造を変更せずに機能を追加することができます。
ミックスインの例:
interface Logger {
log(message: string): void;
}
class Person {
constructor(public name: string) {}
greet() {
console.log(`こんにちは、${this.name}です!`);
}
}
function mixinLogger(klass: any): void {
klass.prototype.log = function(message: string) {
console.log(`[ログ] ${message}`);
};
}
mixinLogger(Person);
const student = new Person("田中");
student.greet(); // こんにちは、田中です!
student.log("勉強を始めます!"); // [ログ] 勉強を始めます!
- 継承を使用せずに、既存のクラスに機能を追加できる
- 柔軟性の高いコードを書くことができる
- 複数のミックスインを使用すると、名前の衝突が発生する可能性がある
- コードが分かりにくくなる可能性がある
継承とミックスインは、それぞれ異なる利点と欠点があります。状況に応じて適切な方法を選択する必要があります。
- 既存のクラスの機能を拡張したい場合は、継承を使用する
- 既存のクラスの階層構造を変更せずに機能を追加したい場合は、ミックスインを使用する
- 複数のクラスに共通する機能を追加したい場合は、インターフェースを使用する
サンプルコード:継承とミックスイン
継承
class Person {
constructor(public name: string) {}
greet() {
console.log(`こんにちは、${this.name}です!`);
}
}
class Student extends Person {
constructor(public name: string, public major: string) {
super(name);
}
study() {
console.log(`${this.major}を勉強しています!`);
}
}
const student = new Student("田中", "コンピュータサイエンス");
student.greet(); // こんにちは、田中です!
student.study(); // コンピュータサイエンスを勉強しています!
説明
このコードは、継承を使用して、Person クラスから Student クラスを拡張する例です。
- Person クラスは、名前 (
name
) を持つ人の基本的な情報を表します。 - Student クラスは、Person クラスを継承し、専攻 (
major
) を持つ学生を表します。 - Student クラスは、greet() メソッドを Person クラスから継承し、study() メソッドを追加します。
ミックスイン
interface Logger {
log(message: string): void;
}
class Person {
constructor(public name: string) {}
greet() {
console.log(`こんにちは、${this.name}です!`);
}
}
function mixinLogger(klass: any): void {
klass.prototype.log = function(message: string) {
console.log(`[ログ] ${message}`);
};
}
mixinLogger(Person);
const student = new Person("田中");
student.greet(); // こんにちは、田中です!
student.log("勉強を始めます!"); // [ログ] 勉強を始めます!
このコードは、ミックスインを使用して、Logger インターフェースと mixinLogger 関数を使用して、Person クラスにログ機能を追加する例です。
- Logger インターフェースは、
log()
メソッドを持つオブジェクトを定義します。 - mixinLogger 関数は、引数として渡されたクラスに
log()
メソッドを追加します。
TypeScriptで2つのクラスを拡張するその他の方法
従来の継承とミックスインに加えて、TypeScriptで2つのクラスを拡張するその他の方法がいくつかあります。
クラスの装飾子は、クラスの定義に情報を追加するために使用できる強力な機能です。装饰器を使用して、新しいプロパティ、メソッド、またはコンストラクタを追加したり、既存のメソッドの動作を変更したりできます。
クラスの装飾子を使用して2つのクラスを拡張するには、次のようにします。
- 拡張したい機能を定義する装飾子を作成します。
- 拡張したいクラスに装飾子を適用します。
// Logger デコレータ
function Logger(target: any) {
const originalMethod = target.prototype.greet;
target.prototype.greet = function(...args: any[]) {
console.log(`[ログ] こんにちは、${this.name}です!`);
originalMethod.apply(this, args);
};
}
// Person クラス
class Person {
constructor(public name: string) {}
greet() {
console.log(`こんにちは、${this.name}です!`);
}
}
// Student クラスに Logger デコレータを適用
@Logger
class Student extends Person {
constructor(public name: string, public major: string) {
super(name);
}
}
const student = new Student("田中", "コンピュータサイエンス");
student.greet(); // [ログ] こんにちは、田中です!
利点:
- 柔軟性と強力性
- 既存のコードを変更せずに機能を追加できる
- 複雑で理解しにくい場合がある
- デバッグが難しい場合がある
動的プロキシは、オブジェクトのプロパティとメソッドへのアクセスを傍受して、動作を変更するためのオブジェクトです。動的プロキシを使用して、2つのクラスを拡張するには、次のようにします。
- 拡張したいクラスのインスタンスを動的プロキシでラップします。
// Logger プロキシ
const loggerProxy = new Proxy(Person, {
get: function(target: Person, property: string) {
if (property === 'greet') {
return function(...args: any[]) {
console.log(`[ログ] こんにちは、${this.name}です!`);
return Reflect.apply(target[property], this, args);
};
}
return Reflect.get(target, property);
}
});
// Student クラスのインスタンスを Logger プロキシでラップ
const studentProxy = new loggerProxy(new Student("田中", "コンピュータサイエンス"));
studentProxy.greet(); // [ログ] こんにちは、田中です!
- パフォーマンスが低下する可能性がある
ユーティリティ関数は、共通のタスクを実行するために使用できる関数です。ユーティリティ関数を使用して2つのクラスを拡張するには、次のようにします。
// logGreet 関数
function logGreet(this: Person) {
console.log(`[ログ] こんにちは、${this.name}です!`);
this.greet();
}
// Person クラス
class Person {
constructor(public name: string) {}
greet() {
console.log(`こんにちは、${this.name}です!`);
}
}
// Student クラス
class Student extends Person {
constructor(public name: string, public major: string) {
super(name);
}
study() {
console.log(`${this.major}を勉強しています!`);
}
// logGreet 関数を呼び出す
greetWithLog() {
logGreet.call(this);
}
}
const student = new Student("田中", "コンピュータサイエンス");
student.greetWithLog(); // [ログ] こんにちは、田中です!
- シンプルで理解しやすい
- コードをモジュール化しやすい
- 他の方法ほど柔軟性がない
- コードの再利用が難しい場合がある
TypeScriptで2つの
javascript oop typescript