본문 바로가기

JAVA/디자인 패턴

[Design Pattern] Prototype(프로토타입) 패턴이란?

반응형
생성 패턴(Creational Pattern)


프로토타입 패턴(Prototype Pattern)은 객체를 생성할 때, 기존 객체의 복사를 통해 새로운 객체를 생성하는 디자인 패턴 중 하나입니다. 이는 객체 생성을 위한 복잡한 과정을 간소화하고, 또한 유사한 객체를 생성할 때 객체 생성 시간과 비용을 줄일 수 있는 장점이 있습니다.

프로토타입 패턴을 사용하면, 객체 생성 과정에서 복잡한 로직이 필요한 경우, 이미 생성된 객체를 복제(clone)하여 새로운 객체를 생성합니다. 이 때 복제를 통해 생성된 객체를 프로토타입 객체(Prototype)라고 합니다.

프로토타입 패턴을 구현할 때는, 복제를 위해 자바에서 제공하는 Cloneable 인터페이스를 구현하고, clone() 메서드를 오버라이드하여 구현합니다.

프로토타입 패턴은 객체의 복제를 통해 새로운 객체를 생성하기 때문에, 기존 객체와 새로운 객체가 독립적으로 존재해야 합니다. 이를 위해 프로토타입 객체는 깊은 복사(deep copy) 또는 얕은 복사(shallow copy)를 이용하여 객체를 복제합니다.

프로토타입 패턴은 다음과 같은 장점을 가지고 있습니다.

 

  1. 객체 생성 비용과 시간을 줄일 수 있습니다.
  2. 새로운 객체를 생성할 때 객체 생성 과정에서 발생할 수 있는 오버헤드를 줄일 수 있습니다.
  3. 객체 생성 방법이 복잡하거나 생성할 객체의 타입이 동적으로 결정되는 경우 유용합니다.

하지만 프로토타입 패턴은 객체를 복제하여 생성하기 때문에, 복제할 객체가 많은 경우 메모리 사용량이 늘어나고, 객체 생성 비용을 줄이기 위해 객체의 상태를 공유하는 경우 부작용이 발생할 수 있습니다. 따라서 프로토타입 패턴을 적용할 때에는 이러한 부작용을 고려하여 적절한 복사 방법을 선택해야 합니다.

 

 

Prototype 패턴 예제

 

Java에서 Prototype 패턴을 구현하는 방법은 `Cloneable` 인터페이스를 구현하고 `clone()` 메서드를 오버라이드하여 구현하는 것입니다.


다음은 Java에서 Prototype 패턴을 구현하는 예제 코드입니다.

Diagrams

 

 

public abstract class Shape implements Cloneable {
    private String id;
    protected String type;
    
    public String getType() {
        return type;
    }
    
    public String getId() {
        return id;
    }
    
    public void setId(String id) {
        this.id = id;
    }
    
    public abstract void draw();
    
    @Override
    public Object clone() {
        Object clone = null;
        
        try {
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        
        return clone;
    }
}

public class Rectangle extends Shape {
    public Rectangle() {
        type = "Rectangle";
    }
    
    @Override
    public void draw() {
        System.out.println("Inside Rectangle::draw() method.");
    }
}

public class Circle extends Shape {
    public Circle() {
        type = "Circle";
    }
    
    @Override
    public void draw() {
        System.out.println("Inside Circle::draw() method.");
    }
}

public class ShapeCache {
    private static Map<String, Shape> shapeMap = new HashMap<>();
    
    public static Shape getShape(String shapeId) {
        Shape cachedShape = shapeMap.get(shapeId);
        return (Shape) cachedShape.clone();
    }
    
    public static void loadCache() {
        Circle circle = new Circle();
        circle.setId("1");
        shapeMap.put(circle.getId(), circle);
        
        Rectangle rectangle = new Rectangle();
        rectangle.setId("2");
        shapeMap.put(rectangle.getId(), rectangle);
    }
}

public class Main {
    public static void main(String[] args) {
        ShapeCache.loadCache();

        Shape clonedShape = ShapeCache.getShape("1");
        System.out.println("Shape : " + clonedShape.getType());
        clonedShape.draw();

        Shape clonedShape2 = ShapeCache.getShape("2");
        System.out.println("Shape : " + clonedShape2.getType());
        clonedShape2.draw();
    }
}


위 예제 코드에서 `Shape` 클래스는 추상 클래스로, `Cloneable` 인터페이스를 구현하고 `clone()` 메서드를 오버라이드하여 복제 가능한 객체로 만듭니다. `Shape` 클래스를 상속받은 `Rectangle` 클래스와 `Circle` 클래스는 각각 다른 `type` 속성을 가지고 있습니다.

`ShapeCache` 클래스는 `shapeMap`이라는 `HashMap` 객체를 사용하여 `Shape` 객체를 캐싱합니다. `loadCache()` 메서드에서는 `Circle` 객체와 `Rectangle` 객체를 생성하여 `shapeMap`에 추가합니다. `getShape()` 메서드는 `shapeMap`에서 해당 `shapeId`에 해당하는 `Shape` 객체를 가져와 복제(clone)한 후 반환합니다.

`Main` 클래스에서는 `ShapeCache` 클래스의 `loadCache()` 메서드를 호출한 후, `ShapeCache` 클래스의 `getShape()` 메서드를 사용하여 `Shape` 객체를 가져와서 복제한 후, `getType()` 메서드를 호출하여 객체의 `type` 속성을 출력합니다. 이를 통해 `Shape` 객체가 복제(clone)되어 객체의 내용이 변경되지 않음을 확인할 수 있습니다.

위 예제 코드를 실행하면 다음과 같은 결과가 나타납니다.

Shape : Circle
Inside Circle::draw() method.
Shape : Rectangle
Inside Rectangle::draw() method.

 

반응형