オブジェクト指向プログラミングにおけるカプセル化とは何ですか
L'カプセル化 それは基本的な柱の一つです オブジェクト指向プログラミング (OOP) クラスの実装の詳細を隠し、クラスの内容だけを公開するという原則を表します。 を通じて必要なパブリックインターフェース 明確に定義されています。この概念は以下にとって非常に重要です 保守可能で安全なモジュール式ソフトウェアを作成します。
🎯 カプセル化の目的
- 情報の隠蔽: 実装の詳細を非表示にする
- データ保護: 不正なアクセスと変更を防止する
- 柔軟性: 外部コードに影響を与えずに実装を変更する
- 保守性: コンポーネント間の結合を軽減する
TypeScript のアクセス修飾子
TypeScript には 3 つの機能があります アクセス修飾子 カプセル化を実装するには:
private, protected e public。修飾子の選択
right は、誰がクラスメンバーにアクセスできるかを決定します。
1. プライベート: 内部アクセスのみ
メンバー private これらは、それらを定義するクラス内でのみアクセスできます。
これは最も制限的なカプセル化レベルであり、すべてのユーザーにとってデフォルトである必要があります。
クラスの内部データ。
class BankAccount {{ '{' }}
// Proprietà private - accesso solo interno
private balance: number;
private accountNumber: string;
private transactionHistory: Transaction[] = [];
private isLocked: boolean = false;
constructor(accountNumber: string, initialBalance: number) {{ '{' }}
this.accountNumber = accountNumber;
this.balance = initialBalance;
this.logTransaction('Account created', initialBalance);
{{ '}' }}
// Metodo pubblico con validazione
public deposit(amount: number): void {{ '{' }}
if (this.isLocked) {{ '{' }}
throw new Error('Account is locked');
{{ '}' }}
if (amount <= 0) {{ '{' }}
throw new Error('Amount must be positive');
{{ '}' }}
this.balance += amount;
this.logTransaction('Deposit', amount);
{{ '}' }}
public withdraw(amount: number): boolean {{ '{' }}
if (this.isLocked) {{ '{' }}
throw new Error('Account is locked');
{{ '}' }}
if (amount > this.balance) {{ '{' }}
console.log('Insufficient funds');
return false;
{{ '}' }}
if (amount <= 0) {{ '{' }}
throw new Error('Amount must be positive');
{{ '}' }}
this.balance -= amount;
this.logTransaction('Withdrawal', amount);
return true;
{{ '}' }}
// Getter pubblico - read-only access
public getBalance(): number {{ '{' }}
return this.balance;
{{ '}' }}
public getAccountNumber(): string {{ '{' }}
return this.accountNumber;
{{ '}' }}
// Metodo privato - helper interno
private logTransaction(type: string, amount: number): void {{ '{' }}
const transaction: Transaction = {{ '{' }}
id: Date.now().toString(),
type,
amount,
timestamp: new Date(),
balanceAfter: this.balance
{{ '}' }};
this.transactionHistory.push(transaction);
{{ '}' }}
// Metodo per ottenere copia della storia (non l'array originale!)
public getTransactionHistory(): readonly Transaction[] {{ '{' }}
return Object.freeze([...this.transactionHistory]);
{{ '}' }}
// Metodi amministrativi
public lockAccount(): void {{ '{' }}
this.isLocked = true;
{{ '}' }}
public unlockAccount(adminCode: string): void {{ '{' }}
if (this.verifyAdminCode(adminCode)) {{ '{' }}
this.isLocked = false;
{{ '}' }}
{{ '}' }}
private verifyAdminCode(code: string): boolean {{ '{' }}
// Logica di verifica sicura
return code === 'ADMIN_SECRET_CODE';
{{ '}' }}
{{ '}' }}
interface Transaction {{ '{' }}
id: string;
type: string;
amount: number;
timestamp: Date;
balanceAfter: number;
{{ '}' }}
// Utilizzo
const account = new BankAccount('IT60X0542811101000000123456', 1000);
account.deposit(500); // ✅ OK
console.log(account.getBalance()); // ✅ OK - 1500
// account.balance = 999999; // ❌ ERRORE: balance è private
// account.logTransaction(); // ❌ ERRORE: logTransaction è private
✨プライベート会員特典
- トータルコントロール: 内部状態を変更できるのはクラスだけです
- 一元的な検証: すべての変更は検証済みのパブリック メソッドを経由します
- 安全なリファクタリング: 外部コードを壊さずにプライベート実装を変更できる
- 簡単なデバッグ: 状態がどこで変更されたかを正確に把握できます
2. 保護: サブクラスのアクセス
メンバー protected クラスとそのサブクラスからアクセスできます。
拡張は許可するが、外部のカプセル化は維持したい場合に便利です。
abstract class Vehicle {{ '{' }}
protected engineStatus: 'on' | 'off' = 'off';
protected currentSpeed: number = 0;
protected readonly maxSpeed: number;
constructor(maxSpeed: number) {{ '{' }}
this.maxSpeed = maxSpeed;
{{ '}' }}
// Metodo protected - usabile da sottoclassi
protected startEngine(): void {{ '{' }}
if (this.engineStatus === 'off') {{ '{' }}
this.engineStatus = 'on';
console.log('Engine started');
{{ '}' }}
{{ '}' }}
protected stopEngine(): void {{ '{' }}
if (this.currentSpeed === 0) {{ '{' }}
this.engineStatus = 'off';
console.log('Engine stopped');
{{ '}' }} else {{ '{' }}
console.log('Cannot stop engine while moving');
{{ '}' }}
{{ '}' }}
// Metodo pubblico
public getSpeed(): number {{ '{' }}
return this.currentSpeed;
{{ '}' }}
// Metodo astratto - deve essere implementato
abstract accelerate(amount: number): void;
{{ '}' }}
class Car extends Vehicle {{ '{' }}
private gear: number = 1;
constructor() {{ '{' }}
super(200); // maxSpeed per auto
{{ '}' }}
accelerate(amount: number): void {{ '{' }}
// Posso accedere a membri protected del genitore
if (this.engineStatus === 'off') {{ '{' }}
this.startEngine(); // ✅ OK - protected method
{{ '}' }}
if (this.currentSpeed + amount <= this.maxSpeed) {{ '{' }}
this.currentSpeed += amount;
this.adjustGear();
{{ '}' }}
{{ '}' }}
brake(amount: number): void {{ '{' }}
this.currentSpeed = Math.max(0, this.currentSpeed - amount);
if (this.currentSpeed === 0) {{ '{' }}
this.stopEngine(); // ✅ OK - protected method
{{ '}' }}
{{ '}' }}
private adjustGear(): void {{ '{' }}
// Logica privata di cambio marcia
if (this.currentSpeed > 80) this.gear = 5;
else if (this.currentSpeed > 60) this.gear = 4;
else if (this.currentSpeed > 40) this.gear = 3;
else if (this.currentSpeed > 20) this.gear = 2;
else this.gear = 1;
{{ '}' }}
{{ '}' }}
// Utilizzo
const car = new Car();
car.accelerate(50);
console.log(car.getSpeed()); // 50
// car.startEngine(); // ❌ ERRORE: startEngine è protected
// car.engineStatus; // ❌ ERRORE: engineStatus è protected
3. 公開: 無料アクセス
メンバー public コードのどの部分からでもアクセスできます。これら
を構成するパブリックインターフェース クラスのメンバーであり、そうあるべきです
最小限に抑えられ、十分に文書化されています。
⚠️ 最小特権の原則
常にプライベートから始めてください そして必要なときだけリラックスしてください。簡単です 公開されていたものを非公開にするよりも後で公開すること (破壊的な変更)。
- デフォルトでは非公開
- サブクラスに必要な場合にのみ保護される
- 重要なインターフェースのみ公開
ゲッターとセッター: アクセス制御
I ゲッター e セッター TypeScript ではエレガントな方法を提供します プロパティへのアクセスを制御し、検証とカスタム ロジックを可能にします。
class User {{ '{' }}
private _email: string;
private _age: number;
private _passwordHash: string;
constructor(email: string, age: number, password: string) {{ '{' }}
this._email = email;
this._age = age;
this._passwordHash = this.hashPassword(password);
{{ '}' }}
// Getter - read-only access con trasformazione
get email(): string {{ '{' }}
return this._email.toLowerCase();
{{ '}' }}
// Setter con validazione
set email(value: string) {{ '{' }}
if (!this.isValidEmail(value)) {{ '{' }}
throw new Error('Invalid email format');
{{ '}' }}
this._email = value;
{{ '}' }}
// Getter
get age(): number {{ '{' }}
return this._age;
{{ '}' }}
// Setter con validazione
set age(value: number) {{ '{' }}
if (value < 0 || value > 150) {{ '{' }}
throw new Error('Invalid age');
{{ '}' }}
this._age = value;
{{ '}' }}
// Proprietà computed - solo getter
get isAdult(): boolean {{ '{' }}
return this._age >= 18;
{{ '}' }}
get ageGroup(): string {{ '{' }}
if (this._age < 18) return 'minor';
if (this._age < 65) return 'adult';
return 'senior';
{{ '}' }}
// Metodo pubblico per cambiare password
changePassword(oldPassword: string, newPassword: string): boolean {{ '{' }}
if (!this.verifyPassword(oldPassword)) {{ '{' }}
return false;
{{ '}' }}
if (newPassword.length < 8) {{ '{' }}
throw new Error('Password must be at least 8 characters');
{{ '}' }}
this._passwordHash = this.hashPassword(newPassword);
return true;
{{ '}' }}
// Metodi privati - helper
private isValidEmail(email: string): boolean {{ '{' }}
const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;
return emailRegex.test(email);
{{ '}' }}
private hashPassword(password: string): string {{ '{' }}
// Simulazione hashing (usa bcrypt in produzione!)
return `hash_${{ '{' }}password.split('').reverse().join(''){{ '}' }}`;
{{ '}' }}
private verifyPassword(password: string): boolean {{ '{' }}
return this.hashPassword(password) === this._passwordHash;
{{ '}' }}
{{ '}' }}
// Utilizzo
const user = new User('john@example.com', 25, 'password123');
console.log(user.email); // "john@example.com"
console.log(user.isAdult); // true
console.log(user.ageGroup); // "adult"
user.age = 30; // ✅ OK - passa validazione
// user.age = -5; // ❌ Errore: Invalid age
// user.email = 'invalid'; // ❌ Errore: Invalid email format
user.changePassword('password123', 'newSecurePass123'); // ✅ OK
// user._passwordHash; // ❌ ERRORE: _passwordHash è private
✅ ゲッター/セッターを使用する場合:
- データ検証が必要です
- 価値の変革
- 計算されたプロパティ
- 変更ログ
- 遅延初期化
❌ 使用しない場合:
- ロジックを必要としないシンプルなアクセス
- ただ「OOPに見える」ため
- ゲッターでの大量の計算
- ゲッターの副作用
読み取り専用: 不変性
修飾子 readonly プロパティが
初期化中 (製造元) にのみ割り当てられ、変更することはできません。
class Configuration {{ '{' }}
public readonly appName: string;
public readonly version: string;
private readonly apiKey: string;
public readonly createdAt: Date;
constructor(appName: string, version: string, apiKey: string) {{ '{' }}
this.appName = appName;
this.version = version;
this.apiKey = apiKey;
this.createdAt = new Date(); // Solo qui può essere assegnato
{{ '}' }}
// Shorthand syntax nel costruttore
constructor(
public readonly appName: string,
public readonly version: string,
private readonly apiKey: string
) {{ '{' }}
// TypeScript crea automaticamente le proprietà
{{ '}' }}
getApiKey(): string {{ '{' }}
return this.apiKey;
{{ '}' }}
{{ '}' }}
const config = new Configuration('MyApp', '1.0.0', 'secret_key');
console.log(config.appName); // "MyApp"
// config.appName = 'NewName'; // ❌ ERRORE: readonly property
カプセル化のベストプラクティス
🎯 専門的なガイドライン
1. 尋ねずに教えてください
ステータスを尋ねてから決定するのではなく、オブジェクトに何をすべきかを指示します。
❌ 悪い (質問):
if (account.getBalance() > amount) {{ '{' }}
account.setBalance(
account.getBalance() - amount
);
{{ '}' }}
✅ 良い (伝える):
account.withdraw(amount);
2. デメテルの法則 (LoD)
見知らぬ人に話しかけないでください。オブジェクトは次の呼び出しのみを行う必要があります。
- 自分自身のやり方
- メソッドに渡されるパラメータ
- 彼が作るオブジェクト
- 直接コンポーネント
❌ LoD 違反:
user.getAddress()
.getCity()
.getZipCode()
✅ LoD を尊重する:
user.getCityZipCode()
3. 可能な場合は不変性
不変オブジェクトを優先します - 推論が容易でスレッドセーフです。
class Point {{ '{' }}
constructor(
public readonly x: number,
public readonly y: number
) {{ '{' }}{{ '}' }}
// Invece di modificare, ritorna nuovo oggetto
move(dx: number, dy: number): Point {{ '{' }}
return new Point(this.x + dx, this.y + dy);
{{ '}' }}
{{ '}' }}
避けるべきよくある間違い
⚠️ カプセル化のアンチパターン
1. 役に立たないゲッター/セッター (JavaBeans アンチパターン)
// ❌ Male - non aggiunge valore
class Product {{ '{' }}
private name: string;
getName(): string {{ '{' }} return this.name; {{ '}' }}
setName(name: string) {{ '{' }} this.name = name; {{ '}' }}
{{ '}' }}
// ✅ Meglio - proprietà pubblica se non serve logica
class Product {{ '{' }}
public name: string;
{{ '}' }}
2. 可変コレクションの表示
// ❌ Male - array può essere modificato esternamente
getItems(): Item[] {{ '{' }}
return this.items;
{{ '}' }}
// ✅ Bene - ritorna copia immutabile
getItems(): readonly Item[] {{ '{' }}
return Object.freeze([...this.items]);
{{ '}' }}
3. ゲッター/セッターが多すぎる
ゲッターとセッターが 20 個ある場合、クラスはおそらく実行することが多すぎます (単一責任に違反します)。
結論
L'カプセル化 これは高品質の OOP コードを作成するために不可欠です。隠れる 実装の詳細を確認し、明確に定義されたインターフェイスのみを公開することで、より安全なコードを取得できます。 保守可能で柔軟です。覚えて: デフォルトではプライベート、そのときだけリラックスしてください。 厳密に必要です。
🎯 重要なポイント
- アメリカ合衆国
privateすべての内部詳細については - アメリカ合衆国
protected制御された拡張のみで - パブリックインターフェースを最小限かつ安定したものに保つ
- 入力データを常に検証する
- ロジックが必要な場合はゲッター/セッターを優先します
- アメリカ合衆国
readonly変更してはいけないデータの場合 - 「尋ねないで教えてください」とデメテルの法則を適用する
📚 OOP シリーズの次作
次の記事では、詳しく見ていきます 継承とポリモーフィズム、 クラスがどのように拡張できるか、ポリモーフィズムを活用する方法を確認する 柔軟で拡張可能なコード。







