_ 개발

[DESIGN PATTERN/JAVA] Prototype - 프로토타입 패턴

옴뇽뇽 2024. 11. 19. 15:30

 

목 차

Prototype : 복사해서 인스턴스 만들기

Prototype 패턴의 구성 요소

Prototype 패턴의 구현예시

Prototype 패턴의 특징

 

Prototype : 복사해서 인스턴스 만들기

프로토타입 패턴은 객체 생성 패턴 중 하나로, 원형이 되는 인스턴스를 기본으로 하여 객체를 복제하여 새로운 인스턴스를 만드는 패턴이다.우리 주변에서 이 프로토타입 패턴이 적용된 것이 무엇이 있을까? 티켓 발권을 예로 들어보자. 한 콘서트를 위해 티켓을 발권할 때, 매번 새로운 티켓 디자인을 만들 필요 없이 기존 티켓의 고정된 디자인을 가지고 이미지, 공연 명, 관람 일시, 예매번호 등만 적절히 변경해서 발권하면 된다. 또 베이커리에서 쿠키나 빵을 만들 때, 일일이 모양을 만들지 않고 틀을 이용해 일정한 모양으로 여러개를 찍어낸다.

프로토타입 패턴은 새로운 객체(인스턴스)를 만들기 위해 기존 객체를 복제(clone)하기 때문에 객체 초기화 과정이 단순화되며, 생성 비용을 줄일 수 있어 개발 효율성이 높아진다.

베이킹 이미지. 쿠키를 만들 때 쿠키틀을 이용해서 동일한 모양을 빠르게 여러개 만들 수 있다.

 


Prototype 패턴의 구성 요소

Prototype 패턴의 다이어그램
Prototype 패턴의 다이어그램

1. Prototype 

  • 인스턴스를 복사해 새로운 인스턴스를 만드는 메서드 createClone()을 정의한 인터페이스

2. ConcretePrototype

  • Prototype 인터페이스를 구현하고, 새로운 인스턴스를 만드는 메서드 createClone()을 구체적으로 구현

3. Client

  • 인스턴스를 복사하는 createClone() 메서드로 Prototype을 복제하여 객체를 생성

Prototype 패턴의 구현 예시

프로토타입 패턴의 예제로 문자열 테두리 감싸거나 밑줄을 표시하는 프로그램을 만들어보자.

Prototype 패턴 예제 프로그램 다이어그램
Prototype 패턴 예제 프로그램 다이어그램

 

(1) Product 인터페이스

package framework;

public interface Product extends Cloneable {
    public abstract void use(String s);
    public abstract Product createClone();
}
  • 추상 메서드 use와 createClone이 선언된 인터페이스이며, 하위 클래스에서 기능에 맞게 구현한다.
  • Cloneable 인터페이스를 상속(extands)하여, Cloneable의 clone 메서드를 이용해 복제 가능하다.

(2) Manager 클래스

package framework;

import java.util.HashMap;
import java.util.Map;

public class Manager {
    private Map<String,Product> showcase = new HashMap<>();

    public void register(String name, Product prototype) {
        showcase.put(name, prototype);
    }

    public Product create(String prototypeName) {
        Product p = showcase.get(prototypeName);
        return p.createClone();
    }
}
  • 프로토타입 객체(Product)를 관리하는 클래스로 객체를 Map(showcase)에 등록하고, 이를 복제해 반환한다.
  • showcase : 복제할 Product를 담을 HashMap 객체
  • resigter() : 주어진 이름(name)과 Product 인터페이스(prototype)를 showcase에 등록한다.
  • create() : 특정 이름(prototypeName)으로 등록된 인스턴스를 showcase에서 찾아 복제한다.

(3) UnderlinePen 클래스, MessageBox 클래스

import framework.Product;

public class UnderlinePen implements Product {
    private char ulchar;

    public UnderlinePen(char ulchar) {
        this.ulchar = ulchar;
    }

    @Override
    public void use(String s) {
        int ulen = s.length();
        System.out.println(s);
        for (int i = 0; i < ulen; i++) {
            System.out.print(ulchar);
        }
        System.out.println();
    }

    @Override
    public Product createClone() {
        Product p = null;
        try {
            p = (Product)clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return p;
    }
}
import framework.Product;

public class MessageBox implements Product {
    private char decochar;

    public MessageBox(char decochar) {
        this.decochar = decochar;
    }

    @Override
    public void use(String s) {
        int decolen = 1 + s.length() + 1;
        for (int i = 0; i < decolen; i++) {
            System.out.print(decochar);
        }
        System.out.println();
        System.out.println(decochar + s + decochar);
        for (int i = 0; i < decolen; i++) {
            System.out.print(decochar);
        }
        System.out.println();
    }

    @Override
    public Product createClone() {
        Product p = null;
        try {
            p = (Product)clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return p;
    }
}
  • UnderlinePen 클래스, MessageBox 클래스는 각각 Product 인터페이스를 구현한다.
  • 각 클래스별 기능에 맞게 use 메서드를 구현한다.
    • UnderlinePen에서는 지정된 밑줄 문자(ulchar)로 문자열을 아래 출력하도록 use 메서드 구현
    • MessageBox에서는 지정된 데코레이션 문자(decochar)로 문자열을 감싸 출력하도록 use 메서드 구현
  • createClone() : 자신을 복제하는 clone 메서드를 사용하며, Cloneable 인터페이스를 구현하고 있는 클래스일때만 clone 메서드로 복사를 할 수 있다. (*아니면 예외 CloneNotSupportedException 발생) 
  • clone 메서드는 자신 클래스(혹은 해당 클래스의 하위 클래스)에서만 호출 가능하기 때문에 외부클래스에서 해당 클래스 복제 요청을 할겨우 createClone 메서드와 같이 다른 메서드에서 구현하여 이용해야 한다.

사용 예시 

Main 클래스

import framework.Manager;
import framework.Product;

public class Main {
    public static void main(String[] args) {
        Manager manager = new Manager();
        UnderlinePen upen = new UnderlinePen('=');
        MessageBox mbox = new MessageBox('*');
        MessageBox sbox = new MessageBox('/');

        // Prototype 인스턴스 등록하기
        manager.register("underlined message", upen);
        manager.register("warning box", mbox);
        manager.register("slash box", sbox);

        // Prototype 인스턴스 복제하여 사용하기
        Product p1 = manager.create("underlined message");
        p1.use("Hello, world.");

        Product p2 = manager.create("warning box");
        p2.use("Java Designe Pattern");

        Product p3 = manager.create("slash box");
        p3.use("Log for Anything!");
    }
}
  • Manager 인스턴스 생성 후 각 프로토타입을 register()를 호출하여 등록한다.
  • create() 메서드로 각 인스턴스 타입을 복제한 후, use()를 호출하여 화면에 출력한다.

실행 결과

Hello, world.
=============
*********************
*Java Design Pattern*
*********************
///////////////////
/Log for Anything!/
///////////////////

 


Prototype 패턴의 특징

☺︎다형성 실현을 통한 객체 생성 비용의 절감

  • 쿠키를 만들 때 예를 들면, 쿠키의 종류는 다른 부재료들을 사용해서 계속해서 새롭게 만들 수 있다. 이 것을 각각 클래스로 만들게 되면 클래스 수가 너무 많아 지기 때문에 소스 관리가 힘들어 진다.
  • 프로토타입 패턴을 이용하면 다양한 종류(하위 클라스)를 복제해 생성할 수 있기 때문에, 객체 생성과정을 단순하게 만들어 비용을 줄일 수 있다.

☺︎객체 복제를 통한 인스턴스 생성

  • Prototype 패턴은 new 키워드 대신 이미 존재하는 인스턴스를 복제하여 새로운 인스턴스를 생성한다.
  • 인스턴스 생성에 복잡한 과정이나 로직이 필요한 경우, 인스턴스를 복사하여 사용하는 것이 효율적이다.

☺︎프레임워크(Framework)와 생성하는 객체 분리

  • Client에서 Prototype 인터페이스를 사용하여 Prototype을 구현한 하위 클래스의 객체를 복사하여 사용한다.
  • Prototype을 구현한 하위 클래스가 변경되거나 추가돼도 Client, Prototype 인터페이스는 영향을 받지 않고 작동된다.

☹︎클론 메서드 구현의 어려움

  • 객체가 복잡하거나 순환 참조를 포함하는 경우, clone 메서드 구현하는 데 어려움이 있을 수 있다.

Ref.