CS

디자인 패턴1

blackmilktea 2024. 1. 12. 21:50

디자인 패턴이란?

 

프로그램을 설계할 때 발생했던 문제점들을 객체 간의 상호 관계 등을 이용하여 해결할 수 있도록 하나의 '규약' 형태로 만들어 놓은 것을 의미

 

1. 싱글톤 패턴(Singleton pattern)

하나의 클래스에 오직 하나의 인스턴스 만 가지는 패턴

하나의 인스턴스를 만들어 놓고 해당 인스턴스를 다른 모듈들이 공유하는 형태로 보통 데이터베이스 연결 모듈에 많이 사용

 

java_code

더보기
class Singleton {
    private static class singleInstanceHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    public static 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
*/

장점: 사용하기 쉽고 실용적이며, 인스턴스를 생성할 때 드는 비용 감소.
단점: 의존성이 높아 독립적인 인스턴스를 만들기 어렵기 때문에 TDD 할 때 걸림돌이 됨.

TDD(Test Driven Development, 테스트 주도 개발)
Write a failing test -> Make the pass test -> Refactor -> Write a failing test
위 과정을 반복하는 매우 짧은 개발 사이클을 반복하는 소프트웨어 개발 프로세스 중 하나
TDD는 단위 테스트를 주로 하는데 테스트가 독립적이어야 하며 어떤 순서로든 테스트를 실행할 수 있어야 함.

 

따라서 모듈간의 강한 결합을 DI를 통해 조금 더 느슨하게 만들어 해결할 수 있음.

 

의존성 주입 DI(Dependency Injection)

메인 모듈이 '직접' 다른 하위 모듈에 대한 의존성을 주기보다

중간에 의존성 주입자(Deoendency Injector)가 이 부분을 가로채 메인 모듈이 '간접'적으로 의존성을 주입하는 방식

이를 통해 상위 모듈은 하위 모듈에 대한 의존성이 떨어지게 되며 이를 디커플링이라고도 함.

 

장점: 모듈들을 쉽게 교체할 수 있는 구조가 되어 테스팅하기 쉽고, 마이그레이션 하기도 수월해 짐.
구현할 때 추상화 레이어를 넣고 이를 기반으로 구현체를 넣어주기 때문에 애플리케이션 의존성 방향이 일관되고

애플리케이션을 쉽게 추론 가능, 모듈 간의 관계들이 조금 더 명확해 짐.


단점: 모듈들이 분리되므로 클래스 수가 늘어나 복잡성이 증가될 수 있으며, 약간의 런타임 페널티가 생기기도 함.

마이그레이션(Migration): 한 운영환경에서 다른 운영환경으로 옮겨가는 과정

 

 

2. 팩토리 패턴(Factory pattern)

객체를 사용하는 코드에서 객체 생성 부분을 떼어내 추상화한 패턴이자 상속 관계에 있는 두 클래스에서 상위 클래스가 중요한 뼈대를 결정하고, 하위 클래스에서 객체 생성에 관한 구체적인 내용을 결정하는 패턴

 

상위 클래스와 하위 클래스가 분리되기 때문에 느슨한 결합을 가지며 상위 클래스에서는 인스턴스 생성 방식에 대해 전혀 알 필요가 없기 때문에 더 많은 유연성을 갖게 됨.

객체 생성 로직이 따로 떼어져 있기 때문에 코드를 리팩터링 하더라도 한 곳만 고칠 수 있게 되니 유지 보수성이 증가


CoffeeFactory 밑에 Coffee 클래스를 놓고

해당 클래스를 상속하는 Latte, Espresso 클래스를 기반으로 구현한 java_code

더보기
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 HelloWorld{ 
     public static void main(String []args){ 
        Coffee latte = CoffeeFactory.getCoffee("Latte", 4000);
        Coffee ame = CoffeeFactory.getCoffee("Americano",3000); 
        System.out.println("Factory latte ::"+latte);
        System.out.println("Factory ame ::"+ame); 
     }
} 
/*
Factory latte ::Hi this coffee is 4000
Factory ame ::Hi this coffee is 3000
*/

 

3. 전략 패턴(Strategy pattern) 또는 정책 패턴(Policy pattern)

객체의 행위를 바꾸고 싶은 경우 '직접' 수정하지 않고

전략이라고 부르는 '캡슐화한 알고리즘'을 컨텍스트 안에서 바꿔주면서 상호 교체가 가능하게 만드는 패턴

컨텍스트: 개발자가 어떠한 작업을 완료하는 데 필요한 모든 관련 정보

 

물건을 살 때 네이버페이, 카카오페이 등 다양한 방법으로 결제하듯

어떤 아이템을 살 때 LUNACard로 사는 것과 KAKAOCard로 사는 것을 구현한 java_code

더보기
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
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<Item>();
    }
    
    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);
        
        // pay by LUNACard
        cart.pay(new LUNACardStrategy("kundol@example.com", "pukubababo"));
        // pay by KAKAOBank
        cart.pay(new KAKAOCardStrategy("Ju hongchul", "123456789", "123", "12/01"));
    }
}
/*
400 paid using LUNACard.
400 paid using KAKAOCard.

쇼핑 카트에 아이템을 담아 LUNACard 또는 KAKAOCard라는 두개의 전략으로 결제
*/

 

 

'면접을 위한 CS 전공지식 노트'를 기반으로 작성한 글입니다.

저자 블로그 https://blog.naver.com/jhc9639/222704238003