ネストされたクラス vs 名前空間 vs モジュール:どれを使うべき?
TypeScriptにおけるネストされたクラス
ネストされたクラスの種類
TypeScriptには、主に3種類のネストされたクラスがあります。
- 公開ネストされたクラス:
public
キーワードを使用して宣言されます。外部クラスからも内部クラスからもアクセスできます。
ネストされたクラスを使用する利点は次のとおりです。
- コードのモジュール化: 関連するクラスを一緒にグループ化することで、コードをより整理しやすくなります。
- 読みやすさの向上: ネストされたクラスを使用すると、コード構造がより明確になり、理解しやすくなります。
- 保守性の向上: コードをモジュール化することで、個々のコンポーネントをより簡単に変更および更新できます。
- カプセル化の強化: ネストされたクラスを使用して、内部実装の詳細を外部クラスから隠すことができます。
class Person {
// 公開プロパティ
public name: string;
// 非公開プロパティ
private age: number;
// 公開ネストされたクラス
public Address {
street: string;
city: string;
state: string;
zipCode: number;
}
// 保護されたネストされたクラス
protected PhoneNumber {
number: string;
type: string;
}
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// 公開メソッド
public introduce() {
console.log(`My name is ${this.name} and I am ${this.age} years old.`);
}
}
// ネストされたクラスの使用例
let person = new Person('John Doe', 30);
person.introduce();
let address = new person.Address();
address.street = '123 Main Street';
address.city = 'Anytown';
address.state = 'CA';
address.zipCode = 94105;
console.log(`Address: ${address.street}, ${address.city}, ${address.state}, ${address.zipCode}`);
この例では、Person
クラス内に Address
と PhoneNumber
という2つのネストされたクラスを定義しています。 Address
クラスは公開されており、外部クラスからもアクセスできます。 PhoneNumber
クラスは保護されており、Person
クラスとその派生クラスからのみアクセスできます。
// アドレスを表すインターフェース
interface Address {
street: string;
city: string;
state: string;
zipCode: number;
}
// 連絡先情報を表すインターフェース
interface Contact {
name: string;
email: string;
phone: string;
address: Address;
}
// 連絡先を表すクラス
class ContactImpl implements Contact {
constructor(
public name: string,
public email: string,
public phone: string,
public address: Address
) {}
}
// 住所帳を表すクラス
class AddressBook {
private contacts: ContactImpl[] = [];
// 新しい連絡先を追加する
public addContact(contact: ContactImpl): void {
this.contacts.push(contact);
}
// すべての連絡先を取得する
public getAllContacts(): ContactImpl[] {
return this.contacts;
}
// 名前で連絡先を検索する
public findContactByName(name: string): ContactImpl | undefined {
return this.contacts.find((contact) => contact.name === name);
}
}
// コードの使用例
let addressBook = new AddressBook();
// 連絡先を作成して追加
let johnDoe = new ContactImpl(
'John Doe',
'[email protected]',
'(555) 555-5555',
{
street: '123 Main Street',
city: 'Anytown',
state: 'CA',
zipCode: 94105
}
);
addressBook.addContact(johnDoe);
// すべての連絡先を取得
let contacts = addressBook.getAllContacts();
console.log(contacts);
// 名前で連絡先を検索
let janeDoe = addressBook.findContactByName('Jane Doe');
if (janeDoe) {
console.log(`連絡先が見つかりました: ${janeDoe.name}`);
} else {
console.log('連絡先が見つかりませんでした: Jane Doe');
}
この例では、Address
と Contact
という2つのインターフェースを定義します。これらのインターフェースは、住所と連絡先情報に必要なプロパティを定義します。
次に、ContactImpl
クラスを作成します。このクラスは Contact
インターフェースを実装し、連絡先オブジェクトを表します。
名前空間は、関連するクラス、インターフェース、関数、変数をグループ化するための方法です。名前空間を使用すると、コードをより整理し、グローバル名前空間での名前の衝突を回避できます。
namespace MyApp {
export class Person {
// ...
}
export interface Address {
// ...
}
// ...
}
モジュール:
// person.ts
export class Person {
// ...
}
// address.ts
export interface Address {
// ...
}
// main.ts
import { Person, Address } from './';
let person = new Person('John Doe', 30);
// ...
ジェネリック:
ジェネリックは、再利用可能なコードを作成するための強力な方法です。ジェネリックを使用すると、さまざまなデータ型で使用できるクラス、インターフェース、関数を作成できます。
class Box<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
setValue(value: T): void {
this.value = value;
}
}
let stringBox = new Box<string>('Hello');
let numberBox = new Box<number>(10);
これらの方法は、それぞれ異なる利点と欠点があります。状況に応じて適切な方法を選択することが重要です。
- 関連するクラスを一緒にグループ化できる
- コードがより読みやすくなる
- カプセル化を強化できる
- コード構造が複雑になる可能性がある
- デバッグが難しくなる可能性がある
名前空間の利点:
- コードをより整理できる
- グローバル名前空間での名前の衝突を回避できる
- コードの読み込みが難しくなる可能性がある
モジュールの利点:
- 依存関係を管理しやすい
- コードの再利用性を高められる
- プロジェクトのセットアップが複雑になる可能性がある
ジェネリックの利点:
- 再利用可能なコードを作成できる
- コードをより簡潔に記述できる
- 型安全性が高い
これらの方法を組み合わせて使用することもできます。例えば、ネストされたクラスを使用して関連するクラスをグループ化し、名前空間を使用してコードをさらに整理することができます。
javascript typescript