디자인패턴 정리

한주영·2023년 10월 23일
0

스터디

목록 보기
6/6

싱글톤 패턴

1)전역변수를 사용하지않고 객체를 하나만 생성하도록 하여
생성된 객체를 어디서든지 참조할수있도록 하는 패턴

2)하나의 인스턴스만을 생성하며 getInstance메서드로모든 클라이언트에게 동일한 인스턴스를 반환

3)private생성자를 가진다
->생성된 싱글톤 오브젝트는 저장할수있는 자신곽 같은 타입의 static 필드를 정의

문제점?

1)의존관계상 클라이언트가 구체 클래스에 의존
2)private생성자로 인해 테스트가 어렵다
3)객체 인스턴스를 하나만 생성해서 공유하는 방식
-->싱글톤 객체를 stateful하게 설계했을경우 큰 장애요인이 발생

해결방법? -> 무상태 설계
1)특정 클라이언트가 의존적인 필드가있으면안됨
2)특정 클라이언트가 값을 변경할수있는 필드가 있으면 안됨
3)읽기전용으로 만들고 필드 대신에 자바에서 공유되지않은 지역변수,파리미터 ,ThreadLocal등을 사용

public class Singleton {
    
    private static Singleton instance;

    //private 생성자로 외부에서 객체 생성을 막아야한다.
    private Singleton() {
    }
    
    //외부에서는 getInstance()로 instance반환
    public static Singleton getInstance(){
        
        if(instance==null){
            //instance가 null일때만 생성
            instance=new Singleton();
        }
        return instance;
    }
}

가교(브릿지)패턴

1)추상부와 구현부를 분리하는패턴
2)기능 인터페이스를 통해정의 및 이용 해당 인터페이스를 따르는 클래스는 따로 구현
3)추상부와 구현부를 독립적으로 수정 및 확장
4)SOLID 원칙 중 단일 책임 원칙(SRP)과 개방 폐쇄 원칙(OCP)에 부합한 패턴

예시코드

package com.example.brige;


interface Shape{
    void draw();

}

interface Drawing{
    void drawShape(int x, int y);
}
class Drawing1 implements Drawing{


    @Override
    public void drawShape(int x, int y) {
        System.out.println("모양을 그린다");
    }
}

class Drawing2 implements Drawing{


    @Override
    public void drawShape(int x, int y) {
        System.out.println("모양을 그린다.");
    }
}
class Cricle implements Shape{

    private int x, y,radius;

    private Drawing drawing;

    public Cricle(int x, int y, int radius, Drawing drawing) {
        this.x = x;
        this.y = y;
        this.radius = radius;
        this.drawing = drawing;
    }

    @Override
    public void draw() {

        drawing.drawShape(x,y);
        System.out.println("반지름이 radis인 원");


    }
}
class Square implements Shape{

    private int x,y,side;
    private Drawing drawing;

    public Square(int x, int y, int side, Drawing drawing) {
        this.x = x;
        this.y = y;
        this.side = side;
        this.drawing = drawing;
    }

    @Override
    public void draw() {
           drawing.drawShape(x,y);
        System.out.println("측면이 있는 정사각형"+side);
    }
}


public class BridgePatternDemo {

    public static void main(String[] args) {


        Drawing drawing= new Drawing1();
        Drawing drawing1= new Drawing2();

        Shape circle= new Cricle(1,2,3,drawing);
        Shape square= new Square(4,5,6,drawing1);

        circle.draw();;
        square.draw();
    }
}

전략패턴

1)알고리즘을 객체 단위로 캡슐화하는 패턴
2)인터페이스를 통해 정의 및 이용
3)사용자는 알고리즘을 필요에따라 바꿔서 사용 가능
3)SOLID 원칙 중 개방 폐쇄 원칙(OCP)에 부합한 패턴

예시코드

거래전략관련 인터페이스 정의

public interface PaymentStrategy {
    void pay(int amount);
}
public class CreditCardPayment implements PaymentStrategy {
    private String cardNumber;
    private String name;

    public CreditCardPayment(String cardNumber, String name) {
        this.cardNumber = cardNumber;
        this.name = name;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + "원을 신용카드로 결제했습니다.");
    }
}

public class PayPalPayment implements PaymentStrategy {
    private String email;

    public PayPalPayment(String email) {
        this.email = email;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + "원을 PayPal로 결제했습니다.");
    }
}
public class ShoppingCart {
    private PaymentStrategy paymentStrategy;

    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    public void checkout(int amount) {
        paymentStrategy.pay(amount);
    }
}

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

        // 신용카드로 결제
        PaymentStrategy creditCardPayment = new CreditCardPayment("1234-5678-9012-3456", "John Doe");
        cart.setPaymentStrategy(creditCardPayment);
        cart.checkout(100);

        // PayPal로 결제
        PaymentStrategy paypalPayment = new PayPalPayment("john@example.com");
        cart.setPaymentStrategy(paypalPayment);
        cart.checkout(50);
    }
}

빌더패턴

1)복잡한 객체를 단계별로 생성할수 있는 생성디자인 패턴
2)생성자에 들어갈 매개변수를 메서드로 하나하나 받아들이고 통합 빌드해서 객체를 생성하는 방식

쉬운 예로 햄버거를 들수있음
수제 햄버거를 주문할때 빵이나 패티 속 재료들은 주문하는 사람이
마음대로 결정할수있듯이
선택적 속재료를 보다 유연하게 받아 다양한 타입의 인스턴스를 생성할수있다.

자바 빈즈 패턴으로 햄버거 클래스를 정의할때

class Hamburger {
    // 필수 매개변수
    private int bun;
    private int patty;

    // 선택 매개변수
    private int cheese;
    private int lettuce;
    private int tomato;
    private int bacon;
    
    public Hamburger() {}

    public void setBun(int bun) {
        this.bun = bun;
    }

    public void setPatty(int patty) {
        this.patty = patty;
    }

    public void setCheese(int cheese) {
        this.cheese = cheese;
    }

    public void setLettuce(int lettuce) {
        this.lettuce = lettuce;
    }

    public void setTomato(int tomato) {
        this.tomato = tomato;
    }

    public void setBacon(int bacon) {
        this.bacon = bacon;
    }
}

기존 생성자 오버로딩에서 나타났던 가독성 문제점이 사라지고 선택적인 파라미터에 대해 해당되는 Setter 메서드를 호출함으로써 유연적으로 객체 생성이 가능해졌지만, 이러한 방식은 객체 생성 시점에 모든 값들을 주입 하지 않아 일관성(consistency) 문제와 불변성(immutable) 문제가 나타나게 된다.

public static void main(String[] args) {
    // 모든 재료가 있는 햄버거
    Hamburger hamburger1 = new Hamburger();
    hamburger1.setBun(2);
    hamburger1.setPatty(1);
    hamburger1.setCheese(2);
    hamburger1.setLettuce(4);
    hamburger1.setTomato(6);
    hamburger1.setBacon(8);

    // 빵과 패티 치즈만 있는 햄버거
    Hamburger hamburger2 = new Hamburger();
    hamburger2.setBun(2);
    hamburger2.setPatty(1);
    hamburger2.setCheese(2);

    // 빵과 패티 베이컨만 있는 햄버거
    Hamburger hamburger3 = new Hamburger();
    hamburger3.setBun(2);
    hamburger2.setPatty(1);
    hamburger3.setBacon(8);
}
public static void main(String[] args) {

    // 생성자 방식
    Hamburger hamburger = new Hamburger(2, 3, 0, 3, 0, 0);

    // 빌더 방식
    Hamburger hamburger = new Hamburger.Builder(10)
        .bun(2)
        .patty(3)
        .lettuce(3)
        .build();
}

빌더패턴에서는 이러한문제를 해결하기위해 별도의 Builder클래스를 만들어
step-by-step으로 값을 입력한 후 최종적으로 build메소드로 하나의 인스턴스를 생성하여 리턴하는 패턴

출처:https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EB%B9%8C%EB%8D%94Builder-%ED%8C%A8%ED%84%B4-%EB%81%9D%ED%8C%90%EC%99%95-%EC%A0%95%EB%A6%AC

팩토리메서드 패턴

1)부모클래스에서 객체들을 생성할수있는 인터페이스를 제공하지만
클래스들이 생성될 객체의 유형을 변경할수있도록 하는 생성 패턴

2)인스턴스 생성을 서브 클래스에게 위임

예시코드
사용자 관리프로그램이있고 네이버계정으로 가입할수있다고 가정

User인터페이스 정의

public interface User{
    void signup();
}

User인터페이스를 구현하는 NaverUser클래스 정의

public class NaverUser implements User{
    @Override
    public void signup(){
        System.out.println("네이버 아이디로 가입");
    }
}

추상클래스로 UserFactory를 정의

-외부에서 객체를 생성할때는 newInstance를 호출
-실제로 어떤 객체를 생성할지는 추상메서드로 정의해서 하위클래스에서 정의

public abstract class UserFactory {

    public User newInstance() {
        User user = createUser();
        user.signup();
        return user;
    }

    protected abstract User createUser();
}
public class NaverUserFactory extends UserFactory {
    @Override
    protected User createUser() {
        return new NaverUser();
    }
}

UserFactory 를 상속받는 NaverUserFactory 를 정의
NaverUser 를 반환하도록 오버라이드

UserFactory userFactory = new NaverUserFactory();
User user = userFactory.newInstance();

클라이언트 코드에서 NaverUser클래스에 대한 의존성 없이 사용 가능
의존성 주입을 사용해서 외부에서 Factory클래스를 받아온다면 NaverUserFactory에 대한 의존성도 제거 가능

출처:https://bcp0109.tistory.com/367

퍼사드 패턴

어떤 소프트웨어의 다른 커다란 코드 부분에대하여 간략하된 인터페이스를 제공해주는 디자인패턴

퍼사드 패턴의 구조

/* Complex parts */

class CPU {
	public void freeze() { ... }
	public void jump(long position) { ... }
	public void execute() { ... }
}

class Memory {
	public void load(long position, byte[] data) {
		...
	}
}

class HardDrive {
	public byte[] read(long lba, int size) {
		...
	}
}

/* Façade */

class Computer {
	public void startComputer() {
        CPU cpu = new CPU();
        Memory memory = new Memory();
        HardDrive hardDrive = new HardDrive();
		cpu.freeze();
		memory.load(BOOT_ADDRESS, hardDrive.read(BOOT_SECTOR, SECTOR_SIZE));
		cpu.jump(BOOT_ADDRESS);
		cpu.execute();
	}
}

/* Client */

class You {
	public static void main(String[] args) throws ParseException {
		Computer facade = /* grab a facade instance */;
		facade.startComputer();
	}
}
profile
백엔드개발자가 되고싶은 코린이:)

0개의 댓글