객체 지향 프로그래밍/디자인 패턴

싱글톤, 프록시, 데코레이터 패턴

ghtis1798 2020. 12. 31. 17:17

객체 지향 디자인 패턴 종류

싱글톤, 프록시, 데코레이터

1. 싱글톤 패턴

 

이번엔 싱글톤 패턴에 대해서 알아보겠습니다. 🙂

싱글톤 패턴이란 인스턴스를 하나만 만들어 사용하기 위한 패턴입니다.

예를 들어 단 하나의 관리자 계정을 여러 명이서 사용하는 경우입니다.

이런 경우 어떻게 코딩하면 좋을까요?

하나의 Admin 계정을 여러 명이 공유해서 사용하는 프로그램을 짜보겠습니다.

package exSingleton;

// Admin 클래스
public class Admin {
    // 정적 참조 변수
    static Admin one_id;
    // 객체 생성 방지
    private Admin() {};

    // 정적 메소드
    public static Admin getInstance() {
        // 참조 변수 == null일 때 객체 생성
        if (one_id == null) {
            one_id = new Admin();
        }

        // 이미 생성된 경우 return
        return one_id;
    }

    public void Work(String s) {
        System.out.println(s+" 작업을 수행했습니다.");
    }
}

 

Admin 클래스에서 정적 참조 변수, 정적 메소드, private 생성자를 정의했습니다.

private을 생성자 앞에 붙인 이유는 객체 생성을 방지하기 위함입니다.

따라서 Admin 클래스의 정적 참조 변수는 1개만 존재합니다.

이 정적 참조 변수는 정적 메소드 getInstance()를 통해 인스턴스로 생성되고,

이미 생성된 경우는 그대로 return합니다.

아래의 Admin 계정을 받아와서 사용하는 유저들은 모두 같은 'ID'를 사용하게 되는 것이죠.

package exSingleton;

public class User {
    public static void main(String[] args) {
        // 생성자 앞 private으로 인해 Admin 객체를 직접 생성하려고 하면 오류가 발생합니다. 
        // Admin admin_user = new Admin();

        // 따라서 정적 메소드를 사용해 Admin 참조변수에 인스턴스를 받아옵니다.
        Admin user1 = Admin.getInstance();
        Admin user2 = Admin.getInstance();
        Admin user3 = Admin.getInstance();

        // 참조 변수가 가리키는 인스턴스 주소 출력
        System.out.println(user1);
        System.out.println(user2);
        System.out.println(user3);

        // 하나의 Admin 인스턴스 계정으로 작업하기
        user1.Work("게시물 오픈");
        user2.Work("게시물 생성");
        user3.Work("게시물 삭제");
    }
}

 

싱글턴 패턴 적용 결과

 

user1, user2, user3은 전부 같은 메모리 주소를 참조하고 있습니다.

그리고 유일한 Admin 객체인 one_id를 getInstance()로 받아와서 Work 작업을 하는 구조입니다.

 

2. 프록시 패턴

 

저는 프록시(Proxy)라는 단어를 처음 들어봤습니다. 🙄

Proxy 사전 검색 결과

 

검색 결과 대리인이라는 뜻인데요.

프록시 패턴은 로직을 수행하는 중간에 해당 업무를 대신하는 대리자를 생성하는 패턴입니다.

그럼 프록시 패턴은 어떤 경우에 필요할까요? 🤔

대리자가 업무를 대신 해주도록 하면, 사용자가 일을 어떻게 처리하는지 모르게 할 수 있습니다.

보안과 관련된 문제를 해결하는데 유용할 것 같네요. 🙂

뿐만아니라 대리자는 어떤 업무를 하기 전에, 다른 추가 업무를 수행할 수도 있습니다.

로직의 흐름을 제어하고 다른 동작을 추가할 수도 있죠.

코드로 예시를 들어보겠습니다. 😶

은행 어플리케이션에서 고객은 자신의 계좌 잔액을 조회할 수 있습니다.

package exproxyPattern;

public interface Client {
    public String getName();
    public abstract float getBalance();
    public abstract String getInformation(Client client);
}
package exproxyPattern;

public class NormalClient implements Client{
    private String name;
    private float balance;

    @Override
    public String getName() {
        return name;
    }

    @Override
    public float getBalance() {
        return balance;
    }

    @Override
    public String getInformation(Client client) {
        return "고객님 성함은 : " + client.getName() + "잔액은 : " + client.getBalance();
    }
}

여기서 끝나면 일반 고객은 getInformation() 메소드로 다른 고객들의 정보도 확인할 수 있습니다.

package exproxyPattern;

public class Bank {
    public static void main(String[] args) {
        NormalClient client1 = new NormalClient("루피", 5500);
        NormalClient client2 = new NormalClient("초파", 3000);

        // 루피가 초파의 정보 조회
        System.out.println(client1.getInformation(client2));
    }
}

 

프록시 패턴 적용 전

 

루피가 초파의 계좌 정보를 조회할 수 있네요.

그렇게 두면 안되겠죠. 🙂 프록시 패턴을 사용해 자기 자신만 조회할 수 있도록 바꿔주겠습니다.

package exproxyPattern;

public class Proxy implements Client {
    private Client client;

    public Proxy (Client client) {
        this.client = client;
    }

    @Override
    public String getName() {
        return client.getName();
    }

    @Override
    public float getBalance() {
        return client.getBalance();
    }

    @Override
    public String getInformation(Client others) {
        // client와 보려고하는 others 정보가 같을 때만 getInformation 메소드 호출
        if (this.client.getName().equals(others.getName())) {
            return this.client.getInformation(others);
        }

        return "다른 사람의 계좌 정보는 확인할 수 없습니다.";
    }
}
package exproxyPattern;

// 은행 클래스
public class Bank {
    public static void main(String[] args) {

        NormalClient loopy = new NormalClient("루피", 5500);
        NormalClient chopa = new NormalClient("초파", 3000);

        // 루피의 대리인 생성
        Proxy proxy = new Proxy(loopy);

        // 대리인을 통해 초파의 정보 조회
        System.out.println(proxy.getInformation(chopa));

        // 대리인을 통해 자신의 정보를 조회
        System.out.println(proxy.getInformation(loopy));
        System.out.println(proxy.getName());
        System.out.println(proxy.getBalance());
    }
}

 

프록시 패턴 적용 후

 

대리인(프록시)을 통한 정보 조회 시 이제 자기 자신의 정보만 조회가 가능합니다. 🙂

프록시 패턴은 권한이나 로직의 흐름을 제어하기 위해 대리자를 생성하는 패턴입니다.

 

3. 데코레이터 패턴

유사한 패턴으로 데코레이터 패턴이 있는데요.

프록시 패턴과 같은 방식으로 구현하면 됩니다. 😙

한 가지 차이가 있다면 리턴값에 변화를 줄 수 있다는 것입니다.

이상으로 프록시 패턴과 데코레이터 패턴에 대해 알아보았습니다. 👍

 

참고 자료

asfirstalways.tistory.com/335

 

[DP] 1. 싱글턴 패턴(Singleton pattern)

#1. 싱글턴 패턴(Singleton Pattern) 싱글턴 패턴이란 인스턴스를 하나만 만들어 사용하기 위한 패턴이다. 커넥션 풀, 스레드 풀, 디바이스 설정 객체 등의 경우, 인스턴스를 여러 개 만들게 되면 자원

asfirstalways.tistory.com

https://jdm.kr/blog/235

 

프록시 패턴(Proxy Pattern) :: JDM's Blog

프록시 패턴 정의 실제 기능을 수행하는 객체Real Object 대신 가상의 객체Proxy Object를 사용해 로직의 흐름을 제어하는 디자인 패턴입니다. 프록시 패턴 특징 원래 하려던 기능을 수행하며 그외의

jdm.kr