clone을 사용하여 Ruby에서 프로토 타입 기반 프로그래밍을 수행 할 수 있습니다. Ruby의 Object 클래스는 clone 메소드와 dup 메소드를 모두 정의합니다. clone과 dup은 모두 복사중인 객체의 얕은 복사본을 생성합니다. 즉, 객체의 인스턴스 변수는 복사되지만 참조하는 객체는 복사되지 않습니다. 예를 보여 드리겠습니다.
class Apple
attr_accessor :color
def initialize
@color = 'red'
end
end
apple = Apple.new
apple.color
=> "red"
orange = apple.clone
orange.color
=> "red"
orange.color << ' orange'
=> "red orange"
apple.color
=> "red orange"
위의 예에서 주황색 클론은 사과 객체의 상태 (즉, 인스턴스 변수)를 복사하지만 사과 객체가 다른 객체 (예 : 문자열 객체 색상)를 참조하는 경우 해당 참조는 복사되지 않습니다. 대신 사과와 오렌지는 모두 같은 물체를 참조합니다! 이 예에서 참조는 문자열 객체 'red'입니다. orange가 append 메소드 인 <<를 사용하여 기존 String 객체를 수정하면 문자열 객체가 'red orange'로 변경됩니다. 결과적으로 apple.color도 같은 String 객체를 가리 키기 때문에 변경됩니다.
부수적으로, 할당 연산자 =는 새로운 객체를 할당하여 참조를 파괴합니다. 데모는 다음과 같습니다.
class Apple
attr_accessor :color
def initialize
@color = 'red'
end
end
apple = Apple.new
apple.color
=> "red"
orange = apple.clone
orange.color
=> "red"
orange.color = 'orange'
orange.color
=> 'orange'
apple.color
=> 'red'
위의 예에서 주황색 클론의 색상 인스턴스 메소드에 새로운 객체를 할당하면 더 이상 apple과 동일한 객체를 참조하지 않습니다. 따라서 사과의 색상 방법에 영향을주지 않고 주황색의 색상 방법을 수정할 수 있지만, 사과에서 다른 객체를 복제하면 해당 객체가 복사 된 인스턴스 변수에서 사과와 동일한 객체를 참조하게됩니다.
dup은 또한 복사중인 객체의 얕은 복사본을 생성하며, dup에 대해 위에서 설명한 것과 동일한 데모를 수행하면 정확히 동일한 방식으로 작동하는 것을 볼 수 있습니다. 그러나 clone과 dup에는 두 가지 주요 차이점이 있습니다. 첫째, 다른 사람들이 언급했듯이 클론은 고정 상태를 복사하고 복제는하지 않습니다. 이것은 무엇을 의미 하는가? 루비에서 '동결'이라는 용어는 불변의 난해한 용어로, 그 자체가 컴퓨터 과학에서 명명 된 용어이므로 무언가를 변경할 수 없습니다. 따라서 Ruby에서 고정 된 객체는 어떤 식 으로든 수정할 수 없습니다. 사실상 불변이다. 고정 된 객체를 수정하려고하면 Ruby는 RuntimeError 예외를 발생시킵니다. clone은 고정 상태를 복사하므로 복제 된 개체를 수정하려고하면 RuntimeError 예외가 발생합니다. 반대로 dup은 고정 상태를 복사하지 않기 때문에
class Apple
attr_accessor :color
def initialize
@color = 'red'
end
end
apple = Apple.new
apple.frozen?
=> false
apple.freeze
apple.frozen?
=> true
apple.color = 'crimson'
RuntimeError: can't modify frozen Apple
apple.color << ' crimson'
=> "red crimson" # we cannot modify the state of the object, but we can certainly modify objects it is referencing!
orange = apple.dup
orange.frozen?
=> false
orange2 = apple.clone
orange2.frozen?
=> true
orange.color = 'orange'
=> "orange" # we can modify the orange object since we used dup, which did not copy the frozen state
orange2.color = 'orange'
RuntimeError: can't modify frozen Apple # orange2 raises an exception since the frozen state was copied via clone
둘째, 더 흥미롭게도 클론은 싱글 톤 클래스 (및 그 메소드)를 복사합니다! Ruby에서 프로토 타입 기반 프로그래밍을 수행하려는 경우 매우 유용합니다. 먼저, 싱글 톤 메소드가 복제본으로 복사 된 다음 Ruby에서 프로토 타입 기반 프로그래밍의 예에 적용 할 수 있음을 보여 드리겠습니다.
class Fruit
attr_accessor :origin
def initialize
@origin = :plant
end
end
fruit = Fruit.new
=> #<Fruit:0x007fc9e2a49260 @origin=:plant>
def fruit.seeded?
true
end
2.4.1 :013 > fruit.singleton_methods
=> [:seeded?]
apple = fruit.clone
=> #<Fruit:0x007fc9e2a19a10 @origin=:plant>
apple.seeded?
=> true
보다시피, fruit 객체 인스턴스의 싱글 톤 클래스는 클론으로 복사됩니다. 따라서 복제 된 객체는 singleton 방법 : seeded?에 액세스 할 수 있습니다. 그러나 이것은 dup의 경우가 아닙니다.
apple = fruit.dup
=> #<Fruit:0x007fdafe0c6558 @origin=:plant>
apple.seeded?
=> NoMethodError: undefined method `seeded?'
이제 프로토 타입 기반 프로그래밍에는 다른 클래스를 확장 한 클래스가 없으며, 청사진 역할을하는 상위 클래스에서 메소드가 파생 된 클래스의 인스턴스를 작성합니다. 대신 기본 객체가 있고 메소드와 상태를 복사하여 객체에서 새 객체를 만듭니다 (물론 복제를 통해 얕은 복사를 수행하기 때문에 인스턴스 변수 참조가 JavaScript와 마찬가지로 공유됩니다) 프로토 타입). 그런 다음 복제 된 메소드의 세부 사항을 채워서 오브젝트의 상태를 채우거나 변경할 수 있습니다. 아래 예제에는 기본 과일 오브젝트가 있습니다. 모든 과일에는 씨앗이 있으므로 number_of_seeds 메서드를 만듭니다. 그러나 사과에는 씨앗이 하나 있으므로 복제본을 만들고 세부 사항을 채 웁니다. 이제 애플을 복제 할 때 메소드를 복제 할뿐만 아니라 상태를 복제했습니다! clone은 상태의 얕은 사본 (인스턴스 변수)을 수행합니다. 그 때문에 red_apple을 얻기 위해 apple을 복제하면 red_apple은 자동으로 1 개의 seed를 갖습니다! red_apple은 Apple에서 상속 된 객체로, 과일에서 상속 된 객체로 생각할 수 있습니다. 따라서 과일과 사과를 대문자로 표기 한 것입니다. 우리는 클래스와 객체의 구별을 복제품으로 제공하지 않았습니다.
Fruit = Object.new
def Fruit.number_of_seeds=(number_of_seeds)
@number_of_seeds = number_of_seeds
end
def Fruit.number_of_seeds
@number_of_seeds
end
Apple = Fruit.clone
=> #<Object:0x007fb1d78165d8>
Apple.number_of_seeds = 1
Apple.number_of_seeds
=> 1
red_apple = Apple.clone
=> #<Object:0x007fb1d892ac20 @number_of_seeds=1>
red_apple.number_of_seeds
=> 1
물론 protoype 기반 프로그래밍에서 생성자 메서드를 사용할 수 있습니다.
Fruit = Object.new
def Fruit.number_of_seeds=(number_of_seeds)
@number_of_seeds = number_of_seeds
end
def Fruit.number_of_seeds
@number_of_seeds
end
def Fruit.init(number_of_seeds)
fruit_clone = clone
fruit_clone.number_of_seeds = number_of_seeds
fruit_clone
end
Apple = Fruit.init(1)
=> #<Object:0x007fcd2a137f78 @number_of_seeds=1>
red_apple = Apple.clone
=> #<Object:0x007fcd2a1271c8 @number_of_seeds=1>
red_apple.number_of_seeds
=> 1
궁극적으로 clone을 사용하면 JavaScript 프로토 타입 동작과 유사한 것을 얻을 수 있습니다.
dup
하고clone
않지만, 왜 당신은 다른 것보다 하나를 사용하십시오.