Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

프로토타입 정리 #88

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions 객체생성/3주차-프로토타입/summary/code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
## Java
```java
public class Order implements Cloneable{
private Orderer orderer;
private long orderIdx;
private long totalAmount;
private long lastAmount;
private long giftCouponPrice;
private long giftCouponUsedAmount;
private int discountRate;
private LocalDateTime expiryDt;
private LocalDateTime earliesstDt;

@Override
public Order clone() throws CloneNotSupportedException {
return (Order) super.clone(); //Object의 clone()이용해서 복제후 Object에서 Order로 클래스 형변환
}
}

public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Order order1 = new Order();
Order order2 = order1.clone();

System.out.println(order1 == order2); //false
System.out.println(order1.equals(order2)); //false
}
}
```
자바에서는 객체를 복제할 수 있도록 최상위 클래스인 Object에서 `clone()`를 지원한다. 하지만 Object의 clone을 이용하기 위해서는 Cloneable을 implements해야만 사용이 가능하다.

또한, 객체간의 동등성을 판단하기 위해 String의 equals() 처럼 object의 equals()를 이용하면 기대했던 값과는 다르게 `false` 가 반환된다. <br>이는 Object의 equals()는 `==`를 통해 비교하기 때문에 동일성비교와 같은 결과를 보여주는 것이고, String의 equals()는 값만을 비교하도록 재정의 했기 때문에 동등성 비교가 가능한 것이다.

따라서 객체의 동등성을 비교하고 싶다면 `equals()`를 재정의 해주면 된다.

```java
public class Order implements Cloneable{
private Orderer orderer;
private long orderIdx;
private long totalAmount;
private long lastAmount;
private long giftCouponPrice;
private long giftCouponUsedAmount;
private int discountRate;
private LocalDateTime expiryDt;
private LocalDateTime earliesstDt;

@Override
public Order clone() throws CloneNotSupportedException {
return (Order) super.clone(); //Object의 clone()이용해서 복제후 Object에서 Order로 클래스 형변환
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Order)) return false;
Order order = (Order) o;
return orderIdx == order.orderIdx && totalAmount == order.totalAmount && lastAmount == order.lastAmount && giftCouponPrice == order.giftCouponPrice && giftCouponUsedAmount == order.giftCouponUsedAmount && discountRate == order.discountRate && Objects.equals(orderer, order.orderer) && Objects.equals(expiryDt, order.expiryDt) && Objects.equals(earliesstDt, order.earliesstDt);
}

@Override
public int hashCode() {
return Objects.hash(orderer, orderIdx, totalAmount, lastAmount, giftCouponPrice, giftCouponUsedAmount, discountRate, expiryDt, earliesstDt);
}
}

public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Order order1 = new Order();
Order order2 = order1.clone();

System.out.println(order1 == order2); //false
System.out.println(order1.equals(order2)); //true
}
}
```

<br>

## PHP
```php
class Order{
private $orderer;
private $orderIdx;
private $totalAmount;
private $lastAmount;
private $giftCouponPrice;
private $giftCouponUsedAmount;
private $discountRate;
private $expiryDt;
private $earliesstDt;
}

class client extends PHPUnit\Framework\TestCase{
function test_clone객체비교() {
$order1 = new Order();
$order2 = clone $order1;

$this->assertFalse($order == $order2);
$this->assertTrue($order === $order3);
}
}
```
php는 `clone`이라는 키워드로 객체 복제를 지원하고 있으며, `==`,`===` 연산자로 동일성, 동등성을 비교해보면 복제된 객체와 동등하지만 동일하지는 않게 복제가 되는 것을 볼 수 있다.
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# 프로토타입 패턴

> **원본 객체를 새로운 객체로 복사**하여 사용하는 패턴

![](https://refactoring.guru/images/patterns/diagrams/prototype/structure.png)

**Prototype**: clone 메소드를 선언되어있는 인터페이스

**Concrete Prototype**: 실제로 클론 메소드를 구현하는 클래스.

## 프로토타입은 언제 사용되는가?

- 다수의 객체를 많이 만들고 싶은 경우

## 프로토타입 패턴 예제 코드

## Java

```java
public class Order implements Cloneable{
private Orderer orderer;
private long orderIdx;
private long totalAmount;
private long lastAmount;
private long giftCouponPrice;
private long giftCouponUsedAmount;
private int discountRate;
private LocalDateTime expiryDt;
private LocalDateTime earliesstDt;

@Override
public Order clone() throws CloneNotSupportedException {
return (Order) super.clone(); //Object의 clone()이용해서 복제후 Object에서 Order로 클래스 형변환
}
}

public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Order order1 = new Order();
Order order2 = order1.clone();

System.out.println(order1 == order2); //false
System.out.println(order1.equals(order2)); //false
}
}
```

자바에서는 객체를 복제할 수 있도록 최상위 클래스인 Object에서 `clone()`를 지원한다. 하지만 Object의 clone을 이용하기 위해서는 Cloneable을 implements해야만 사용이 가능하다.

또한, 객체간의 동등성을 판단하기 위해 String의 equals() 처럼 object의 equals()를 이용하면 기대했던 값과는 다르게 `false` 가 반환된다.
이는 Object의 equals()는 `==`를 통해 비교하기 때문에 동일성비교와 같은 결과를 보여주는 것이고, String의 equals()는 값만을 비교하도록 재정의 했기 때문에 동등성 비교가 가능한 것이다.

따라서 객체의 동등성을 비교하고 싶다면 `equals()`를 재정의 해주면 된다.

```java
public class Order implements Cloneable{
private Orderer orderer;
private long orderIdx;
private long totalAmount;
private long lastAmount;
private long giftCouponPrice;
private long giftCouponUsedAmount;
private int discountRate;
private LocalDateTime expiryDt;
private LocalDateTime earliesstDt;

@Override
public Order clone() throws CloneNotSupportedException {
return (Order) super.clone(); //Object의 clone()이용해서 복제후 Object에서 Order로 클래스 형변환
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Order)) return false;
Order order = (Order) o;
return orderIdx == order.orderIdx && totalAmount == order.totalAmount && lastAmount == order.lastAmount && giftCouponPrice == order.giftCouponPrice && giftCouponUsedAmount == order.giftCouponUsedAmount && discountRate == order.discountRate && Objects.equals(orderer, order.orderer) && Objects.equals(expiryDt, order.expiryDt) && Objects.equals(earliesstDt, order.earliesstDt);
}

@Override
public int hashCode() {
return Objects.hash(orderer, orderIdx, totalAmount, lastAmount, giftCouponPrice, giftCouponUsedAmount, discountRate, expiryDt, earliesstDt);
}
}

public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Order order1 = new Order();
Order order2 = order1.clone();

System.out.println(order1 == order2); //false
System.out.println(order1.equals(order2)); //true
}
}
```

## PHP

```php
class Order{
private $orderer;
private $orderIdx;
private $totalAmount;
private $lastAmount;
private $giftCouponPrice;
private $giftCouponUsedAmount;
private $discountRate;
private $expiryDt;
private $earliesstDt;
}

class client extends PHPUnit\Framework\TestCase{
function test_clone객체비교() {
$order1 = new Order();
$order2 = clone $order1;

$this->assertFalse($order == $order2);
$this->assertTrue($order === $order3);
}
}
```

php는 `clone`이라는 키워드로 객체 복제를 지원하고 있으며, `==`,`===` 연산자로 동일성, 동등성을 비교해보면 복제된 객체와 동등하지만 동일하지는 않게 복제가 되는 것을 볼 수 있다.

## 패턴의 장/단점

✅ 장점:

- 구체적인 클래스로부터 커플링 없이 객체 복사 가능
- 반복적인 초기화 코드를 제거해서, 프로토타입을 복제 할 수 있음.
- 복잡한 객체를 더 편리하게 생산할 수 있음
- 복잡한 객체에 대한 사전 설정을 처리할때 상속 대신 사용 가능

🚨 단점:

- 순환 참조가 있는 복잡한 객체를 복제하는 것은 매우 까다로울 수 있다.

## 비슷한 패턴

- 많은 디자인패턴들이 **팩토리 메소드**(하위 클래스를 통해서 덜 복잡하고, 더 커스터마이징 가능하게)를 사용하는데, 추**상 팩토리 패턴, 프로토타입, 빌더**(더 유연하지만 좀 더 복잡한 형태)로 발전했다.

- **추상팩토리** 클래스는 **팩토리 메소드**의 기반으로 설계되어있지만, **프로토타입**을 쓰면 이런 클래스들의 메소드들을 구성할 수 도 있다.

- **프로토 타입**은 **커멘드 패턴**의 복사본을 기록에 저장해야할때 도움을 줍니다.

- **데코레이터**와 **컴포짓 패턴**을 많이 사용한 설계의 경우 **프로토타입**을 사용하는게 많은 이점을 누릴 수 있다. 적용하면, 복잡한 구조를 처음부터 재구성할 필요가 없고, 복제 가능해진다.

- **프로토타입**은 상속에 기반하지 않아서, 상속에 문제점이 없다. 반면에 복제할 객체의 초기화가 복잡해진다. 반대로 **팩토리 메소드**는 상속을 사용하지만, 초기화 단계가 필수적이지 않다.

- **프로토타입**은 **메멘토 패턴**의 간단한 대체제가 될 수 있다. 이력에 저장하려는 상태인 객체가 매우 단순하고 외부 리소스에 대한 연결성이 없거나, 재연결성이 쉽게 되는 경우에 사용하는 것이 좋다.

- **추상 팩토리 패턴, 빌더, 프로토타입**은 싱글톤으로 구현 가능하다.