객체 지향 프로그래밍의 캡슐화란 무엇입니까?
L'캡슐화 그것은 의 기본 기둥 중 하나입니다. 객체 지향 프로그래밍(OOP) 클래스의 구현 세부 사항을 숨기고 그것이 무엇인지만 노출하는 원칙을 나타냅니다. 통해 꼭 필요한공개 인터페이스 잘 정의되어 있습니다. 이 개념은 유지 관리가 가능하고 안전한 모듈식 소프트웨어를 만듭니다.
🎯 캡슐화의 목적
- 정보 숨기기: 구현 세부정보 숨기기
- 데이터 보호: 무단 접근 및 수정 방지
- 유연성: 외부 코드에 영향을 주지 않고 구현 변경
- 유지 관리성: 구성 요소 간의 결합을 줄입니다.
TypeScript의 액세스 한정자
TypeScript는 세 가지를 제공합니다. 액세스 수정자 캡슐화를 구현하려면:
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
✅ Getter/Setter를 사용해야 하는 경우:
- 데이터 검증이 필요합니다
- 가치변환
- 계산된 속성
- 로깅 변경
- 지연 초기화
❌ 사용하지 말아야 할 경우:
- 논리 없이 간단한 액세스
- 그냥 "OOP를 보기 위해"
- getter의 무거운 계산
- 게터의 부작용
읽기 전용: 불변성
수정자 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개의 getter 및 setter가 있는 경우 클래스가 너무 많은 작업을 수행하는 것일 수 있습니다(단일 책임 위반).
결론
L'캡슐화 이는 고품질 OOP 코드를 생성하는 데 필수적입니다. 숨기기 구현 세부 사항을 파악하고 잘 정의된 인터페이스만 노출함으로써 더욱 안전한 코드를 얻을 수 있습니다. 유지 관리가 가능하고 유연합니다. 기억하다: 기본적으로 비공개, 그리고 다음과 같은 경우에만 휴식을 취하세요. 꼭 필요합니다.
🎯 핵심 포인트
- 미국
private모든 내부 세부 사항에 대해 - 미국
protected제어된 확장에 의해서만 - 공용 인터페이스를 최소화하고 안정적으로 유지하세요.
- 항상 입력 데이터의 유효성을 검사합니다.
- 논리가 필요할 때 getter/setter를 선호합니다.
- 미국
readonly변경되어서는 안 되는 데이터의 경우 - '묻지 말고 말하고'와 데메테르의 법칙을 적용하세요
📚 다음 OOP 시리즈
다음 기사에서는 살펴보겠습니다. 상속과 다형성, 클래스가 확장되는 방법과 다형성을 활용하는 방법을 살펴봅니다. 유연하고 확장 가능한 코드.







