Error rendering WebPanel: No renderer found for resource type: velocity Template contents: <meta name="ajs-keyboardshortcut-hash" content="$keyboardShortcutManager.shortcutsHash">
메타 데이터의 끝으로 건너뛰기
메타 데이터의 시작으로 이동


프로토타입 패턴(prototype pattern)

생성할 객체들의 타입이 프로토타입인 인스턴스로부터 결정되도록 하며, 인스턴스는 새 객체를 만들기 위해 자신을 복제(clone)하게 된다. (이상 위키)


우리는 일상에서 '프로토타입' 이라는 키워드를 사용할 때 그림의 빨간 박스 부분의 초기 단계 정도를 상상한다.

사전에 ‘프로토타입’을 검색해보면, 우리말로 '원형' 이라고 하는데, 이는 '原(근원 원) 型(모형 형)' 의 의미라고 한다.

다시 말해 기본(근원) 적인 모형.

결과적으로, 프로토타입 패턴의 컨셉은 '기본적인 모형을 이용하는 패턴' 정도로 이해하면 좋을 것 같다.




프로토타입의 특징

  • 런타임에 새로운 객체를 추가/삭제
    : 원형으로 생성되는 인스턴스를 등록하는 것만으로 시스템에 새로운 제품 클래스를 추가/삭제 할 수 있다.

  • 값을 다양화함으로써 새로운 객체를 명세
    : 새로운 클래스를 생성하지 않고 객체에 변수의 값을 지정할 수 있다.

  • 구조를 다양화함으로써 새로운 객체를 명세
    : 반복적이고 복잡한 구조의 기본 설계가 필요한 경우, 사용자에게 필요한 구조를 설계하여 제공함으로써 사용자는 원형 패턴을 이용하여 자신만의 구조로 가져갈 수 있다.

  • 서브클래스의 수를 줄여줌
    : 팩토리 메서드 패턴과 달리 Creator 클래스를 통한 상속 계층이 아닌, 원형의 복제이므로 서브클래스를 줄일 수 있다.

  • 동적으로 클래스에 따라 응용프로그램을 설정
    : 동적으로 로드된 클래스의 인스턴스를 생성하고  싶은 응용프로그램은 정적으로 클래스의 생성자를 참조할 수 없지만, 런타임 환경이 그 클래스의 인스턴스를 자동으로 생성하고 원형 관리자에 등록함.
    그럼으로써 응용프로그램은 원형 관리자에게서 필요한 클래스의 인스턴스를 얻게됨



구현 방법


  • Prototype - declares an interface for cloning itself.
  • ConcretePrototype - implements the operation for cloning itself.
  • Client - creates a new object by asking a prototype to clone itself.

구현 방법은 비교적 간단하다.
  • 전체 패턴의 구조를 잡아줄 프로토타입 인터페이스(또는 추상클래스)를 선언하여 Clone() 연산을 구현한다.
  • 복제 작업을 진행할 클래스를 구현한다.
  • 프토로타입 복제를 통해 새로운 객체를 만든다.



유의해야할 점

프로토타입 패턴 사용시 가장 주의할 부분이 바로 Clone()  연산을 정확히 구현하는 것.

바로 얕은복사(Shallow Copy)깊은 복사(Deep Copy) 문제인데, 어떤 객체를 복사한다는 것이 인스턴스 변수들까지 복제(Deep Coy)하는 것인지, 변수를 공유(Shallow Copy)하는 것인지 고민해야한다.



샘플 코드


// Prototype Abstract Class
public abstract class StorePrototype implements Cloneable{
    abstract void makeBookList();

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}


public class Book {
    private int serial;
    private String name;

    public int getSerial() {
        return serial;
    }

    public void setSerial(int serial) {
        this.serial = serial;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Book{" +
                "serial=" + serial +
                ", name='" + name + '\'' +
                '}';
    }
}


얕은 복사 (Shallow Copy)

// implements Prototype Class
public class BookStore extends StorePrototype {
    private String area;
    private List<Book> bookList = new ArrayList<>();

    public String getArea() {
        return area;
    }

    public void setArea(String area) {
        this.area = area;
    }

    public List<Book> getBookList() {
        return bookList;
    }

    public void setBookList(List<Book> bookList) {
        this.bookList = bookList;
    }

    @Override
    public String toString() {
        return "BookStore{" +
                "area='" + area + '\'' +
                ", bookList=" + bookList +
                '}';
    }

    @Override
    void makeBookList() {
        for (int i=0; i<10 ; i++){
            Book b = new Book();
            b.setSerial(i);
            b.setTitle("Book_"+i);
            this.getBookList().add(b);
        }
    }
}


깊은 복사 (Deep Copy) 

public class BookStore extends StorePrototype {
    private String area;
    private List<Book> bookList = new ArrayList<>();

    public String getArea() {
        return area;
    }

    public void setArea(String area) {
        this.area = area;
    }

    public List<Book> getBookList() {
        return bookList;
    }

    public void setBookList(List<Book> bookList) {
        this.bookList = bookList;
    }

    @Override
    public String toString() {
        return "BookStore{" +
                "area='" + area + '\'' +
                ", bookList=" + bookList +
                '}';
    }

    @Override
    void makeBookList() {
        for (int i=0; i<10 ; i++){
            Book b = new Book();
            b.setSerial(i);
            b.setTitle("Book_"+i);
            this.getBookList().add(b);
        }
    }


	// Deep Copy
	public BookStore clone(){
    	BookStore store = new BookStore();
	    for(Book b : this.getBookList()){
    	    store.getBookList().add(b);
	    }
    	return store;
	}
}


실행 클래스

public class BookDemo {
    public static void main(String[] args) throws CloneNotSupportedException{
        BookStore store = new BookStore(); // 복사할 메인 인스턴스 생성
        store.setArea("강남_1호점");
        store.makeBookList();

        BookStore store2 = (BookStore)store.clone(); // 메인 인스턴스 복사
        store2.setArea("강남_2호점");

        store.getBookList().remove(1); // (shallow copy & deep copy 비교)

        System.out.println(store);
        System.out.println(store2);
    }
}



가변객체(mutable) 과 불변객체(immutable)

얕은복사(Shallow) 라고하면, 소위 객체의 주소값(?)만을 복사하여 객체를 복사하는 것을 말한다. 때문에 A라는 객체와 A를 복사한 A’ 라는 2개의 객체가 존재할 때. A’ 객체의 어떤 값을 변화시키면, A객체도 같이 바뀌게 된다.

반면 깊은복사(Deep copy)는 객체 주소값을 참조하는 것이 아니라, 객체의 값 그 자체를 복사한다. 때문에 복사한 객체 어느것의 값이 변하여도, 복사된 이후로는 서로에게 영향을 주지 않게 된다.


image

<immutable 과 mutable>



비평




참고

  • 레이블 없음