디자인 패턴

과녁스·2022년 2월 9일
0

Java

목록 보기
6/8
post-thumbnail

개요


Java 프로그램 설계 시 디자인 패턴에 대한 이해가 필요하여 학습한 내용을 정리해보았습니다. (계속해서 내용 추가 중입니다😅)

디자인 패턴이란❓


  • 자주 사용하는 설계 패턴을 정형화시켜서 유형 별로 가장 최적의 방법으로 개발할 수 있도록 정해둔 설계
  • 알고리즘과 유사 하지만, 명확하게 정답이 있는 것이 아니며, 프로젝트의 상황에 맟추어 적용 가능하다

Gof 디자인 패턴

소프트웨어 설계 시 기존에 경험이 매우 중요하다. 하지만 모든 사람들이 다양한 경험을 가지고 있을 수는 없기 때문에, 이러한 지식을 공유하기 위해서 나온것이 GOF(Gang of Four)의 디자인 패턴이다.

Gof 디자인 패턴은 객체지향 개념에 따른 설계 중 재사용할 경우 유용한 설계를 디자인 패턴으로 정리해둔 것이다.

Gof 디자인 패턴은 총 23개 이며, 이를 잘 이해하고 활용한다면, 경험이 부족하더라도 좋은 소프트웨어를 설계할 수 있다.

디자인 패턴 장점

  1. 개발자(설계자) 간의 원활한 소통에 도움이 된다.
  2. 소프트웨어의 구조를 파악하는데 용이하다.
  3. 재사용을 통해 개발 시간 단축이 가능하다.
  4. 설계 변경 요청에 대하여 유연하게 대처를 할 수 있다.

디자인 패턴 단점

  1. 객체지향 설계/구현이 쉽지 않다.
  2. 초기 투자 비용이 부담 될 수 있다.

패턴의 종류


생성 패턴

객체를 생성하는 것과 관련된 패턴으로, 객체의 생성과 변경이 전체 시스템에 미치는 영향을 최소화하고, 코드의 유연성을 높여준다.

  • Factory Method
  • Singletone
  • Prototype
  • Builder
  • Abstarct Factory
  • Chaining(Gof 패턴 아님)

구조 패턴

프로그램 내의 자료구조나 인터페이스 구조 등 프로그램 구조를 설계하는데 활용이 될 수 있는 패턴 클래스, 객체들의 구성을 통해서 더 큰 구조를 만들 수 있게 해준다.
큰 규모의 시스템에서는 많은 클래스들이 서로 의존성을 가지게 되는데, 이런 복잡한 구조를 개발하기 쉽게 만들어 주고, 유지 보수 하기 쉽게 만들어 준다.

  • Adapter
  • Composite
  • Bridge
  • Decorator
  • Facade
  • Flyweight
  • Proxy

행위 패턴

반복적으로 사용되는 객체들의 상호작용을 패턴화한 것으로, 클래스나 객체들이 상호작용하는 방법과 책임을 분산하는 방법을 제공 한다. 행위 패턴은 행위 관련 패턴을 사용하여 독립적으로 일을 처리하고자 할때 사용한다.

  • Template Method
  • Interpreter
  • Iterator
  • Observer
  • Strategy
  • Visitor
  • Chain of responsibility
  • Command
  • Mediator
  • State
  • Memento

디자인 패턴 상세


생성 패턴

싱글톤 패턴

어떠한 클래스(객체)가 유일하게 1개만 존재 할 때 사용한다. 이를 주로 사용하는 곳은 서로 자원을 공유할 때 사용한다. 가장 좋은 예시로 프린터가 해당된다.

사용 예시

  • 스피링 Bean
  • TCP Socket 통신에서 서버와 연결된 connect
public class SocketClient {

    private static SocketClient socketClient = null;

    // 외부에서 생성자를 호출하지 못하도록 접근제어를 private으로 설정
    private SocketClient(){}

	// 외부에서 인스턴스를 호출
    public static SocketClient getInstance(){

		// 인스턴스가 없다면 생성하여 설정
        if(socketClient == null){
            socketClient = new SocketClient();
            System.out.println("socket new instance");
        }

        return socketClient;
    }

	// getinstance를 통하여 객체의 메서드를 사용할 수 있음
    public void connect(){
        System.out.println("socket");
    }

}

구조 패턴

어댑터 패턴

호환성이 없는 기존 클래스이 인터페이스를 변환하여 재사용 할 수 있도록 한다. SOLID중에서 개방폐쇄 원칙(OCP)를 따른다.

실생활에서 예를 들면 110v를 220v로 변경해주거나, 그 반대로 해주는 '돼지코'라고 불리는 변환기를 예를 들 수 있다.

이미지 출처 : [Java] 디자인 패턴 - 어댑터 패턴(Adapter Pattern)

어댑터 패턴은 아래와 같은 예시를 들 수 있다.

  1. 각각 전압에 따른 인터페이스 생성
public interface Electronic110V {
    public void powerOn();
}
public interface Electronic220V {
    public void connect();
}
  1. 콘센트를 이용하여 사용할 전자 기기 생성
public class AirConditioner implements Electronic220V{
    @Override
    public void connect() {
        System.out.println("220v 에어컨 ON");
    }
}
public class Cleaner implements Electronic220V{
    @Override
    public void connect() {
        System.out.println("220v 청소기 ON");
    }
}
public class HairDryer implements Electronic110V {
    @Override
    public void powerOn() {
        System.out.println("110v 헤어드라이기 ON");
    }
}
  1. 220v 제품을 110v로 변환시켜주는 어댑터 생성
public class SocketAdapter implements Electronic110V {

    private Electronic220V electronic220V;

    public SocketAdapter(Electronic220V electronic220V){
        this.electronic220V = electronic220V;
    }

    @Override
    public void powerOn() {
        electronic220V.connect();
    }
}
  1. 사용할 전자기기에 어댑터 결합
public class Main {

    public static void main(String[] args) throws InterruptedException {
	    // ...
        
        // 110v 제품
        HairDryer hairDryer = new HairDryer();
        connect(hairDryer);

		// 220v 제품을 110v로 사용하기 위하여 어댑터 결합
        Cleaner cleaner = new Cleaner();
        AirConditioner airConditioner = new AirConditioner();

        //connect(cleaner);
        //connect(airConditioner);

        Electronic110V _cleaner = new SocketAdapter(cleaner);
        Electronic110V _airConditioner = new SocketAdapter(airConditioner);

        connect(_cleaner);
        connect(_airConditioner);
        
        //...
    }

	// 110v만 출력 가능
    public static void connect(Electronic110V electronic110V){
        electronic110V.powerOn();
    }
}

프록시 패턴

Proxy는 '대리인' 이라는 뜻으로써, 대신해서 처리하는 역할을 한다. Proxy Class를 통해서 대신 전달하는 형태로 설계되며, Cient는 Proxy로 부터 결과를 받는다. 대표적으로 많이 사용되는 사례로는 'Cache'의 기능으로 활용이 가능하다.

SOLID중에서 개방폐쇄 원칙(OCP)과 의존 역전 원칙(DIP)를 따른다.

public interface IBrowser {
    Html show();
}

public class Html {
    private String url;

    public Html(String url){
        this.url = url;
    }
}

public class Browser implements IBrowser {

    private String url;

    public Browser(String url){
        this.url = url;
    }

    @Override
    public Html show() {
        System.out.println("Browser loading html from "+url);
        return new Html(url);
    }
}

public class BrowserProxy implements IBrowser {

    private String url;
    private Html html;

    public BrowserProxy(String url){
        this.url = url;
    }

    @Override
    public Html show() {
        if(html == null){
            this.html = new Html(url);
            System.out.println("BrowserProxy loading html from "+url);
        }
        // html 객체가 null이 아니라면 캐싱
        System.out.println("BrowserProxy use cache html");
        return html;
    }
}

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

public class AopProxy implements IAopBrowser {

    private String url;
    private Html html;
    private Runnable before;	// aop 기능 구현을 위한 시작 전
    private Runnable after;		// aop 기능 구현을 위한 시작 후

    public AopProxy(String url){
        this.url = url;
    }

    public AopProxy(String url, Runnable before,  Runnable after) {
        this.url = url;
        this.before = before;
        this.after = after;
    }

    @Override
    public Html show() throws InterruptedException {
    	before.run();

		// 캐시와 동일한 기능 구현
        if(html == null){
            this.html = new Html(url);
            System.out.println("BrowserProxy loading html from "+url);
        }
        System.out.println("BrowserProxy use cache html");
        Thread.sleep(2000); // 시간 체크를 위해서
        
        after.run();
        
        return html;
    }
}

public interface IAopBrowser {
    Html show() throws InterruptedException;
}

public class Main {

  public static void main(String[] args) throws InterruptedException {

        // 캐시 기능이 없을 경우 매번 로딩이 발생
        Browser browser = new Browser("www.naver.com");
        browser.show();
        browser.show();
        browser.show();

        // 프록시를 사용하여 캐시기능 구현
        IBrowser browser = new BrowserProxy("www.naver.com");
        browser.show();
        browser.show();

		// aop 기능을 활용한 프록시 패턴
		AtomicLong start = new AtomicLong(); // 동시성 문제 처리를 위한 AtomicLong 사용
        AtomicLong end = new AtomicLong();
        
		IBrowser aopBrowser = new AopProxy("www.naver.com",
        	()->{
            	System.out.println("before");
                start.set(System.currentTimeMills());
            },
            ()->{
            	long now = System.currentTimeMills();
                end.set(now - start.get());
            }
            
        aopBrowser.show();
        System.out.println(endTime + " ms");
  }
}

aop 패턴은 프록시 패턴을 활용하고 있다. 특정한 메서드에 앞/뒤로 arguments 또는 기능을 조작할 수 있으며, 흩어져있는 기능을 하나로 통합할 수 있다. Http Client가 통신할 때 클라이언트 코드의 시간을 체크하거나, DB 트랜잭션이 작업이 있는 경우 aop를 활용하여 시간 체크 등이 가능하다.

출처 및 참고

  • 한번에 끝내는 Java/Spring 웹 개발 마스터 초격차 패키지 Online. - 패스트캠프
profile
ㅎㅅㅎ

0개의 댓글