encapsulation
Use when exposing internal state. Use when making fields public for convenience. Use when external code modifies object internals.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Use when exposing internal state. Use when making fields public for convenience. Use when external code modifies object internals.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Use when writing tests. Use when test structure is unclear. Use when arrange/act/assert phases are mixed.
Use when designing or modifying APIs. Use when adding breaking changes. Use when clients depend on API stability.
Use when implementing authentication. Use when storing passwords. Use when asked to store credentials insecurely.
Use when same data is fetched repeatedly. Use when database queries are slow. Use when implementing caching without invalidation strategy.
Use when tempted to use class inheritance. Use when creating class hierarchies. Use when subclass needs only some parent behavior.
Use when acquiring multiple locks. Use when operations wait for each other. Use when system hangs without crashing.
| name | encapsulation |
| description | Use when exposing internal state. Use when making fields public for convenience. Use when external code modifies object internals. |
Hide internal state. Expose behavior, not data. Control access through methods.
Public fields let anyone modify your object's internals, bypassing validation and breaking invariants. Encapsulation protects data integrity.
NEVER expose internal state directly. Always use methods to control access.
No exceptions:
If internal state is directly accessible, STOP:
// ❌ VIOLATION: Public state
class BankAccount {
public balance: number = 0; // Anyone can modify!
public transactions: Transaction[] = [];
}
// Callers can break invariants
const account = new BankAccount();
account.balance = -1000000; // Negative balance!
account.transactions = []; // Audit trail destroyed!
Problems:
// ✅ CORRECT: Encapsulated state
class BankAccount {
private _balance: number = 0;
private _transactions: Transaction[] = [];
get balance(): number {
return this._balance;
}
deposit(amount: number): void {
if (amount <= 0) {
throw new Error('Deposit must be positive');
}
this._balance += amount;
this._transactions.push({
type: 'deposit',
amount,
timestamp: new Date()
});
}
withdraw(amount: number): void {
if (amount <= 0) {
throw new Error('Withdrawal must be positive');
}
if (amount > this._balance) {
throw new Error('Insufficient funds');
}
this._balance -= amount;
this._transactions.push({
type: 'withdrawal',
amount,
timestamp: new Date()
});
}
getTransactionHistory(): ReadonlyArray<Transaction> {
return [...this._transactions]; // Return copy
}
}
// Now invariants are protected
const account = new BankAccount();
account.deposit(100); // ✅ Validated, logged
account.withdraw(50); // ✅ Validated, logged
account.balance = -1000; // ❌ Error: Cannot set
class User {
private _password: string;
setPassword(newPassword: string): void {
if (newPassword.length < 8) throw new Error('Too short');
this._password = hash(newPassword);
}
checkPassword(attempt: string): boolean {
return verify(attempt, this._password);
}
}
class Config {
readonly apiUrl: string;
readonly timeout: number;
constructor(apiUrl: string, timeout: number) {
this.apiUrl = apiUrl;
this.timeout = timeout;
}
}
class Order {
private _items: OrderItem[] = [];
// ❌ BAD: Returns reference
getItems(): OrderItem[] {
return this._items; // Caller can modify!
}
// ✅ GOOD: Returns copy
getItems(): OrderItem[] {
return [...this._items];
}
// ✅ ALSO GOOD: Return readonly
getItems(): ReadonlyArray<OrderItem> {
return this._items;
}
}
class Product {
private _price: number = 0;
get price(): number {
return this._price;
}
set price(value: number) {
if (value < 0) throw new Error('Price cannot be negative');
if (value > 1000000) throw new Error('Price too high');
this._price = value;
}
}
Pressure: "Public fields are less code"
Response: Less code now, more bugs later. Encapsulation prevents invalid states.
Action: Private fields + methods. The extra code is validation.
Pressure: "Our team won't misuse public fields"
Response: Teams grow. Code evolves. Mistakes happen. Protect invariants in code.
Action: Don't rely on caller discipline. Enforce in class.
Pressure: "This class is just a data container"
Response: Even data has rules. Emails have formats. Ages have ranges.
Action: Use DTOs/interfaces for pure data. Classes = behavior + encapsulation.
Pressure: "Java-style getters/setters are boilerplate"
Response: TypeScript has concise get/set syntax. Use it.
Action: get balance() is not verbose.
public keyword on mutable fieldsAll of these mean: Encapsulate the state.
| Exposed (Bad) | Encapsulated (Good) |
|---|---|
public balance | private _balance + deposit()/withdraw() |
return this.items | return [...this.items] |
| Direct mutation | Method with validation |
| Trust callers | Enforce in class |
| Excuse | Reality |
|---|---|
| "Simpler" | Simpler to write, harder to maintain. |
| "We trust callers" | Code should enforce, not trust. |
| "Just data" | Data has constraints. Enforce them. |
| "Verbose" | TypeScript getters are concise. |
| "Over-engineering" | It's just engineering. |
Private state. Public methods. Validate on every change.
Never expose internal state directly. Return copies of collections. Validate in setters. Encapsulation protects invariants and enables safe evolution.