일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- jenkins
- it
- Spring Batch
- 요리
- Design Patterns
- Git
- 맛집
- java
- IntelliJ
- redis
- tool
- Spring
- springboot
- php
- linux
- ReactJS
- Oracle
- Gradle
- devops
- laravel
- MySQL
- jsp
- Web Server
- ubuntu
- AWS
- db
- JVM
- javascript
- elasticsearch
- Spring Boot
- Today
- Total
아무거나
[Design Pattern] Prototype Pattern 본문
프로토타입 패턴(Prototype Pattern)
복잡한 인스턴스를 복사할 수 있다.
즉, 생산 비용이 높은 인스턴스
를 복사를 통해서 쉽게 생성할 수 있도록 하는 패턴
- 인스턴스 생산 비용이 높은 경우
종류가 너무 많아서
클래스로 정리되지 않는 경우- 클래스로부터 인스턴스
생성이 어려운 경우
프로토 타입 패턴 예시 - 1
[요구사항]
- 일러스트레이터와 같은 그림 그리기 툴을 개발중이다. 어떤 모양(Shape) 그릴 수 있또록 하고 복사 붙여넣기 기능을 구현하자.
모양에 대한 함수를 만들자.
[Shape.java]
package com.bkjeon.prototype;
public class Shape implements Cloneable {
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
여기서 Cloneable interface는 인스턴스가 복제 가능하도록 하기 위해서 구현해야 하는 인터페이스이다. clone() 이라는 함수를 사용하면 된다. 하지만 사용용도에 따라 불변성이 지켜지지 않을 수 있으므로 잘 확인하여 사용하자.
원형 클래스를 작성하자. 여기에선 clone() 을 사용하기 위하여 Shape 클래스를 상속받자.
[Circle.java]
package com.bkjeon.prototype;
public class Circle extends Shape {
private int x,y,r;
public Circle(int x, int y, int r) {
this.x = x;
this.y = y;
this.r = r;
}
public void setX(int x) {
this.x = x;
}
public int getX() {
return x;
}
public void setY(int y) {
this.y = y;
}
public int getY() {
return y;
}
public void setR(int r) {
this.r = r;
}
public int getR() {
return r;
}
public Circle copy() throws CloneNotSupportedException {
Circle circle = (Circle) clone();
return circle;
}
}
메인 클래스를 작성하자.
[Main.java]
package com.bkjeon.prototype;
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Circle circle1 = new Circle(1, 1, 3);
Circle circle2 = circle1.copy();
System.out.println(circle1.getX() + "," + circle1.getY() + "," + circle1.getR());
System.out.println(circle2.getX() + "," + circle2.getY() + "," + circle2.getR());
}
}
실행결과
1,1,3
1,1,3
프로토 타입 패턴 예시 - 2
[요구사항]
- 복사 후 붙여 넣기를 하면 두 도형이 겹치는데 안겹치도록 살짝 옆으로 이동되게 하자.
이런 경우는 위의 Circle 클래스의 copy() 하는 과정에서 변경이 가능하다.
[Circle.java]
package com.bkjeon.prototype;
public class Circle extends Shape {
private int x,y,r;
public Circle(int x, int y, int r) {
this.x = x;
this.y = y;
this.r = r;
}
public void setX(int x) {
this.x = x;
}
public int getX() {
return x;
}
public void setY(int y) {
this.y = y;
}
public int getY() {
return y;
}
public void setR(int r) {
this.r = r;
}
public int getR() {
return r;
}
public Circle copy() throws CloneNotSupportedException {
Circle circle = (Circle) clone();
// 위치 변경
circle.x += 1;
circle.y += 1;
return circle;
}
}
실행결과
1,1,3
2,2,3
깊은 복사와 얕은 복사 - 1
Cloneable 인터페이스에서 제공하는 clone() 함수에 대하여 상세히 알아보자.
우선 알기 예시로 고양이 이름을 지어주는 클래스를 생성해보자.
[Cat.java]
package com.bkjeon.prototype2;
public class Cat {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
메인 클래스에서 실행해보자.
[Main.java]
package com.bkjeon.prototype2;
public class Main {
public static void main(String[] args) {
Cat navi = new Cat();
navi.setName("navi");
System.out.println(navi.getName());
}
}
실행결과
navi
이제 위의 Cat 인스턴스를 복사한 인스턴스에 yo 라는 이름의 고양이로 지정해주고 실행해보자.
[Main.java]
package com.bkjeon.prototype2;
public class Main {
public static void main(String[] args) {
Cat navi = new Cat();
navi.setName("navi");
// yo 고양이 이름 지정
Cat yo = navi;
yo.setName("yo");
System.out.println(navi.getName());
System.out.println(yo.getName());
}
}
실행결과
yo
yo
이상하게도 둘다 yo로 이름이 지정되어있다. 해당 실행을 디버깅해보면 복사할 때 인스턴스의 주소값도 동일하게 복사해 가므로 이런 현상이 발생한 것이다. 이것이 바로 낮은 수준의 복사이다. 위와 같은 현상에서 값들만 복사하는것이 깊은 수준의 복사라고 한다.
깊은 수준의 복사를 위하여 Cloneable 인터페이스를 사용해보자.
[Cat.java]
package com.bkjeon.prototype2;
public class Cat implements Cloneable {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
// 추가
public Cat copy() throws CloneNotSupportedException {
Cat ret = (Cat) this.clone();
return ret;
}
}
이제 메인클래스에서 깊은 복사로 변경해주자.
[Main.java]
package com.bkjeon.prototype2;
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Cat navi = new Cat();
navi.setName("navi");
// yo 고양이 이름 지정
Cat yo = navi.copy();
yo.setName("yo");
System.out.println(navi.getName());
System.out.println(yo.getName());
}
}
실행결과
navi
yo
깊은 복사와 얕은 복사 - 2
이해를 위하여 다른 케이스도 작성해보자. 고양이에게 나이를 설정할 수 있게 추가하자.
[Cat.java]
package com.bkjeon.prototype2;
public class Cat implements Cloneable {
private String name;
private Integer age;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
// 추가
public Cat copy() throws CloneNotSupportedException {
Cat ret = (Cat) this.clone();
return ret;
}
}
메인 클래스도 변경하자.
[Main.java]
package com.bkjeon.prototype2;
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Cat navi = new Cat();
navi.setName("navi");
navi.setAge(3);
// yo 고양이 이름 지정
Cat yo = navi.copy();
yo.setName("yo");
yo.setAge(1);
System.out.println(navi.getName());
System.out.println(yo.getName());
System.out.println(navi.getAge());
System.out.println(yo.getAge());
}
}
실행결과
navi
yo
3
1
위와 같이 잘 나오지만 만약 age 같은 경우는 다양한 값을 가지고 나이를 나타낼 수 있을 것이다.
다양한 Age 값을 고려한 클래스를 작성해보자.
[Age.java]
package com.bkjeon.prototype2;
public class Age {
int year;
int value;
public void setValue(int value) {
this.value = value;
}
public void setYear(int year) {
this.year = year;
}
public int getValue() {
return value;
}
public int getYear() {
return year;
}
}
Cat 함수에 선언되어있는 age에도 적용시키자.
[Cat.java]
package com.bkjeon.prototype2;
public class Cat implements Cloneable {
private String name;
private Age age;
public Age(int year, int value) {
this.year = year;
this.value = value;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(Age age) {
this.age = age;
}
public Age getAge() {
return age;
}
// 추가
public Cat copy() throws CloneNotSupportedException {
Cat ret = (Cat) this.clone();
return ret;
}
}
이제 마지막으로 메인함수도 수정하자.
[Main.java]
package com.bkjeon.prototype2;
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Cat navi = new Cat();
navi.setName("navi");
navi.setAge(new Age(2012, 3));
// yo 고양이 이름 지정
Cat yo = navi.copy();
yo.setName("yo");
yo.setAge(new Age(2013, 2));
System.out.println(navi.getName());
System.out.println(yo.getName());
System.out.println(navi.getAge().getYear());
System.out.println(yo.getAge().getYear());
}
}
실행결과
navi
yo
2012
2013
다른 방식으로도 변경해보자.
[Main.java]
package com.bkjeon.prototype2;
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Cat navi = new Cat();
navi.setName("navi");
navi.setAge(new Age(2012, 3));
// yo 고양이 이름 지정
Cat yo = navi.copy();
yo.setName("yo");
yo.getAge().setYear(2013);
yo.getAge().setValue(2);
System.out.println(navi.getName());
System.out.println(yo.getName());
System.out.println(navi.getAge().getYear());
System.out.println(yo.getAge().getYear());
System.out.println(navi.getAge().getValue());
System.out.println(yo.getAge().getValue());
}
}
실행결과를 보면 이름을 제외한 Age에서는 깊은 복사가 이루어지지 않았음을 볼 수 있다.
navi
yo
2013
2013
2
2
해결방법은 Cat 클래스에서 copy하는 부분에서 우리가 명시적으로 깊은복사를 해주면 된다.
[Cat.java]
package com.bkjeon.prototype2;
public class Cat implements Cloneable {
private String name;
private Age age;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(Age age) {
this.age = age;
}
public Age getAge() {
return age;
}
// 추가
public Cat copy() throws CloneNotSupportedException {
Cat ret = (Cat) this.clone();
ret.setAge(new Age(this.age.getYear(), this.age.getValue()));
return ret;
}
}
실행결과
navi
yo
2012
2013
3
2
즉, 객체 내에 있는 멤버 변수는 원시 변수(int, char, float 등) , Immutable Class (String, Boolean, Integer 등) 또는 Enum 형식일 때는 원본의 값을 바로 대입해도 되지만, 그렇지 않을 때는 멤버변수의 clone을 호출하여 복사해야 한다. 멤버변수가 List, Map 등 여러 유형으로 복잡하게 만들어졌을경우 각각 복사를 해주는 등 다양한 케이스에 유연하게 대응해야 한다.
'Java & Kotlin > Java' 카테고리의 다른 글
[Design Pattern] Abstract Factory Pattern (0) | 2019.12.10 |
---|---|
[Design Pattern] Builder Pattern (0) | 2019.12.08 |
[Design Pattern] Singleton Pattern (0) | 2019.12.07 |
[Design Pattern] Factory Method Pattern (0) | 2019.12.07 |
[Design Pattern] Template Method Pattern (0) | 2019.12.07 |