[Design Pattern] Prototype Pattern

이은수, Lee EunSoo·2024년 10월 8일
0

DesignPattern

목록 보기
7/12
post-thumbnail

개요

프로토타입 패턴이란 new를 사용하여 객체를 만드는 것이 아니라 기존의 객체를 복사하여 새로운 객체를 생성

new, 생성자를 사용하지 않는부분은 팩토리 메소드 패턴과 유사하지만 프로토타입 패턴은 객체를 복사하여 새롭게 생성한다는 점이 다르다.

여기서 사용되는 복사는 shallow copy이다.

설명

  • Prototype
    • 복사될 수 있는 객체들이 가져야할 공통적인 인터페이스를 선언한다.
    • createCopy( )
  • ConcreatePrototype
    • Prototype인터페이스를 구현
  • Client
    • 새로운 객체를 복사하기 위해서 createCopy()메소드를 사용한다.

적용시점

  1. 프레임워크와 객체 생성을 분리하고 싶을때
  2. 클래스로 부터 객체 생성이 어려운 상황인 경우

코드 예시

다음과 같은 형태의 코드를 자바와 swift로 만들었다.

java

public interface Product extends Cloneable {
    public abstract void use(String s);

    public abstract Product createCopy();
}

하위 클래스에서 clone하기 위해서 Cloneable인터페이스를 상속받음

나머지메소드들은 추상 클래스로 분리함


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);
    }

	//Product의 객체를 여기서 생성
    public Product create(String prototypeName) {
        Product p = showcase.get(prototypeName);
        return p.createCopy();
    }
}

객체들을 생성하고 저장해놓는 클래스 Map에 불러오기 위한 이름 즉 문자열을 키값으로 생성된 객체는 value로 지정하고 객체들을 저장, 관리함


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 createCopy() {
        Product p = null;
        try {
            p = (Product) clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return p;
    }
}

decochar에 저장된 문자로 입력된 문자열을 감싸는 문자열을 출력함

createCopy()는 여기서 사용되지 않고 Manager클래스에서 사용됨

use()는 문자열을 출력하는 기능을 수행



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 createCopy() {
        Product p = null;
        try {
            p = (Product) clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return p;
    }
}

decochar에 저장된 문자로 입력된 문자열 다음줄에 밑줄형태의 문자열을 출력함

createCopy()와 use()는 MessageBox의 기능과 유사함 특히 createCopy()는 중복코드임


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('/');

        manager.register("strong message", upen);
        manager.register("warning box", mbox);
        manager.register("slash box", sbox);

        Product p1 = manager.create("strong message"); //결국 createCopy() 호출하게 됨
        p1.use("Hello, world.");
        Product p2 = manager.create("warning box");
        p2.use("Hello, world.");
        Product p3 = manager.create("slash box");
        p3.use("Hello, world.");

    }
}

Manager클래스가 Prototype의 역할을 한다.

객체 생성은 매니저 클래스에서 전부 담당한다. 매니저에서 객체를 저장하고 있다가 use하게 되면 저장되어있던 객체의 복사본을 반환해서 각 클래스에 반환하고 이 객체를 이용해서 MessageBox나 UnderlinePen에서 데이터를 처리하고 출력한다.

즉 manager클래스의 객체와 MessageBox나 UnderlinePen안에서 사용하는 객체는 내용은 같으나 메모리주소는 다른 객체라 볼 수 있다.

Swift

import Foundation

protocol Product {
    func use(_ s: String)
    func createCopy() -> Product
}

class Manager {
    private var showcases = Dictionary<String, Product>()
    
    func registaer(_ name:String, _ prototype: Product) {
        showcases[name] = prototype
    }
    
    func create(_ prototypeName: String) -> Product {
        var p: Product = showcases[prototypeName]!
        return p.createCopy()
    }
}

class MessageBox: Product {
    private var decochar: Character
    
    init(decochar: Character) {
        self.decochar = decochar
    }
    
    func use(_ s: String) {
        var decolen = 1 + s.count + 1;
        for i in 0..<decolen {
            print(decochar, terminator: "")
        }
        print()
        print(decochar, s, decochar)
        for i in 0..<decolen {
            print(decochar, terminator: "")
        }
        print()
    }
    
    func createCopy() -> Product {
        var p: Product
        p = self as Product
        
        return p
    }
}

class UnderlinePen: Product {
    private var ulcochar: Character
    
    init(ulchar: Character) {
        self.ulcochar = ulchar
    }
    
    func use(_ s: String) {
        var ulen = s.count
        print(s)
        for i in 0...ulen {
            print(ulcochar, terminator: "")
        }
        print()
    }
    
    func createCopy() -> Product {
        var p: Product
        p = self as Product
        
        return p
    }
}

//Main

var manager:Manager = Manager()

var upen: UnderlinePen = UnderlinePen(ulchar: "-")
var mbox: MessageBox = MessageBox(decochar: "*")
var sbox: MessageBox = MessageBox(decochar: "/")

manager.registaer("strong message", upen)
manager.registaer("warning box", mbox)
manager.registaer("slash box", sbox)

var p1: Product = manager.create("strong message")
p1.use("Hello, World!")

var p2: Product = manager.create("warning box")
p2.use("Hello, World!")

var p3: Product = manager.create("slash box")
p3.use("Hello, World!")
Hello, World!
--------------
***************
* Hello, World! *
***************
///////////////
/ Hello, World! /
///////////////

swift에서는 =을 사용하면 java와 동일하게 인스턴스를 shallow copy 해주기 때문에 따로 Cloneable인터페이스의 역할을 하는 프로토콜을 채택/구현 할 필요가 없었다.

createCopy()는 self를 이용해서 본인의 인스턴스를 Prototype프로토콜의 형태로 업캐스팅해서 return 하는 java코드와 완전 동일하게 처리하였다.

정리

프로토타입 패턴은 일정한 규칙은 있으나 너무 종류가 너무 많아 객체의 정리가 필요한 경우 사용하면 좋은 패턴이다,

또한 프레임워크를 사용하는 경우에도 요긴하게 사용할 수 있는데 프레임워크와 인스턴스를 분리하고 싶은 경우에도 사용하면 좋을 듯 하다.

또한 객체 생성을 각 클래스의 생성자에서 하지 않으므로 객체생성구현을 헷갈리지 않게 직관적으로 할 수 있다는 점이 장점이다.

profile
iOS 개발자 취준생, 천 리 길도 한 걸음부터

0개의 댓글