๐ค ๋ฐ์ฝ๋ ์ดํฐ ํจํด์ด๋?
๋ฐ์ฝ๋ ์ดํฐ ํจํด(Decorator Pattern)์ ๊ฐ์ฒด์ ๊ตฌ์กฐ๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ ์๋ก์ด ๊ธฐ๋ฅ์ ๋์ ์ผ๋ก ์ถ๊ฐํ ์ ์๋ ๊ตฌ์กฐ์ ๋์์ธ ํจํด์ ๋๋ค. ๊ธฐ์กด ๊ฐ์ฒด๋ฅผ ๊ฐ์ธ๋ ๋ํผ(wrapper) ๊ฐ์ฒด๋ฅผ ํตํด ๊ธฐ๋ฅ์ ํ์ฅํ๋ ๋ฐฉ์์ผ๋ก ๋์ํฉ๋๋ค.
๐๏ธ ๋ฐ์ฝ๋ ์ดํฐ ํจํด์ ํต์ฌ ๊ฐ๋
๐ ์ฃผ์ ๊ตฌ์ฑ ์์
- Component: ๊ธฐ๋ณธ ์ธํฐํ์ด์ค ๋๋ ์ถ์ ํด๋์ค
- ConcreteComponent: ๊ธฐ๋ณธ ๊ตฌํ์ฒด
- Decorator: ๋ฐ์ฝ๋ ์ดํฐ ๊ธฐ๋ณธ ํด๋์ค
- ConcreteDecorator: ๊ตฌ์ฒด์ ์ธ ๋ฐ์ฝ๋ ์ดํฐ ๊ตฌํ์ฒด
โ ์ฅ์
- ๋ฐํ์์ ๊ฐ์ฒด์ ๊ธฐ๋ฅ์ ๋์ ์ผ๋ก ํ์ฅ ๊ฐ๋ฅ
- ์์๋ณด๋ค ์ ์ฐํ ๊ธฐ๋ฅ ํ์ฅ
- ๋จ์ผ ์ฑ ์ ์์น ์ค์
- ๊ธฐ๋ฅ์ ์กฐํฉ์ด ์์ ๋ก์
โ ๏ธ ๋จ์
- ์์ ๊ฐ์ฒด๋ค์ด ๋ง์ด ์์ฑ๋ ์ ์์
- ๋๋ฒ๊น ์ด ๋ณต์กํด์ง ์ ์์
- ๊ฐ์ฒด ์๋ณ์ด ์ด๋ ค์ธ ์ ์์
๐ป ์ค๋ฌด ์์ ์ฝ๋
1. โ Java ์์ - ํ์ฌ ์์คํ
// Component ์ธํฐํ์ด์ค
interface Company {
String getDescription();
double getRevenue();
}
// ConcreteComponent
class BasicCompany implements Company {
private String name;
private double baseRevenue;
public BasicCompany(String name, double baseRevenue) {
this.name = name;
this.baseRevenue = baseRevenue;
}
@Override
public String getDescription() {
return name;
}
@Override
public double getRevenue() {
return baseRevenue;
}
}
// Decorator ์ถ์ ํด๋์ค
abstract class CompanyDecorator implements Company {
protected Company company;
public CompanyDecorator(Company company) {
this.company = company;
}
@Override
public String getDescription() {
return company.getDescription();
}
@Override
public double getRevenue() {
return company.getRevenue();
}
}
// ConcreteDecorator๋ค
class TechDivision extends CompanyDecorator {
public TechDivision(Company company) {
super(company);
}
@Override
public String getDescription() {
return company.getDescription() + " + ๊ธฐ์ ์ฌ์
๋ถ";
}
@Override
public double getRevenue() {
return company.getRevenue() + 5000000;
}
}
class MarketingDivision extends CompanyDecorator {
public MarketingDivision(Company company) {
super(company);
}
@Override
public String getDescription() {
return company.getDescription() + " + ๋ง์ผํ
์ฌ์
๋ถ";
}
@Override
public double getRevenue() {
return company.getRevenue() + 3000000;
}
}
class GlobalExpansion extends CompanyDecorator {
public GlobalExpansion(Company company) {
super(company);
}
@Override
public String getDescription() {
return company.getDescription() + " + ๊ธ๋ก๋ฒ ์ฌ์
";
}
@Override
public double getRevenue() {
return company.getRevenue() * 1.5;
}
}
// ์ฌ์ฉ ์์
public class CompanyExample {
public static void main(String[] args) {
// ๊ธฐ๋ณธ ํ์ฌ
Company basicCompany = new BasicCompany("ํ
ํฌ์คํํธ", 10000000);
System.out.println(basicCompany.getDescription() + " - ๋งค์ถ: " + basicCompany.getRevenue());
// ๊ธฐ์ ์ฌ์
๋ถ ์ถ๊ฐ
Company techCompany = new TechDivision(basicCompany);
System.out.println(techCompany.getDescription() + " - ๋งค์ถ: " + techCompany.getRevenue());
// ๋ง์ผํ
์ฌ์
๋ถ๋ ์ถ๊ฐ
Company fullCompany = new MarketingDivision(techCompany);
System.out.println(fullCompany.getDescription() + " - ๋งค์ถ: " + fullCompany.getRevenue());
// ๊ธ๋ก๋ฒ ์ฌ์
ํ์ฅ
Company globalCompany = new GlobalExpansion(fullCompany);
System.out.println(globalCompany.getDescription() + " - ๋งค์ถ: " + globalCompany.getRevenue());
}
}
2. ๐ฆ TypeScript ์์ - ์ง์ ์์คํ
// Component ์ธํฐํ์ด์ค
interface Employee {
getName(): string;
getSalary(): number;
getRole(): string;
}
// ConcreteComponent
class BasicEmployee implements Employee {
private name: string;
private baseSalary: number;
constructor(name: string, baseSalary: number) {
this.name = name;
this.baseSalary = baseSalary;
}
getName(): string {
return this.name;
}
getSalary(): number {
return this.baseSalary;
}
getRole(): string {
return "์ผ๋ฐ ์ง์";
}
}
// Decorator ์ถ์ ํด๋์ค
abstract class EmployeeDecorator implements Employee {
protected employee: Employee;
constructor(employee: Employee) {
this.employee = employee;
}
getName(): string {
return this.employee.getName();
}
getSalary(): number {
return this.employee.getSalary();
}
getRole(): string {
return this.employee.getRole();
}
}
// ConcreteDecorator๋ค
class TeamLeader extends EmployeeDecorator {
constructor(employee: Employee) {
super(employee);
}
getRole(): string {
return this.employee.getRole() + " + ํ ๋ฆฌ๋";
}
getSalary(): number {
return this.employee.getSalary() + 500000;
}
}
class ProjectManager extends EmployeeDecorator {
constructor(employee: Employee) {
super(employee);
}
getRole(): string {
return this.employee.getRole() + " + ํ๋ก์ ํธ ๋งค๋์ ";
}
getSalary(): number {
return this.employee.getSalary() + 800000;
}
}
class SeniorDeveloper extends EmployeeDecorator {
constructor(employee: Employee) {
super(employee);
}
getRole(): string {
return this.employee.getRole() + " + ์๋์ด ๊ฐ๋ฐ์";
}
getSalary(): number {
return this.employee.getSalary() + 1000000;
}
}
class Certification extends EmployeeDecorator {
private certName: string;
constructor(employee: Employee, certName: string) {
super(employee);
this.certName = certName;
}
getRole(): string {
return this.employee.getRole() + ` + ${this.certName} ์๊ฒฉ์ฆ`;
}
getSalary(): number {
return this.employee.getSalary() + 200000;
}
}
// ์ฌ์ฉ ์์
const basicEmployee = new BasicEmployee("๊น๊ฐ๋ฐ", 4000000);
console.log(`${basicEmployee.getName()} - ${basicEmployee.getRole()} - ๊ธ์ฌ: ${basicEmployee.getSalary()}`);
const teamLeader = new TeamLeader(basicEmployee);
console.log(`${teamLeader.getName()} - ${teamLeader.getRole()} - ๊ธ์ฌ: ${teamLeader.getSalary()}`);
const seniorTeamLeader = new SeniorDeveloper(teamLeader);
console.log(`${seniorTeamLeader.getName()} - ${seniorTeamLeader.getRole()} - ๊ธ์ฌ: ${seniorTeamLeader.getSalary()}`);
const certifiedSeniorTeamLeader = new Certification(seniorTeamLeader, "AWS");
console.log(`${certifiedSeniorTeamLeader.getName()} - ${certifiedSeniorTeamLeader.getRole()} - ๊ธ์ฌ: ${certifiedSeniorTeamLeader.getSalary()}`);
const projectManagerRole = new ProjectManager(certifiedSeniorTeamLeader);
console.log(`${projectManagerRole.getName()} - ${projectManagerRole.getRole()} - ๊ธ์ฌ: ${projectManagerRole.getSalary()}`);
3. ๐ Python ์์ - ๋๋ฌผ ์์คํ
from abc import ABC, abstractmethod
# Component ์ธํฐํ์ด์ค
class Animal(ABC):
@abstractmethod
def get_description(self) -> str:
pass
@abstractmethod
def get_abilities(self) -> list:
pass
@abstractmethod
def get_power_level(self) -> int:
pass
# ConcreteComponent
class BasicAnimal(Animal):
def __init__(self, name: str, species: str, base_power: int):
self.name = name
self.species = species
self.base_power = base_power
def get_description(self) -> str:
return f"{self.name} ({self.species})"
def get_abilities(self) -> list:
return ["๊ธฐ๋ณธ ์์กด ๋ฅ๋ ฅ"]
def get_power_level(self) -> int:
return self.base_power
# Decorator ์ถ์ ํด๋์ค
class AnimalDecorator(Animal):
def __init__(self, animal: Animal):
self._animal = animal
def get_description(self) -> str:
return self._animal.get_description()
def get_abilities(self) -> list:
return self._animal.get_abilities()
def get_power_level(self) -> int:
return self._animal.get_power_level()
# ConcreteDecorator๋ค
class FlyingAbility(AnimalDecorator):
def get_description(self) -> str:
return self._animal.get_description() + " + ๋นํ ๋ฅ๋ ฅ"
def get_abilities(self) -> list:
abilities = self._animal.get_abilities().copy()
abilities.append("๋นํ")
return abilities
def get_power_level(self) -> int:
return self._animal.get_power_level() + 30
class SwimmingAbility(AnimalDecorator):
def get_description(self) -> str:
return self._animal.get_description() + " + ์์ ๋ฅ๋ ฅ"
def get_abilities(self) -> list:
abilities = self._animal.get_abilities().copy()
abilities.append("์์")
return abilities
def get_power_level(self) -> int:
return self._animal.get_power_level() + 20
class PoisonousAbility(AnimalDecorator):
def get_description(self) -> str:
return self._animal.get_description() + " + ๋
์ฑ ๋ฅ๋ ฅ"
def get_abilities(self) -> list:
abilities = self._animal.get_abilities().copy()
abilities.append("๋
์ฑ ๊ณต๊ฒฉ")
return abilities
def get_power_level(self) -> int:
return self._animal.get_power_level() + 50
class CamouflageAbility(AnimalDecorator):
def get_description(self) -> str:
return self._animal.get_description() + " + ์์ฅ ๋ฅ๋ ฅ"
def get_abilities(self) -> list:
abilities = self._animal.get_abilities().copy()
abilities.append("์์ฅ")
return abilities
def get_power_level(self) -> int:
return self._animal.get_power_level() + 25
class SuperStrength(AnimalDecorator):
def get_description(self) -> str:
return self._animal.get_description() + " + ์ด๊ฐ๋ ฅ"
def get_abilities(self) -> list:
abilities = self._animal.get_abilities().copy()
abilities.append("์ด๊ฐ๋ ฅ ํ")
return abilities
def get_power_level(self) -> int:
return self._animal.get_power_level() * 2
# ์ฌ์ฉ ์์
def print_animal_info(animal: Animal):
print(f"๐ฆ {animal.get_description()}")
print(f"๐ช ๋ฅ๋ ฅ: {', '.join(animal.get_abilities())}")
print(f"โก ํ์ ๋ ๋ฒจ: {animal.get_power_level()}")
print("-" * 50)
# ๊ธฐ๋ณธ ๋๋ฌผ๋ค
tiger = BasicAnimal("ํธ๋์ด", "๋งน์", 80)
print_animal_info(tiger)
eagle = BasicAnimal("๋
์๋ฆฌ", "๋งน๊ธ๋ฅ", 60)
print_animal_info(eagle)
snake = BasicAnimal("๋ฑ", "ํ์ถฉ๋ฅ", 40)
print_animal_info(snake)
# ๋ฅ๋ ฅ ๊ฐํ
flying_tiger = FlyingAbility(tiger)
print_animal_info(flying_tiger)
swimming_flying_tiger = SwimmingAbility(flying_tiger)
print_animal_info(swimming_flying_tiger)
super_swimming_flying_tiger = SuperStrength(swimming_flying_tiger)
print_animal_info(super_swimming_flying_tiger)
# ๋
์๋ฆฌ ๊ฐํ
camouflage_eagle = CamouflageAbility(eagle)
super_camouflage_eagle = SuperStrength(camouflage_eagle)
print_animal_info(super_camouflage_eagle)
# ๋ฑ ๊ฐํ
poisonous_snake = PoisonousAbility(snake)
camouflage_poisonous_snake = CamouflageAbility(poisonous_snake)
swimming_camouflage_poisonous_snake = SwimmingAbility(camouflage_poisonous_snake)
print_animal_info(swimming_camouflage_poisonous_snake)
๐ ์ค์ ์ฌ์ฉ ์ฌ๋ก
๐ฑ UI ์ปดํฌ๋ํธ ์์คํ
- ๊ธฐ๋ณธ ๋ฒํผ์ ์์ด์ฝ, ๋ก๋ฉ ์คํผ๋, ํดํ ๋ฑ์ ๋์ ์ผ๋ก ์ถ๊ฐ
- ๊ฐ ๊ธฐ๋ฅ์ ๋ ๋ฆฝ์ ์ผ๋ก ์กฐํฉ ๊ฐ๋ฅ
๐ ๋ฏธ๋ค์จ์ด ์์คํ
- HTTP ์์ฒญ์ ์ธ์ฆ, ๋ก๊น , ์บ์ฑ, ์์ถ ๋ฑ์ ๊ธฐ๋ฅ์ ๊ณ์ธต์ ์ผ๋ก ์ถ๊ฐ
- Express.js, Spring Boot์ ๋ฏธ๋ค์จ์ด ์ฒด์ธ
๐พ ๋ฐ์ดํฐ ์คํธ๋ฆผ ์ฒ๋ฆฌ
- ๊ธฐ๋ณธ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ์ํธํ, ์์ถ, ๋ฒํผ๋ง ๋ฑ์ ์ถ๊ฐ
- Java์ InputStream/OutputStream ํด๋์ค๋ค
๐ฎ ๊ฒ์ ๊ฐ๋ฐ
- ์บ๋ฆญํฐ์ ๋ฌด๊ธฐ, ๋ฐฉ์ด๊ตฌ, ์คํฌ, ๋ฒํ ๋ฑ์ ๋์ ์ผ๋ก ์ฅ์ฐฉ
- ์์ดํ ์กฐํฉ ์์คํ
๐ค ๋ค๋ฅธ ํจํด๊ณผ์ ๋น๊ต
๐ ๋ฐ์ฝ๋ ์ดํฐ vs ์์
- ์์: ์ปดํ์ผ ํ์์ ๊ณ ์ , ๋จ์ผ ํ์ฅ๋ง ๊ฐ๋ฅ
- ๋ฐ์ฝ๋ ์ดํฐ: ๋ฐํ์์ ๋์ , ๋ค์ค ๊ธฐ๋ฅ ์กฐํฉ ๊ฐ๋ฅ
๐ง ๋ฐ์ฝ๋ ์ดํฐ vs ์ด๋ํฐ
- ์ด๋ํฐ: ํธํ๋์ง ์๋ ์ธํฐํ์ด์ค๋ฅผ ์ฐ๊ฒฐ
- ๋ฐ์ฝ๋ ์ดํฐ: ๋์ผํ ์ธํฐํ์ด์ค์ ๊ธฐ๋ฅ ์ถ๊ฐ
๐๏ธ ๋ฐ์ฝ๋ ์ดํฐ vs ์ปดํฌ์งํธ
- ์ปดํฌ์งํธ: ํธ๋ฆฌ ๊ตฌ์กฐ์ ๊ฐ์ฒด ๊ด๊ณ ํํ
- ๋ฐ์ฝ๋ ์ดํฐ: ์ ํ์ ์ธ ๊ธฐ๋ฅ ํ์ฅ
๐ ๋ฒ ์คํธ ํ๋ํฐ์ค
โจ ํจ๊ณผ์ ์ธ ์ฌ์ฉ๋ฒ
- ๋จ์ผ ์ฑ ์ ์์น: ๊ฐ ๋ฐ์ฝ๋ ์ดํฐ๋ ํ๋์ ๊ธฐ๋ฅ๋ง ๋ด๋น
- ์ธํฐํ์ด์ค ์ผ๊ด์ฑ: ๋ชจ๋ ๋ฐ์ฝ๋ ์ดํฐ๋ ๊ฐ์ ์ธํฐํ์ด์ค ๊ตฌํ
- ์์ ๋ ๋ฆฝ์ฑ: ๋ฐ์ฝ๋ ์ดํฐ์ ์ ์ฉ ์์๊ฐ ๊ฒฐ๊ณผ์ ์ํฅ์ ์ฃผ์ง ์๋๋ก ์ค๊ณ
- ์ฑ๋ฅ ๊ณ ๋ ค: ๋๋ฌด ๋ง์ ๋ฐ์ฝ๋ ์ดํฐ ์ฒด์ธ์ ์ฑ๋ฅ ์ ํ ๊ฐ๋ฅ
๐ซ ํผํด์ผ ํ ๊ฒ๋ค
- ๋ณต์กํ ๋ฐ์ฝ๋ ์ดํฐ ์ฒด์ธ์ผ๋ก ์ธํ ๋๋ฒ๊น ์ด๋ ค์
- ๋ฐ์ฝ๋ ์ดํฐ ๊ฐ์ ๊ฐํ ์์กด์ฑ
- ์ธํฐํ์ด์ค ์ผ๊ด์ฑ ๊นจ๋จ๋ฆฌ๊ธฐ
๐ฏ ๋ง๋ฌด๋ฆฌ
๋ฐ์ฝ๋ ์ดํฐ ํจํด์ ๊ฐ์ฒด์ ๊ธฐ๋ฅ์ ์ ์ฐํ๊ฒ ํ์ฅํ ์ ์๋ ๊ฐ๋ ฅํ ๋์์ธ ํจํด์ ๋๋ค. ์์์ ํ๊ณ๋ฅผ ๊ทน๋ณตํ๊ณ ๋ฐํ์์ ๋์ ์ผ๋ก ๊ธฐ๋ฅ์ ์กฐํฉํ ์ ์์ด ํ๋ ์ํํธ์จ์ด ๊ฐ๋ฐ์์ ๋งค์ฐ ์ ์ฉํฉ๋๋ค. ํนํ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ, ๋ฏธ๋ค์จ์ด ์์คํ , UI ์ปดํฌ๋ํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฑ์์ ๊ด๋ฒ์ํ๊ฒ ํ์ฉ๋๊ณ ์์ต๋๋ค.
ํต์ฌ์ **"๊ธฐ๋ฅ์ ์กฐํฉ์ ํตํ ํ์ฅ"**์ด๋ผ๋ ๊ฐ๋ ์ ์ดํดํ๊ณ , ๊ฐ ์ธ์ด์ ํน์ฑ์ ๋ง๊ฒ ์ ์ ํ ๊ตฌํํ๋ ๊ฒ์ ๋๋ค. ์์ ์์ ๋ค์ ์ฐธ๊ณ ํ์ฌ ์ฌ๋ฌ๋ถ์ ํ๋ก์ ํธ์ ๋ฐ์ฝ๋ ์ดํฐ ํจํด์ ์ ์ฉํด๋ณด์ธ์! ๐
๋๊ธ