1. 프로토타입 패턴이란?
- 어떤 객체에 대한 프로토타입을 만들어 놓고 그것을 복사해서 사용하는 패턴
- Clone을 이용하여 객체를 생성하는 방법
- 본래의 오브젝트의 복사본을 만들어 각 객체에 따라 데이터를 수정해주는 방식으로 오브젝트를 생성
- 기존에 생성된 객체를 이용하여 해당 타입의 객체를 생성
- 생성할 객체들의 타입이 프로토타입인 인스턴스로부터 결정, 인스턴스는 새 객체를 만들기 위해 자기 자신을 복제
- 유니티에서는 Instantiate()가 같은 역할을 하도록 구현되어 있음.
- 객체들을 그의 특정 클래스들에 결합하지 않고 복제할 수 있도록 하는 생성 디자인 패턴
2. 프로토타입 패턴을 사용하는 경우
- 비슷한 오브젝트를 지속적으로 생성해야 할 경우
- 클래스로부터 인스턴스 생성이 어려운 경우
- Framework와 생성하는 인스턴스를 분리하고 싶은 경우
- 종류가 너무 많아서 클래스로 정리할 수 없는 경우
3. 프로토타입 패턴의 장점
1) 추상화 : 객체를 만드는 복잡한 과정을 숨길 수 있음.
2) 효율성 : 기존 객체를 복제하는 과정이 새 인스턴스를 만드는 것보다 비용면에서 효율적일 수 있음.
3) 유연성 : clone에서 반환하는 타입이 clone을 정의한 클래스와 반드시 동일할 필요는 없다. 즉 클래스의 계층 구조가 있을 때 추상화된 타입을 반환할 수 있는 유연성이 있음.
4. 프로토타입 패턴의 단점
1) 구현의 복잡성 : 인스턴스를 복제하는 과정 자체가 복잡할 수 있음.
2) 적용의 어려움 : 복제과정을 통해 객체를 생성하는 것이 어려운 경우 적용하기 어려움
5. 코드 구현
- IExercise라는 인터페이스를 통해 얕은 복사 함수들을 정의해주고, 각 클래스마다 이를 재정의하여 사용한다.
- 최초 생성되는 fit라는 객체가 프로토타입의 역할을 하고, 이후 생성되는 shallow와 deep이 프로토타입 fit을 복사해서 사용하는 새로운 객체들이다.
- string과 StringBuilder의 차이를 알고 싶다면 다음을 참고하자.
using System;
using System.Net;
using System.Text;
namespace StudyCSharp
{
public interface IExercise
{
// cf) 복사의 종류
// 1. 얕은 복사(Shallow Copy) : 주소 값이 같은 인스턴스 생성(한 쪽 수정시 다른 쪽이 영향을 받음)
// 2. 깊은 복사(Deep Copy) : 주소 값이 다른 인스턴스 생성(원본과 별개)
// 얕은 복사
IExercise ShallowCopyClone();
// 깊은 복사
IExercise DeepCopyClone();
}
public class ExerciseID
{
public int ID;
public ExerciseID(int Id)
{
this.ID = Id;
}
}
public class Fitness : IExercise
{
public string risk { get; set; } // 위험도(A~F)
public StringBuilder time { get; set; } // 평균 운동 시간(0h~10h)
public int strength { get; set; } // 강도(0~5)
public ExerciseID exerciseID { get; set; } // 넘버링(0~100)
public IExercise ShallowCopyClone()
{
// 얕은 복사(Shallow Copy)
// MemberwiseClone() 함수 : C# 내장함수, 얕은 복사로 객체의 단순 복사본을 생성하고 리턴해줌.
return (IExercise)MemberwiseClone(); // IExercise 타입으로 this의 복사본을 생성
// ** 주의 **
// string은 참조타입이지만 불변성을 가지므로 새로운 객체를 생성하게 되어 얕은 복사가 일어나지 않음.
// int 또한 불변성을 가지기 때문에 새로운 객체를 생성해서 재할당하는 과정이 일어나기 때문에 얕은 복사가 일어나지 않음.
// StringBuilder와 ExerciseID 클래스는 각각 string과 int를 얕은 복사로 어떻게 이용하는지 보여준다.
}
public IExercise DeepCopyClone()
{
Fitness clone = new Fitness(); // 새로운 객체 생성
clone.risk = risk;
clone.time = new StringBuilder().Append(time);
clone.strength = strength;
clone.exerciseID = new ExerciseID(exerciseID.ID);
return clone;
}
}
public class Crossfit : IExercise
{
public string risk { get; set; } // 위험도(A~F)
public StringBuilder time { get; set; } // 평균 운동 시간(0h~10h)
public int strength { get; set; } // 강도(0~5)
public ExerciseID exerciseID { get; set; } // 넘버링(0~100)
public IExercise ShallowCopyClone()
{
return this.MemberwiseClone() as IExercise;
}
public IExercise DeepCopyClone()
{
// 깊은 복사(Deep Copy)
Crossfit clone = new Crossfit(); // 새로운 객체 생성
// 현재 값 복사 -> 새로운 객체에 저장됨.
clone.risk = risk; /*clone.risk = string.Copy(risk);*/ // 둘 다 똑같이 깊은 복사가 일어남 -> string 불변성
clone.time = new StringBuilder().Append(time);
clone.strength = strength;
clone.exerciseID = new ExerciseID(exerciseID.ID);
return clone;
}
}
class Program
{
static void Main(string[] args)
{
// 최초 생성(Prototype)
Fitness fit = new Fitness();
fit.risk = "C";
fit.time = new StringBuilder("1.5h");
fit.strength = 4;
fit.exerciseID = new ExerciseID(1);
// 복사본 생성
Fitness shallow = (Fitness)fit.ShallowCopyClone();
Fitness deep = (Fitness)fit.DeepCopyClone();
Console.WriteLine("[원본]");
Console.WriteLine("위험도 \t\t: {0}", fit.risk);
Console.WriteLine("평균 운동 시간 \t: {0}", fit.time);
Console.WriteLine("강도 \t\t: {0}", fit.strength);
Console.WriteLine("운동 고유 번호 \t: {0}", fit.exerciseID.ID);
Console.WriteLine();
Console.WriteLine("[얕은 복사]");
Console.WriteLine("위험도 \t\t: {0}", shallow.risk);
Console.WriteLine("평균 운동 시간 \t: {0}", shallow.time);
Console.WriteLine("강도 \t\t: {0}", shallow.strength);
Console.WriteLine("운동 고유 번호 \t: {0}", shallow.exerciseID.ID);
Console.WriteLine();
Console.WriteLine("[깊은 복사]");
Console.WriteLine("위험도 \t\t: {0}", deep.risk);
Console.WriteLine("평균 운동 시간 \t: {0}", deep.time);
Console.WriteLine("강도 \t\t: {0}", deep.strength);
Console.WriteLine("운동 고유 번호 \t: {0}", deep.exerciseID.ID);
Console.WriteLine();
Console.WriteLine("-----------------------------------------------");
// 원본 값 수정
fit.risk = "Z";
fit.time.Replace("1.5h", "2h");
fit.strength = 3;
fit.exerciseID.ID = 99;
Console.WriteLine("-----------------------------------------------");
Console.WriteLine();
Console.WriteLine("[수정 후 원본]");
Console.WriteLine("위험도 \t\t: {0}", fit.risk);
Console.WriteLine("평균 운동 시간 \t: {0}", fit.time);
Console.WriteLine("강도 \t\t: {0}", fit.strength);
Console.WriteLine("운동 고유 번호 \t: {0}", fit.exerciseID.ID);
Console.WriteLine();
Console.WriteLine("[수정 후 얕은 복사]");
Console.WriteLine("위험도 \t\t: {0}", shallow.risk);
Console.WriteLine("평균 운동 시간 \t: {0}", shallow.time);
Console.WriteLine("강도 \t\t: {0}", shallow.strength);
Console.WriteLine("운동 고유 번호 \t: {0}", shallow.exerciseID.ID);
Console.WriteLine();
Console.WriteLine("[수정 후 깊은 복사]");
Console.WriteLine("위험도 \t\t: {0}", deep.risk);
Console.WriteLine("평균 운동 시간 \t: {0}", deep.time);
Console.WriteLine("강도 \t\t: {0}", deep.strength);
Console.WriteLine("운동 고유 번호 \t: {0}", deep.exerciseID.ID);
Console.WriteLine();
}
}
}
<참고 사이트>