Definition of Prototype Pattern
기존의 인스턴스를 복제하여 새로운 인스턴스를 만드는 방법에 해당한다.
자세히 설명하자면, 복제 기능을 갖추고 있는 기존 인스턴스를 프로토타입으로 활용하여 새 인스턴스를 만드는 것
시간이 오래걸리는 DB작업이나 HTTP 통신을 통한 데이터의 경우 여러번 받아오려면 시간이 걸린다.
따라서 동일한 데이터가 사용되는 여러 객체가 필요하게 되는 경우 기존의 객체 하나를 만들어두고 해당 데이터를 포함하는 객체의 클론(복제)를 통해 사용하면 딜레이가 줄어들고 자원사용에 이득을 볼 수 있다.
기본적인 클래스 다이어그램은 위와 같이 구성되는데 Prototype
의 clone()
메서드에 대한 상세한 내용은 구체클래스인 ConcretePrototype
에 기술되게 된다.
ConcretePrototype
은 여러가지 만들어질 수 있으며 이 클래스에 따라 다른 구현방식을 사용함으로써 객체 복사 방법에 차등을 둘 수 있다.
Object Class의 .clone() 메서드
자바에서는 기본적으로 .clone() 메서드를 제공하고 있는데, 아래의 주석을 해석해보도록 하겠다
/**
* Creates and returns a copy of this object. The precise meaning * of "copy" may depend on the class of the object. The general * intent is that, for any object {@code x}, the expression:
* <blockquote> * <pre> * x.clone() != x</pre></blockquote> * will be true, and that the expression: * <blockquote> * <pre> * x.clone().getClass() == x.getClass()</pre></blockquote> * will be {@code true}, but these are not absolute requirements.
* While it is typically the case that: * <blockquote> * <pre> * x.clone().equals(x)</pre></blockquote> * will be {@code true}, this is not an absolute requirement.
* <p> * By convention, the returned object should be obtained by calling * {@code super.clone}. If a class and all of its superclasses (except
* {@code Object}) obey this convention, it will be the case that
* {@code x.clone().getClass() == x.getClass()}.
* <p> * By convention, the object returned by this method should be independent * of this object (which is being cloned). To achieve this independence, * it may be necessary to modify one or more fields of the object returned * by {@code super.clone} before returning it. Typically, this means
* copying any mutable objects that comprise the internal "deep structure" * of the object being cloned and replacing the references to these * objects with references to the copies. If a class contains only * primitive fields or references to immutable objects, then it is usually * the case that no fields in the object returned by {@code super.clone}
* need to be modified.
* * @implSpec
* The method {@code clone} for class {@code Object} performs a
* specific cloning operation. First, if the class of this object does * not implement the interface {@code Cloneable}, then a
* {@code CloneNotSupportedException} is thrown. Note that all arrays
* are considered to implement the interface {@code Cloneable} and that
* the return type of the {@code clone} method of an array type {@code T[]}
* is {@code T[]} where T is any reference or primitive type.
* Otherwise, this method creates a new instance of the class of this * object and initializes all its fields with exactly the contents of * the corresponding fields of this object, as if by assignment; the * contents of the fields are not themselves cloned. Thus, this method * performs a "shallow copy" of this object, not a "deep copy" operation. * <p> * The class {@code Object} does not itself implement the interface
* {@code Cloneable}, so calling the {@code clone} method on an object
* whose class is {@code Object} will result in throwing an
* exception at run time. **/
Creates and return a copy of this object.
이 객체의 복사본을 생성하고 반환한다.
The precise meaning * of “copy” may depend on the class of the object.
"복사"의 정확한 의미는 이 객체의 클래스에 따라 달라질 수 있다.
The general * intent is that, for any object {@code x}, the expression:
x.clone() != x will be true, and that the expression x.clone().getClass() == x.getClass() will be, but these are not absolute requirements.
일반적인 경우에 어떤 객체에 대해 x.clone() != x
는 참을 반환할 것이고, x.clone().getClass() == x.getClass()
또한 참을 반환할 것이나 이것이 절대적인 성립요건에 해당하지는 않는다.
While it is typically the case that: x.clone().equals(x) will be true, this is not an absolute requirement.
일반적으로 x.clone().equals(x)는 참을 반환할것인데 이것도 절대적인 성립 요건에 해당하지는 않는다.
-> 이로 인해 hashCode
와 equals
에 대한 오버라이딩이 필요하다
By convention, the returned object should be obtained by calling super.clone.
컨벤션상, 반환된 Object는 super.clone()
을 호출하는 것 만으로도 얻을 수 있어야 한다.
If a class and all of its superclasses (except {@code Object}) obey this convention, it will be the case that x.clone().getClass() == x.getClass()
만약 클래스와 그 상위 클래스들이 이 용례/관습을 따른다면 x.clone().getClass() == x.getClass()
를 만족하는 경우라고 볼 수 있을 것이다
By convention, the object returned by this method should be independent of this object (which is being cloned).
컨벤션에 따라 이 메서드에 의해 반환된 객체는 복제되는 객체와는 다른 독립적인 객체여야한다.
To achieve this independence, it may be necessary to modify one or more fields of the object returned by super.clone before returning it.
이러한 복제/피복제 객체간의 독립성을 달성하기 위해 super.clone()에 의해 객체가 반환되기 전에 하나 또는 그것보다 많은 객체 필드에 변경을 가하는 행위가 필요할 수 있다.
Typically, this means copying any mutable objects that comprise the internal “deep structure” of the object being cloned and replacing the references to these objects with references to the copies.
이것은 일반적으로 복제되는 객체의 내부 '깊은 구조’를 구성하는 변경 가능한 객체를 복사하고 이러한 객체에 대한 참조를 복사본에 대한 참조로 교체하는 것을 의미한다.
If a class contains only primitive fields or references to immutable objects, then it is usually *the case that no fields in the object returned by super.clone need to be modified.
만약 클래스의 필드가 원시타입으로만 구성되어있거나 불변 객체의 참조들로 구성되어있는 경우에는 super.clone()에 의해 반환되는 객체필드에 변경이 필요한 필드가 없는 경우가 많다.
The method {@code clone} for class {@code Object} performs a specific cloning operation. First, if the class of this object does not implement the interface Cloneable, then a CloneNotSupportedException is thrown.
Object.clone()
메서드는 특정한 복제 작업을 수행한다.
먼저 이 객체의 클래스가 Clonable
을 구현하고 있지 않은 경우 CloneNotSupportedException
을 반환함.
-> 따라서 clone()
사용하려면 꼭 Cloneable
을 객체 생성할때 implements
로 달아줘야 한다.
Note that all arrays are considered to implement the interface Cloneable and that the return type of the clone method of an array type T[] is {@code T[]} where T is any reference or primitive type.
일러두면, 모든 배열은 Cloneable
을 구현하고 있으며 여기서 발생하는 모든 클론 메서드의 반환 타입은 T[]
가 된다. 여기서 T
는 참조형 타입일수도, 원시타입일수도있다.
Otherwise, this method creates a new instance of the class of this object and initializes all its fields with exactly the contents of the corresponding fields of this object, as if by assignment; the contents of the fields are not themselves cloned. Thus, this method performs a “shallow copy” of this object, not a “deep copy” operation.
배열이 아닌 경우에 이 메서드는 객체의 클래스의 새로운 인스턴스를 생성하고 모든 필드의 내용을 그대로 복제해다가 복제된 객체에 할당한다, 따라서 필드들의 내용은 그 자체로 복제되는 것이 아닙니다, 따라서 이것은 "얕은 복사"에 해당하고 "깊은 복사"에 해당하지 않는다.
-> 클래스의 필드에 지정된 값을 그대로 가져다가 넣는데, 여기서 필드가 참조형 타입인 경우 참조형 변수의 값이 그대로 써지기 때문에 객체가 새로 생성되는것이 아니라 기존의 객체의 참조값이 그냥 복사되는 것으로 이해.
따라서 클론 한 두 객체 인스턴스가 다른 경우 예를 들어 아래의 클래스에서 클론된 두개의 객체가 있다고 가정하면
public class GithubIssueData {
private int id;
private String title;
private String repositoryUser;
private String repositoryName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getRepositoryUser() {
return repositoryUser;
}
public void setRepositoryUser(String repositoryUser) {
this.repositoryUser = repositoryUser;
}
public String getRepositoryName() {
return repositoryName;
}
public void setRepositoryName(String repositoryName) {
this.repositoryName = repositoryName;
}
@Override
public String toString() {
return "GithubIssueData{" +
"id=" + id +
", title='" + title + '\'' +
", repositoryUser='" + repositoryUser + '\'' +
", repositoryName='" + repositoryName + '\'' +
'}';
}
}
두 객체는 다르지만 두 객체 내의 String
필드는 동일한 객체 주소를 갖고있기 때문에 둘 중에 하나 변경하게 되면 둘 다 변경됨.
GithubIssueData
로 생성한 객체 A
와 B
가 있다고 가정했을 때, A.setTitle("X")
해버리면 그 변화값이 B에도 영향을 주는 사이드이펙트가 발생함.
The class {@code Object} does not itself implement the interface {@code Cloneable}, so calling the {@code clone} method on an object whose class is {@code Object} will result in throwing an exception at run time.
Object
클래스 자체는 Cloneable
인터페이스를 구현하지 않고 있음, 따라서 Object
타입인 객체에 .clone()
메서드를 호출하게 되면 런타임에서 예외를 발생할 것임.
궁금한게 Object
구현체에 보면 clone()
이 abstract
도 안걸려있는데 그냥 메서드 시그니쳐만 딱 있어서 이게 가능한가 싶어 질문을 올렸더니 자바 JNI에 관련되어 있다고 한다.
요건 추후에 마이크로프로세서 응용 실습과목에서 다루게 될 거라 따로 작성해보겠다!
https://stackoverflow.com/questions/27224577/why-is-object-clone-native-in-java
프로토타입 패턴 장단점
- 장점
- 복잡한 객체를 만드는 과정을 추상화할 수 있다.
- 기존 객체를 복제하는 과정이 새 인스턴스를 만드는 것 보다 비용적인 면에서 효율적일 수 있다.
- 추상 타입의 리턴이 가능하다.
- return에서 반환하는 객체가 원 객체와 같을 필요가 없다
- 계층형 구조에서 유용하게 사용할 수 있다.
- 단점
- 복잡한 객체를 만드는 과정 자체가 복잡할 수 있다(특히 순환참조가 있는 경우에 해당한다)
'전공 > Design Pattern' 카테고리의 다른 글
Design Pattern - Builder Pattern (0) | 2023.10.09 |
---|---|
Design Pattern - Abstract Factory Pattern (0) | 2023.10.07 |
Design Pattern - Factory Method Pattern (0) | 2023.10.07 |
Design Pattern - Singleton Pattern (1) | 2023.10.05 |