// 具体原型。克隆方法会创建一个新对象并将其传递给构造函数。直到构造函数运 // 行完成前,它都拥有指向新克隆对象的引用。因此,任何人都无法访问未完全生 // 成的克隆对象。这可以保持克隆结果的一致。 class Rectangle extends Shape is field width: int field height: int
constructorRectangle(source: Rectangle)is // 需要调用父构造函数来复制父类中定义的私有成员变量。 super(source) this.width = source.width this.height = source.height methodclone():Shape is return new Rectangle(this)
class Circle extends Shape is field radius: int
constructorCircle(source: Circle)is super(source) this.radius = source.radius methodclone():Shape is return new Circle(this)
// 客户端代码中的某个位置。 class Application is field shapes: arrayof Shape
namespaceRefactoringGuru.DesignPatterns.Prototype.Conceptual { publicclassPerson { publicint Age; public DateTime BirthDate; publicstring Name; public IdInfo IdInfo;
public Person ShallowCopy() { return (Person) this.MemberwiseClone(); }
public Person DeepCopy() { Person clone = (Person) this.MemberwiseClone(); clone.IdInfo = new IdInfo(IdInfo.IdNumber); clone.Name = String.Copy(Name); return clone; } }
classProgram { staticvoidMain(string[] args) { Person p1 = new Person(); p1.Age = 42; p1.BirthDate = Convert.ToDateTime("1977-01-01"); p1.Name = "Jack Daniels"; p1.IdInfo = new IdInfo(666);
// Perform a shallow copy of p1 and assign it to p2. Person p2 = p1.ShallowCopy(); // Make a deep copy of p1 and assign it to p3. Person p3 = p1.DeepCopy();
// Display values of p1, p2 and p3. Console.WriteLine("Original values of p1, p2, p3:"); Console.WriteLine(" p1 instance values: "); DisplayValues(p1); Console.WriteLine(" p2 instance values:"); DisplayValues(p2); Console.WriteLine(" p3 instance values:"); DisplayValues(p3);
// Change the value of p1 properties and display the values of p1, // p2 and p3. p1.Age = 32; p1.BirthDate = Convert.ToDateTime("1900-01-01"); p1.Name = "Frank"; p1.IdInfo.IdNumber = 7878; Console.WriteLine("\nValues of p1, p2 and p3 after changes to p1:"); Console.WriteLine(" p1 instance values: "); DisplayValues(p1); Console.WriteLine(" p2 instance values (reference values have changed):"); DisplayValues(p2); Console.WriteLine(" p3 instance values (everything was kept the same):"); DisplayValues(p3); }
/** * ConcretePrototype1 is a Sub-Class of Prototype and implement the Clone Method * In this example all data members of Prototype Class are in the Stack. If you * have pointers in your properties for ex: String* name_ ,you will need to * implement the Copy-Constructor to make sure you have a deep copy from the * clone method */
classConcretePrototype1 : public Prototype { private: float concrete_prototype_field1_;
/** * Notice that Clone method return a Pointer to a new ConcretePrototype1 * replica. so, the client (who call the clone method) has the responsability * to free that memory. I you have smart pointer knowledge you may prefer to * use unique_pointer here. */ Prototype *Clone()constoverride{ returnnewConcretePrototype1(*this); } };
classConcretePrototype2 : public Prototype { private: float concrete_prototype_field2_;
/** * In PrototypeFactory you have two concrete prototypes, one for each concrete * prototype class, so each time you want to create a bullet , you can use the * existing ones and clone those. */
/** * Notice here that you just need to specify the type of the prototype you * want and the method will create from the object with this type. */ Prototype *CreatePrototype(Type type){ return prototypes_[type]->Clone(); } };
voidClient(PrototypeFactory &prototype_factory){ std::cout << "Let's create a Prototype 1\n";
for (inti=0; i < shapes.size(); i++) { if (shapes.get(i) != shapesCopy.get(i)) { System.out.println(i + ": Shapes are different objects (yay!)"); if (shapes.get(i).equals(shapesCopy.get(i))) { System.out.println(i + ": And they are identical (yay!)"); } else { System.out.println(i + ": But they are not identical (booo!)"); } } else { System.out.println(i + ": Shape objects are the same (booo!)"); } } } }
OutputDemo.txt: 执行结果
1 2 3 4 5 6
0: Shapes are different objects (yay!) 0: And they are identical (yay!) 1: Shapes are different objects (yay!) 1: And they are identical (yay!) 2: Shapes are different objects (yay!) 2: And they are identical (yay!)
Shapeshape1= cache.get("Big green circle"); Shapeshape2= cache.get("Medium blue rectangle"); Shapeshape3= cache.get("Medium blue rectangle");
if (shape1 != shape2 && !shape1.equals(shape2)) { System.out.println("Big green circle != Medium blue rectangle (yay!)"); } else { System.out.println("Big green circle == Medium blue rectangle (booo!)"); }
if (shape2 != shape3) { System.out.println("Medium blue rectangles are two different objects (yay!)"); if (shape2.equals(shape3)) { System.out.println("And they are identical (yay!)"); } else { System.out.println("But they are not identical (booo!)"); } } else { System.out.println("Rectangle objects are the same (booo!)"); } } }
OutputDemo.txt: 执行结果
1 2 3
Big green circle != Medium blue rectangle (yay!) Medium blue rectangles are two different objects (yay!) And they are identical (yay!)
/** * The example class that has cloning ability. We'll see how the values of field * with different types will be cloned. */ classPrototype { public$primitive; public$component; public$circularReference;
/** * PHP has built-in cloning support. You can `clone` an object without * defining any special methods as long as it has fields of primitive types. * Fields containing objects retain their references in a cloned object. * Therefore, in some cases, you might want to clone those referenced * objects as well. You can do this in a special `__clone()` method. */ publicfunction__clone() { $this->component = clone$this->component;
// Cloning an object that has a nested object with backreference // requires special treatment. After the cloning is completed, the // nested object should point to the cloned object, instead of the // original object. $this->circularReference = clone$this->circularReference; $this->circularReference->prototype = $this; } }
/** * Note that the constructor won't be executed during cloning. If you have * complex logic inside the constructor, you may need to execute it in the * `__clone` method as well. */ publicfunction__construct(Prototype $prototype) { $this->prototype = $prototype; } }
$p2 = clone$p1; if ($p1->primitive === $p2->primitive) { echo"Primitive field values have been carried over to a clone. Yay!\n"; } else { echo"Primitive field values have not been copied. Booo!\n"; } if ($p1->component === $p2->component) { echo"Simple component has not been cloned. Booo!\n"; } else { echo"Simple component has been cloned. Yay!\n"; }
if ($p1->circularReference === $p2->circularReference) { echo"Component with back reference has not been cloned. Booo!\n"; } else { echo"Component with back reference has been cloned. Yay!\n"; }
if ($p1->circularReference->prototype === $p2->circularReference->prototype) { echo"Component with back reference is linked to original object. Booo!\n"; } else { echo"Component with back reference is linked to the clone. Yay!\n"; } }
clientCode();
Output.txt: 执行结果
1 2 3 4
Primitive field values have been carried over to a clone. Yay! Simple component has been cloned. Yay! Component with back reference has been cloned. Yay! Component with back reference is linked to the clone. Yay!
/** * You can control what data you want to carry over to the cloned object. * * For instance, when a page is cloned: * - It gets a new "Copy of ..." title. * - The author of the page remains the same. Therefore we leave the * reference to the existing object while adding the cloned page to the list * of the author's pages. * - We don't carry over the comments from the old page. * - We also attach a new date object to the page. */ publicfunction__clone() { $this->title = "Copy of " . $this->title; $this->author->addToPage($this); $this->comments = []; $this->date = new\DateTime(); } }
Dump of the clone. Note that the author is now referencing two objects.
RefactoringGuru\Prototype\RealWorld\Page Object ( [title:RefactoringGuru\Prototype\RealWorld\Page:private] => Copy of Tip of the day [body:RefactoringGuru\Prototype\RealWorld\Page:private] => Keep calm and carry on. [author:RefactoringGuru\Prototype\RealWorld\Page:private] => RefactoringGuru\Prototype\RealWorld\Author Object ( [name:RefactoringGuru\Prototype\RealWorld\Author:private] => John Smith [pages:RefactoringGuru\Prototype\RealWorld\Author:private] => Array ( [0] => RefactoringGuru\Prototype\RealWorld\Page Object ( [title:RefactoringGuru\Prototype\RealWorld\Page:private] => Tip of the day [body:RefactoringGuru\Prototype\RealWorld\Page:private] => Keep calm and carry on. [author:RefactoringGuru\Prototype\RealWorld\Page:private] => RefactoringGuru\Prototype\RealWorld\Author Object *RECURSION* [comments:RefactoringGuru\Prototype\RealWorld\Page:private] => Array ( [0] => Nice tip, thanks! )
classSomeComponent: """ Python provides its own interface of Prototype via `copy.copy` and `copy.deepcopy` functions. And any class that wants to implement custom implementations have to override `__copy__` and `__deepcopy__` member functions. """
def__copy__(self): """ Create a shallow copy. This method will be called whenever someone calls `copy.copy` with this object and the returned value is returned as the new shallow copy. """
# First, let's create copies of the nested objects. some_list_of_objects = copy.copy(self.some_list_of_objects) some_circular_ref = copy.copy(self.some_circular_ref)
# Then, let's clone the object itself, using the prepared clones of the # nested objects. new = self.__class__( self.some_int, some_list_of_objects, some_circular_ref ) new.__dict__.update(self.__dict__)
return new
def__deepcopy__(self, memo={}): """ Create a deep copy. This method will be called whenever someone calls `copy.deepcopy` with this object and the returned value is returned as the new deep copy. What is the use of the argument `memo`? Memo is the dictionary that is used by the `deepcopy` library to prevent infinite recursive copies in instances of circular references. Pass it to all the `deepcopy` calls you make in the `__deepcopy__` implementation to prevent infinite recursions. """
# First, let's create copies of the nested objects. some_list_of_objects = copy.deepcopy(self.some_list_of_objects, memo) some_circular_ref = copy.deepcopy(self.some_circular_ref, memo)
# Then, let's clone the object itself, using the prepared clones of the # nested objects. new = self.__class__( self.some_int, some_list_of_objects, some_circular_ref ) new.__dict__ = copy.deepcopy(self.__dict__, memo)
# Let's change the list in shallow_copied_component and see if it changes in # component. shallow_copied_component.some_list_of_objects.append("another object") if component.some_list_of_objects[-1] == "another object": print( "Adding elements to `shallow_copied_component`'s " "some_list_of_objects adds it to `component`'s " "some_list_of_objects." ) else: print( "Adding elements to `shallow_copied_component`'s " "some_list_of_objects doesn't add it to `component`'s " "some_list_of_objects." )
# Let's change the set in the list of objects. component.some_list_of_objects[1].add(4) if4in shallow_copied_component.some_list_of_objects[1]: print( "Changing objects in the `component`'s some_list_of_objects " "changes that object in `shallow_copied_component`'s " "some_list_of_objects." ) else: print( "Changing objects in the `component`'s some_list_of_objects " "doesn't change that object in `shallow_copied_component`'s " "some_list_of_objects." )
deep_copied_component = copy.deepcopy(component)
# Let's change the list in deep_copied_component and see if it changes in # component. deep_copied_component.some_list_of_objects.append("one more object") if component.some_list_of_objects[-1] == "one more object": print( "Adding elements to `deep_copied_component`'s " "some_list_of_objects adds it to `component`'s " "some_list_of_objects." ) else: print( "Adding elements to `deep_copied_component`'s " "some_list_of_objects doesn't add it to `component`'s " "some_list_of_objects." )
# Let's change the set in the list of objects. component.some_list_of_objects[1].add(10) if10in deep_copied_component.some_list_of_objects[1]: print( "Changing objects in the `component`'s some_list_of_objects " "changes that object in `deep_copied_component`'s " "some_list_of_objects." ) else: print( "Changing objects in the `component`'s some_list_of_objects " "doesn't change that object in `deep_copied_component`'s " "some_list_of_objects." )
print( f"id(deep_copied_component.some_circular_ref.parent): " f"{id(deep_copied_component.some_circular_ref.parent)}" ) print( f"id(deep_copied_component.some_circular_ref.parent.some_circular_ref.parent): " f"{id(deep_copied_component.some_circular_ref.parent.some_circular_ref.parent)}" ) print( "^^ This shows that deepcopied objects contain same reference, they " "are not cloned repeatedly." )
Output.txt: 执行结果
1 2 3 4 5 6 7
Adding elements to `shallow_copied_component`'s some_list_of_objects adds it to `component`'s some_list_of_objects. Changing objects in the `component`'s some_list_of_objects changes that object in `shallow_copied_component`'s some_list_of_objects. Adding elements to `deep_copied_component`'s some_list_of_objects doesn't add it to `component`'s some_list_of_objects. Changing objects in the `component`'s some_list_of_objects doesn't change that object in `deep_copied_component`'s some_list_of_objects. id(deep_copied_component.some_circular_ref.parent): 4429472784 id(deep_copied_component.some_circular_ref.parent.some_circular_ref.parent): 4429472784 ^^ This shows that deepcopied objects contain same reference, they are not cloned repeatedly.
# The example class that has cloning ability. We'll see how the values of field # with different types will be cloned. classPrototype attr_accessor:primitive, :component, :circular_reference
# Cloning an object that has a nested object with backreference requires # special treatment. After the cloning is completed, the nested object # should point to the cloned object, instead of the original object. @circular_reference = deep_copy(@circular_reference) @circular_reference.prototype = self deep_copy(self) end
# deep_copy is the usual Marshalling hack to make a deep copy. But it's rather # slow and inefficient, therefore, in real applications, use a special gem private defdeep_copy(object) Marshal.load(Marshal.dump(object)) end end
if p1.primitive == p2.primitive puts 'Primitive field values have been carried over to a clone. Yay!' else puts 'Primitive field values have not been copied. Booo!' end
if p1.component.equal?(p2.component) puts 'Simple component has not been cloned. Booo!' else puts 'Simple component has been cloned. Yay!' end
if p1.circular_reference.equal?(p2.circular_reference) puts 'Component with back reference has not been cloned. Booo!' else puts 'Component with back reference has been cloned. Yay!' end
if p1.circular_reference.prototype.equal?(p2.circular_reference.prototype) print 'Component with back reference is linked to original object. Booo!' else print 'Component with back reference is linked to the clone. Yay!' end
output.txt: 执行结果
1 2 3 4
Primitive field values have been carried over to a clone. Yay! Simple component has been cloned. Yay! Component with back reference has been cloned. Yay! Component with back reference is linked to the clone. Yay!
/// Swift has built-in cloning support. To add cloning support to your class, /// you need to implement the NSCopying protocol in that class and provide the /// implementation for the `copy` method. classBaseClass: NSCopying, Equatable {
/// MARK: - NSCopying funccopy(withzone: NSZone? =nil) -> Any { let prototype =type(of: self).init() prototype.intValue = intValue prototype.stringValue = stringValue print("Values defined in BaseClass have been cloned!") return prototype }
/// Subclasses can override the base `copy` method to copy their own data into /// the resulting object. But you should always call the base method first. classSubClass: BaseClass {
privatevar boolValue =true
funccopy() -> Any { return copy(with: nil) }
overridefunccopy(withzone: NSZone?) -> Any { guardlet prototype =super.copy(with: zone) as?SubClasselse { returnSubClass() // oops } prototype.boolValue = boolValue print("Values defined in SubClass have been cloned!") return prototype } }
/// The client code. classClient { // ... staticfuncsomeClientCode() { let original =SubClass(intValue: 2, stringValue: "Value2")
let author =Author(id: 10, username: "Ivan_83") let page =Page(title: "My First Page", contents: "Hello world!", author: author)
page.add(comment: Comment(message: "Keep it up!"))
/// Since NSCopying returns Any, the copied object should be unwrapped. guardlet anotherPage = page.copy() as?Pageelse { XCTFail("Page was not copied") return }
/// Comments should be empty as it is a new page. XCTAssert(anotherPage.comments.isEmpty)
/// Note that the author is now referencing two objects. XCTAssert(author.pagesCount ==2)
/** * The example class that has cloning ability. We'll see how the values of field * with different types will be cloned. */ classPrototype { publicprimitive: any; publiccomponent: object; publiccircularReference: ComponentWithBackReference;
publicclone(): this { const clone = Object.create(this);
clone.component = Object.create(this.component);
// Cloning an object that has a nested object with backreference // requires special treatment. After the cloning is completed, the // nested object should point to the cloned object, instead of the // original object. Spread operator can be handy for this case. clone.circularReference = { ...this.circularReference, prototype: { ...this }, };
return clone; } }
classComponentWithBackReference { public prototype;
const p2 = p1.clone(); if (p1.primitive === p2.primitive) { console.log('Primitive field values have been carried over to a clone. Yay!'); } else { console.log('Primitive field values have not been copied. Booo!'); } if (p1.component === p2.component) { console.log('Simple component has not been cloned. Booo!'); } else { console.log('Simple component has been cloned. Yay!'); }
if (p1.circularReference === p2.circularReference) { console.log('Component with back reference has not been cloned. Booo!'); } else { console.log('Component with back reference has been cloned. Yay!'); }
if (p1.circularReference.prototype === p2.circularReference.prototype) { console.log('Component with back reference is linked to original object. Booo!'); } else { console.log('Component with back reference is linked to the clone. Yay!'); } }
clientCode();
Output.txt: 执行结果
1 2 3 4
Primitive field values have been carried over to a clone. Yay! Simple component has been cloned. Yay! Component with back reference has been cloned. Yay! Component with back reference is linked to the clone. Yay!