디자인 패턴, 프로그래밍 패러다임

박세건·3일 전
1

📚 라이브러리 vs 프레임워크

✅ 공통점

  • 공통으로 사용될 수 있는 특정한 기능들을 모듈화(모아놓은 것)

🔍 차이점

구분설명비유
라이브러리내가 호출해서 사용하는 도구. 제어 흐름을 개발자가 관리✂️ 가위를 내가 직접 사용하는 것
프레임워크프레임워크가 내 코드를 호출함. 제어 흐름을 프레임워크가 관리✈️ 비행기를 타고 이동하는 것

💡 제어의 역전(Inversion of Control, IoC)
이 개념은 프레임워크를 설명할 때 자주 등장합니다.

  • 라이브러리는 개발자가 주도하지만
  • 프레임워크는 전체 흐름을 프레임워크가 주도하고 개발자가 필요한 부분만 작성합니다.

🧠 디자인 패턴 (Design Pattern)

프로그램을 설계할 때 발생하는 문제점들을 객체 간의 관계를 통해 해결할 수 있도록 정형화된 설계 방식

💡 디자인 패턴은 재사용 가능하고 검증된 설계 방식으로, 개발자의 의사소통을 원활하게 하고 유지보수를 쉽게 만들어 줍니다.


🧩 싱글톤 패턴 (Singleton Pattern)

🔷 정의

  • 하나의 클래스에 오직 하나의 인스턴스만 존재하도록 제한하는 디자인 패턴입니다.
  • 보통 DB 연결, 설정 클래스, 로깅, 캐시 등 공유 리소스가 필요한 경우에 사용됩니다.

🗃️ "하나의 객체를 모두가 공유" — 메모리 절약 + 일관성 보장


✅ 장점

  • 인스턴스 재사용 → 객체 생성 비용 감소
  • 전역 접근 가능 → 어디서든 동일한 인스턴스를 사용할 수 있음

❌ 단점

  • 높은 결합도 → 코드 변경 시 ripple effect(파급 효과) 발생 가능
  • 테스트 어려움 → TDD 관점에서 독립적인 테스트가 어려움

💻 예시 코드 (Java)

class Singleton {
    private static class SingleInstanceHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static synchronized Singleton getInstance() {
        return SingleInstanceHolder.INSTANCE;
    }
}

public class HelloWorld {
    public static void main(String[] args) {
        Singleton a = Singleton.getInstance();
        Singleton b = Singleton.getInstance();
        System.out.println(a.hashCode());
        System.out.println(b.hashCode());
        if (a == b) {
            System.out.println(true);
        }
    }
}

// 출력 예시:
// 705927765
// 705927765
// true

🧠 이처럼 ab는 동일한 인스턴스를 참조하므로 == 비교 결과가 true입니다.


🧪 TDD에서의 단점

  • 단위 테스트는 독립적으로 수행되어야 하는데,
  • 싱글톤은 하나의 인스턴스를 공유하기 때문에
    테스트 간 상태 공유 문제 발생 가능 → 테스트 신뢰도 저하

🧷 결합도 증가 문제와 해결 방법 – 의존성 주입 (DI: Dependency Injection)

📌 의존성이란?

A가 B의 기능을 사용하고 있다면, A는 B에 의존하고 있다고 합니다.
즉, B가 변경되면 A도 영향을 받게 됩니다.

💉 의존성 주입(DI)이란?

  • 직접 객체를 생성하지 않고, 외부에서 주입받는 방식
  • 아래 그림처럼 중간에 주입자를 두고 의존성을 간접적으로 연결합니다

🧠 DI의 장점

  • 유연한 변경 → 모듈 교체가 쉬움

  • 유지보수 용이

  • 테스트 쉬움 → 테스트용 객체를 쉽게 주입 가능

  • 마이그레이션 용이

    💡 예: MySQL에서 Oracle로 DB 교체 시, DI 구조라면 쉽게 DB 객체 교체 가능


⚠️ DI의 단점

  • 구조가 복잡해질 수 있음 → 학습 비용 증가
  • 런타임 성능에 미세한 손해 발생 가능 (DI 컨테이너 활용 시)

🏭 팩토리 패턴 (Factory Pattern)

✅ 정의

  • 객체 생성 로직을 분리하여 캡슐화하는 디자인 패턴
  • 객체 생성에 대한 책임을 팩토리 클래스에 위임하고, 클라이언트는 구체적인 클래스를 몰라도 객체를 생성할 수 있음

📌 객체 생성 코드를 분리함으로써, 클래스 간의 결합도를 낮추고 유지보수성을 향상시킵니다.


🧩 구조 및 역할

역할설명
Coffee추상 클래스 (Product) – 인터페이스 제공
Latte, Americano, DefaultCoffee구체 클래스 (Concrete Product) – 실제 객체 생성 및 로직 정의
CoffeeFactory팩토리 클래스 (Creator) – 어떤 객체를 생성할지 결정하고 생성 책임을 가짐

🧪 예시 코드

abstract class Coffee {
    public abstract int getPrice();

    @Override
    public String toString() {
        return "Hi this coffee is " + this.getPrice();
    }
}

class CoffeeFactory {
    public static Coffee getCoffee(String type, int price) {
        if ("Latte".equalsIgnoreCase(type)) return new Latte(price);
        else if ("Americano".equalsIgnoreCase(type)) return new Americano(price);
        else return new DefaultCoffee();
    }
}

class DefaultCoffee extends Coffee {
    private int price;

    public DefaultCoffee() {
        this.price = -1;
    }

    @Override
    public int getPrice() {
        return this.price;
    }
}

class Latte extends Coffee {
    private int price;

    public Latte(int price) {
        this.price = price;
    }

    @Override
    public int getPrice() {
        return this.price;
    }
}

class Americano extends Coffee {
    private int price;

    public Americano(int price) {
        this.price = price;
    }

    @Override
    public int getPrice() {
        return this.price;
    }
}

public class CoffeeTest {
    public static void main(String[] args) {
        Coffee coffee1 = CoffeeFactory.getCoffee("Latte", 4500);
        Coffee coffee2 = CoffeeFactory.getCoffee("Americano", 4000);
        Coffee coffee3 = CoffeeFactory.getCoffee("Mocha", 0); // 정의되지 않은 커피

        System.out.println(coffee1); // Hi this coffee is 4500
        System.out.println(coffee2); // Hi this coffee is 4000
        System.out.println(coffee3); // Hi this coffee is -1
    }
}

💡 핵심 포인트

  • CoffeeFactory.getCoffee(type, price)만 호출하면 적절한 커피 객체가 생성됨 ☕
  • 클라이언트는 Latte, Americano 클래스의 세부 구현을 몰라도 객체 생성 가능
  • 새로운 커피 종류가 추가되어도 기존 코드를 변경하지 않고 확장 가능(개방-폐쇄 원칙)

✅ 장점

  • 객체 생성 코드와 비즈니스 로직 분리 → 결합도 감소
  • 코드 재사용성, 유지보수성 향상
  • OCP(Open-Closed Principle) 만족 → 기능 추가 시 기존 코드 변경 X

❌ 단점

  • 클래스가 많아지고 구조가 복잡해질 수 있음
  • 간단한 프로그램에서는 과한 설계가 될 수 있음

📌 정리
팩토리 패턴은 “무엇을 만들지 알지만, 어떻게 만들지는 몰라도 된다”는 철학을 기반으로 합니다.
→ 객체 생성이 자주 바뀌거나, 확장 가능한 구조가 필요한 경우 특히 유용합니다.



🧠 전략 패턴 (Strategy Pattern)

✅ 정의

  • 객체의 행위를 동적으로 변경할 수 있도록 행위를 캡슐화한 **전략 객체(Strategy)**를 정의하고, 이를 런타임에 교체할 수 있도록 만드는 패턴
  • 행위를 직접 수정하지 않고, 행위(알고리즘)를 클래스로 추출하여 유연하게 교체 가능

🧩 구조

구성 요소설명
PaymentStrategy전략(Strategy) 인터페이스 – 공통 행위 정의
KAKAOCardStrategy, LUNACardStrategy구체 전략 – 각기 다른 행위(결제 방식) 구현
ShoppingCart컨텍스트(Context) – 전략 객체를 주입받아 내부에서 행위를 위임


💻 예시 코드

interface PaymentStrategy {
    public void pay(int amount);
}

class KAKAOCardStrategy implements PaymentStrategy {
    private String name;
    private String cardNumber;
    private String cvv;
    private String dateOfExpiry;

    public KAKAOCardStrategy(String nm, String ccNum, String cvv, String expiryDate) {
        this.name = nm;
        this.cardNumber = ccNum;
        this.cvv = cvv;
        this.dateOfExpiry = expiryDate;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid using KAKAOCard.");
    }
}

class LUNACardStrategy implements PaymentStrategy {
    private String emailId;
    private String password;

    public LUNACardStrategy(String email, String pwd) {
        this.emailId = email;
        this.password = pwd;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid using LUNACard.");
    }
}

class Item {
    private String name;
    private int price;

    public Item(String name, int cost) {
        this.name = name;
        this.price = cost;
    }

    public String getName() {
        return name;
    }

    public int getPrice() {
        return price;
    }
}

class ShoppingCart {
    List<Item> items;

    public ShoppingCart() {
        this.items = new ArrayList<>();
    }

    public void addItem(Item item) {
        this.items.add(item);
    }

    public void removeItem(Item item) {
        this.items.remove(item);
    }

    public int calculateTotal() {
        int sum = 0;
        for (Item item : items) {
            sum += item.getPrice();
        }
        return sum;
    }

    public void pay(PaymentStrategy paymentMethod) {
        int amount = calculateTotal();
        paymentMethod.pay(amount);
    }
}

public class HelloWorld {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        Item A = new Item("kundolA", 100);
        Item B = new Item("kundolB", 300);

        cart.addItem(A);
        cart.addItem(B);

        cart.pay(new LUNACardStrategy("kundol@example.com", "pukubababo"));
        cart.pay(new KAKAOCardStrategy("Ju hongchul", "123456789", "123", "12/01"));
    }
}

🧾 실행 결과

400 paid using LUNACard.
400 paid using KAKAOCard.

🔍 설명

  • PaymentStrategy결제 알고리즘을 추상화한 인터페이스
  • KAKAOCardStrategy, LUNACardStrategy전략 객체, 각각의 결제 방식 구현
  • ShoppingCart는 전략 객체를 전달받아 pay() 동작을 위임
  • 결제 방식이 바뀌어도 기존 코드를 수정하지 않고 전략 객체만 바꾸면 됨

🎯 장점

  • 행위(전략)의 변경이 자유롭고 코드 수정 없이 확장 가능
  • 조건문, 분기문 제거 → 코드 가독성 증가
  • OCP(개방-폐쇄 원칙) 준수
    → 새로운 전략 추가 시 기존 코드 변경 없이 확장

⚠️ 단점

  • 전략 클래스가 많아질 수 있음 → 클래스 수 증가
  • 클라이언트가 전략을 직접 선택해야 하는 부담


👁️ 옵저버 패턴 (Observer Pattern)

✅ 정의

  • **주체(Subject)**의 상태 변화가 있을 때, 이를 **관찰(Observe)**하던 **옵저버(Observer)**들에게 자동으로 알림을 전달하는 패턴
  • 발행-구독(Publish-Subscribe) 모델과 유사한 구조

예: 팔로우한 유저가 글을 올리면 알림이 오는 구조


🧩 사용 구조

구성 요소역할 설명
Subject옵저버 등록/해제/알림 처리 인터페이스
Observer상태 변경 시 알림을 받는 객체 인터페이스
Topic실제 주체, 상태가 변경되면 옵저버에게 알림
MyObserver실제 옵저버 클래스, update() 메서드 구현

💻 예시 코드

// Observer 인터페이스
interface Observer {
    void update();
    void setSubject(Subject sub);
}

// Subject 인터페이스
interface Subject {
    void register(Observer obj);
    void unregister(Observer obj);
    void notifyObservers();
    Object getUpdate(Observer obj);
}

// Subject 구현체
class Topic implements Subject {
    private List<Observer> observers;
    private String message;
    private boolean changed;

    public Topic() {
        observers = new ArrayList<>();
        message = "";
        changed = false;
    }

    @Override
    public void register(Observer obj) {
        if (!observers.contains(obj)) observers.add(obj);
    }

    @Override
    public void unregister(Observer obj) {
        observers.remove(obj);
    }

    @Override
    public void notifyObservers() {
        if (!changed) return;
        for (Observer obj : observers) {
            obj.update();
        }
        changed = false;
    }

    @Override
    public Object getUpdate(Observer obj) {
        return message;
    }

    public void postMessage(String msg) {
        System.out.println("Message Posted: " + msg);
        this.message = msg;
        this.changed = true;
        notifyObservers();
    }
}

// Observer 구현체
class MyObserver implements Observer {
    private String name;
    private Subject topic;

    public MyObserver(String name) {
        this.name = name;
    }

    @Override
    public void update() {
        String msg = (String) topic.getUpdate(this);
        System.out.println(name + ": Consuming message → " + msg);
    }

    @Override
    public void setSubject(Subject sub) {
        this.topic = sub;
    }
}

// 실행
public class ObserverPatternDemo {
    public static void main(String[] args) {
        Topic topic = new Topic();

        Observer user1 = new MyObserver("User1");
        Observer user2 = new MyObserver("User2");
        Observer user3 = new MyObserver("User3");

        user1.setSubject(topic);
        user2.setSubject(topic);
        user3.setSubject(topic);

        topic.register(user1);
        topic.register(user2);
        topic.register(user3);

        topic.postMessage("옵저버 패턴 알림 메시지입니다.");
    }
}

🧾 실행 결과

Message Posted: 옵저버 패턴 알림 메시지입니다.
User1: Consuming message → 옵저버 패턴 알림 메시지입니다.
User2: Consuming message → 옵저버 패턴 알림 메시지입니다.
User3: Consuming message → 옵저버 패턴 알림 메시지입니다.

🧠 개념 요약

  • postMessage()를 호출하면 내부 메시지가 변경되고, 이때 notifyObservers()를 통해 모든 옵저버에게 통보
  • 옵저버는 update()를 통해 최신 메시지를 받아 처리

🌐 실제 활용 예

  • Vue.js 양방향 바인딩: 데이터 변화 → DOM 자동 반영

  • Model-View-Controller(MVC) 구조:

    • Model이 변경 → View에 알림 → Controller가 동작

🧬 extends (상속) vs implements (구현)

상속 (extends)

  • 자식 클래스가 부모 클래스의 속성과 메서드를 물려받음
  • 일반 클래스, 추상 클래스 모두 상속 가능
  • 추상 클래스 상속 시에는 반드시 추상 메서드 구현 필수
  • 단일 상속만 가능

구현 (implements)

  • 자식 클래스가 인터페이스의 모든 메서드를 반드시 구현
  • 다중 구현 가능 (여러 인터페이스를 동시에 구현 가능)

🧮 추상 클래스 vs 인터페이스

구분추상 클래스인터페이스
상속 방식extendsimplements
변수인스턴스 변수 사용 가능상수만 선언 가능 (Java 8 이후 default 메서드 허용)
상속 수단일 상속만 가능다중 구현 가능
용도일부 구현 + 공통 기능 제공명세(기능 선언) 중심 구조


🧱 프록시 패턴 (Proxy Pattern)

✅ 정의

  • **실제 객체(Real Subject)**에 접근하기 전에, 중간에 위치한 **프록시 객체(Proxy)**를 통해 접근 흐름을 제어하는 구조
  • 접근 제어, 로깅, 캐싱, 지연 초기화(lazy loading) 등에 사용

🧩 구조

구성 요소설명
Image 인터페이스공통 인터페이스 (실제 객체와 프록시 객체가 구현)
RealImage실제 동작을 수행하는 객체
ProxyImage실제 객체 대신 동작을 제어하는 프록시 객체

💻 예시 코드

interface Image {
    void display();
}

class RealImage implements Image {
    private String filename;

    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk(); // 무거운 작업
    }

    private void loadFromDisk() {
        System.out.println("Loading " + filename);
    }

    @Override
    public void display() {
        System.out.println("Displaying " + filename);
    }
}

class ProxyImage implements Image {
    private RealImage realImage;
    private String filename;

    public ProxyImage(String filename) {
        this.filename = filename;
    }

    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename); // 지연 초기화
        }
        realImage.display();
    }
}

public class ProxyPatternDemo {
    public static void main(String[] args) {
        Image image = new ProxyImage("myPhoto.jpg");

        System.out.println("이미지를 처음 요청할 때:");
        image.display(); // 실제 객체 생성 + 표시

        System.out.println("이미지를 다시 요청할 때:");
        image.display(); // 기존 객체로 표시
    }
}

🧾 실행 결과

이미지를 처음 요청할 때:
Loading myPhoto.jpg
Displaying myPhoto.jpg

이미지를 다시 요청할 때:
Displaying myPhoto.jpg

🧠 요약

  • ProxyImage는 내부적으로 RealImage필요할 때만 생성
  • 불필요한 리소스 낭비 방지, 보안 및 접근 제어 가능

🌐 프록시 서버 (Proxy Server)

✅ 정의

  • 클라이언트와 서버 사이에 위치하여 클라이언트의 요청을 대리 처리하는 서버
  • 실제 서버를 숨기고 중간에서 흐름을 조정하거나 보호


🧰 주요 기능

기능설명
캐싱자주 요청되는 데이터를 프록시 서버에 저장하여 서버에 직접 요청하지 않고 응답 가능
보안예: CloudFlare를 앞단에 두어 서버 IP와 포트를 숨김, 악의적 트래픽 차단 가능
HTTPS 지원NGINX나 CloudFlare를 통해 손쉽게 인증서를 적용해 HTTPS 구축 가능
CORS 해결프록시 서버가 요청을 중계함으로써 포트가 다른 요청을 우회 가능
로드밸런싱여러 서버에 요청을 분산하여 트래픽 부하를 분산

🔧 예시 도구

  • NGINX: 프록시 서버, 로드밸런서, 정적 리소스 서버 등 다양한 용도로 사용됨
  • CloudFlare: 전 세계적으로 사용되는 CDN + 프록시 서버 역할

💬 예

  • Vue, React 개발 중 localhost:3000에서 실행 시, 백엔드(localhost:12010)와 포트가 달라 CORS 에러 발생
  • vite.config.jsvue.config.js에서 프록시 설정을 통해 문제 해결


🔁 이터레이터 패턴 (Iterator Pattern)

✅ 정의

  • 컬렉션 내부 구조를 노출하지 않고, 요소들을 순차적으로 접근할 수 있게 해주는 디자인 패턴
  • 자료구조가 무엇이든 동일한 인터페이스(Iterator)를 통해 요소를 순회 가능

자바에서는 java.util.Iterator 인터페이스가 이 패턴을 구현하고 있음


🧩 구조

구성 요소설명
Iterator순회 동작을 정의하는 인터페이스 (hasNext(), next())
Aggregate컬렉션 인터페이스 또는 추상 클래스
ConcreteAggregate실제 데이터를 보관하는 클래스
ConcreteIterator실제 순회 동작을 구현하는 클래스

자바에서는 대부분의 컬렉션이 자체적으로 Iterator를 반환하는 .iterator() 메서드를 제공


💻 예시 코드 (Java)

import java.util.*;

public class JavaIteratorExample {
    public static void main(String[] args) {
        // List 순회
        List<String> list = Arrays.asList("Apple", "Banana", "Cherry");
        Iterator<String> listIterator = list.iterator();

        System.out.println("List 순회:");
        while (listIterator.hasNext()) {
            System.out.println(listIterator.next());
        }

        // Set 순회
        Set<Integer> set = new HashSet<>(Arrays.asList(10, 20, 30));
        Iterator<Integer> setIterator = set.iterator();

        System.out.println("\nSet 순회:");
        while (setIterator.hasNext()) {
            System.out.println(setIterator.next());
        }

        // Map 순회 (EntrySet 기준)
        Map<String, Integer> map = new HashMap<>();
        map.put("Alice", 90);
        map.put("Bob", 85);
        map.put("Charlie", 95);

        Iterator<Map.Entry<String, Integer>> mapIterator = map.entrySet().iterator();

        System.out.println("\nMap 순회:");
        while (mapIterator.hasNext()) {
            Map.Entry<String, Integer> entry = mapIterator.next();
            System.out.println(entry.getKey() + " → " + entry.getValue());
        }
    }
}

🧾 실행 결과

List 순회:
Apple
Banana
Cherry

Set 순회:
10
20
30

Map 순회:
Alice → 90
Bob → 85
Charlie → 95

🧠 요약

  • 자료구조가 List, Set, Map 등 어떤 형태이든 .iterator()를 통해 순회 로직을 통일
  • 내부 구조 변경 없이 반복 기능을 추가할 수 있음
  • 다양한 자료형을 일관된 방식으로 다룰 수 있게 해주는 핵심 디자인 패턴


🧱 MVC 패턴 (Model-View-Controller)

✅ 정의

  • 사용자 인터페이스, 데이터, 제어 로직을 세 가지 역할로 분리하여 구조화한 디자인 패턴
  • 각 구성 요소의 책임을 분리함으로써 유지보수성과 확장성을 향상시킴

📦 구성요소 및 역할

구성 요소역할스프링 예시
Model애플리케이션의 데이터, 비즈니스 로직 관리@Service, @Repository, Entity
View사용자에게 보여지는 UI 요소Thymeleaf, HTML, JSON
Controller사용자 요청을 받고 결과를 반환하는 중간 제어자@Controller, @RestController

🔁 Spring Boot MVC 동작 흐름

[1] 사용자 요청: "/hello"
       ↓
[2] Controller (@GetMapping("/hello"))
       ↓
[3] Model(Service/Repository)에서 데이터 조회
       ↓
[4] View 반환 (HTML or JSON)


👨‍🏫 MVP 패턴 (Model-View-Presenter)

✅ 정의

  • MVC에서 파생된 구조
  • Controller → Presenter로 대체
  • View와 Presenter 간에 강한 결합
  • Presenter가 모든 로직을 직접 호출하고 제어

특징

  • View는 Presenter의 명령에만 따름
  • Presenter는 View와 Model 양쪽을 모두 제어
  • 안드로이드 초창기 구조에서 많이 사용

❗ 현재는 잘 사용되지 않음 → MVVM으로 전환 추세


🔄 MVVM 패턴 (Model-View-ViewModel)

✅ 정의

  • MVC에서 Controller가 ViewModel로 대체된 구조
  • View와 ViewModel 간 자동 바인딩
  • ViewModel은 비즈니스 로직과 View 사이의 중간 추상화 계층 역할

특징

  • View ↔ ViewModel 간 양방향 바인딩
  • ViewModel은 Model과의 직접적인 연동 없이 상태만 관리
  • 사용자의 행동 변화는 ViewModel을 통해 처리됨

예시

  • Vue.js: watch, computed, v-model
  • React + MobX, Angular도 MVVM 유사 구조


💡 프로그래밍 패러다임

프로그래밍을 바라보는 사고 방식 또는 설계 방법론.
문제를 어떤 관점으로 해결할지에 대한 체계.

주요 패러다임:

  • 선언형: 무엇을 할 것인지 중심 (함수형)
  • 명령형: 어떻게 할 것인지 중심 (절차지향, 객체지향)

🔹 함수형 프로그래밍 (Functional Programming)

  • 무엇을 해결할지를 선언적으로 표현
  • 상태 변화나 부작용 없이 함수 중심으로 구성

📌 특징

특징설명
순수 함수동일 입력 → 동일 출력. 외부 상태 변화 없음 (전역 변수 사용 X)
고차 함수함수를 인자로 전달하거나 반환함 (함수를 값처럼 사용)
일급 객체함수를 변수에 할당하거나 매개변수로 전달 가능해야 함

일급 객체 조건

  1. 변수에 할당 가능
  2. 매개변수로 전달 가능
  3. 함수의 반환값으로 사용 가능

📍 예시

  • Java: Function<T, R>, 람다식
  • JavaScript: map, filter, reduce

🔸 객체지향 프로그래밍 (OOP)

  • 객체들의 협력으로 동작을 구성
  • 현실 세계의 개념을 추상화하여 클래스와 객체로 표현

📌 특징

특징설명
추상화핵심만 뽑아냄 (ex. 엘리베이터 → "올라가기", "내려가기")
캡슐화데이터 + 메서드 은닉, 외부는 인터페이스만 접근
상속성코드 재사용, 유지보수성 향상
다형성동일 인터페이스에 여러 구현 가능 (오버라이딩, 오버로딩)

🔐 SOLID 원칙

원칙설명
SRP (단일 책임 원칙)클래스는 하나의 책임만
OCP (개방-폐쇄 원칙)확장에 열려 있고, 변경에는 닫혀 있어야 함
LSP (리스코프 치환 원칙)하위 클래스는 상위 클래스의 역할을 대체할 수 있어야 함
ISP (인터페이스 분리 원칙)하나의 인터페이스는 하나의 역할에 집중해야 함
DIP (의존 역전 원칙)구현이 아닌 추상(인터페이스)에 의존해야 함

🔸 절차지향 프로그래밍 (Procedural Programming)

  • 명령어(절차) 중심의 흐름 제어
  • 빠른 성능, 단순한 구조
  • 코드 재사용 어려움, 유지보수 복잡

📍 예시 코드

const ret = [1, 2, 3, 4, 5, 11, 12];
let a = 0;

for (let i = 0; i < ret.length; i++) {
    a = Math.max(ret[i], a);
}

console.log(a); // 12

⚖️ 어떤 패러다임이 좋은가?

정답은 없다.
상황, 문제 도메인, 성능, 유지보수, 협업 방식 등에 따라 적절한 패러다임을 선택하는 것이 핵심

예시:

  • 게임 개발: OOP 중심
  • 수치 계산, 빠른 성능: 절차지향
  • 프론트엔드, 상태 동기화: 함수형 + MVVM

profile
멋있는 사람 - 일단 하자

0개의 댓글