Prototype Pattern in Java – Java中的原型模式

最后修改: 2019年 10月 20日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

In this tutorial, we’re going to learn about one of the Creational Design Patterns – the Prototype pattern. At first, we’ll explain this pattern and then proceed to implement it in Java.

在本教程中,我们将学习休闲设计模式中的一种–原型模式。首先,我们将解释这一模式,然后继续在Java中实现它。

We’ll also discuss some of its advantages and disadvantages.

我们还将讨论它的一些优点和缺点。

2. Prototype Pattern

2.原型模式

The Prototype pattern is generally used when we have an instance of the class (prototype) and we’d like to create new objects by just copying the prototype.

原型模式一般是在我们有一个类的实例(原型),并且我们想通过复制原型来创建新的对象时使用的

Let’s use an analogy to better understand this pattern.

让我们用一个比喻来更好地理解这种模式。

In some games, we want trees or buildings in the background. We may realize that we don’t have to create new trees or buildings and render them on the screen every time the character moves.

在一些游戏中,我们希望在背景中出现树木或建筑物。我们可能会意识到,我们不必在角色每次移动时都创建新的树木或建筑并在屏幕上渲染它们。

So, we create an instance of the tree first. Then we can create as many trees as we want from this instance (prototype) and update their positions. We may also choose to change the color of the trees for a new level in the game.

因此,我们首先创建一个树的实例。然后,我们可以从这个实例(原型)创建我们想要的许多树,并更新它们的位置。我们也可以选择为游戏中的新关卡改变树木的颜色。

The Prototype pattern is quite similar. Instead of creating new objects, we just have to clone the prototypical instance.

原型模式也很类似。我们不需要创建新的对象,我们只需要克隆原型的实例。

3. UML Diagram

3.UML图示

Prototype Pattern

In the diagram, we see that the client is telling the prototype to clone itself and create an object. Prototype is an interface and declares a method for cloning itself. ConcretePrototype1 and ConcretePrototype2 implement the operation to clone themselves.

在图中,我们看到客户端正在告诉原型克隆自己并创建一个对象。Prototype是一个接口,并声明了一个克隆自身的方法。ConcretePrototype1ConcretePrototype2实现克隆自己的操作。

4. Implementation

4.实施

One of the ways we can implement this pattern in Java is by using the clone() method. To do this, we’d implement the Cloneable interface.

我们可以在Java中实现这种模式的方法之一是使用clone()方法。要做到这一点,我们要实现Cloneable接口。

When we’re trying to clone, we should decide between making a shallow or a deep copy. Eventually, it boils down to the requirements.

当我们试图克隆时,我们应该决定是做一个浅层拷贝还是深层拷贝。最终,这将归结为需求。

For example, if the class contains only primitive and immutable fields, we may use a shallow copy.

例如,如果类只包含primitiveimmutable fields,我们可以使用浅层拷贝。

If it contains references to mutable fields, we should go for a deep copy. We might do that with copy constructors or serialization and deserialization.

如果它包含对易变字段的引用,我们应该采用deep copy。我们可以通过复制构造器序列化和反序列化来实现。

Let’s take the example we mentioned earlier and proceed to see how to apply the Prototype pattern without using the Cloneable interface. In order to do this, let’s create an abstract class called Tree with an abstract method ‘copy’.

让我们以前面提到的例子为例,继续看看如何在不使用Cloneable接口的情况下应用Prototype模式。为了做到这一点,让我们创建一个抽象类,名为Tree,有一个抽象方法‘copy’

public abstract class Tree {
    
    // ...
    public abstract Tree copy();
    
}

Now let’s say we have two different implementations of Tree called PlasticTree and PineTree:

现在我们假设有两个不同的Tree实现,叫做PlasticTreePineTree

public class PlasticTree extends Tree {

    // ...

    @Override
    public Tree copy() {
        PlasticTree plasticTreeClone = new PlasticTree(this.getMass(), this.getHeight());
        plasticTreeClone.setPosition(this.getPosition());
        return plasticTreeClone;
    }

}
public class PineTree extends Tree {
    // ...

    @Override
    public Tree copy() {
        PineTree pineTreeClone = new PineTree(this.getMass(), this.getHeight());
        pineTreeClone.setPosition(this.getPosition());
        return pineTreeClone;
    }
}

So here we see that the classes which extend Tree and implement the copy method can act as prototypes for creating a copy of themselves.

所以在这里我们看到,扩展Tree并实现copy方法的类可以作为原型来创建自己的副本。

Prototype pattern also lets us create copies of objects without depending on the concrete classes. Let’s say we have a list of trees and we would like to create copies of them. Due to polymorphism, we can easily create multiple copies without knowing the types of trees.

原型模式还可以让我们在不依赖具体类的情况下创建对象的副本。比方说,我们有一个树的列表,我们想创建它们的副本。由于多态性,我们可以在不知道树的类型的情况下轻松创建多个副本。

5. Testing

5.测试

Now let’s test it:

现在我们来测试一下。

public class TreePrototypesUnitTest {

    @Test
    public void givenAPlasticTreePrototypeWhenClonedThenCreateA_Clone() {
        // ...

        PlasticTree plasticTree = new PlasticTree(mass, height);
        plasticTree.setPosition(position);
        PlasticTree anotherPlasticTree = (PlasticTree) plasticTree.copy();
        anotherPlasticTree.setPosition(otherPosition);

        assertEquals(position, plasticTree.getPosition());
        assertEquals(otherPosition, anotherPlasticTree.getPosition());
    }
}

We see that the tree has been cloned from the prototype and we have two different instances of PlasticTree. We’ve just updated the position in the clone and retained the other values.

我们看到树已经从原型中克隆出来了,我们有两个不同的PlasticTree的实例。我们只是更新了克隆中的位置,保留了其他的值。

Now let’s clone a list of trees:

现在让我们克隆一个树的列表。

@Test
public void givenA_ListOfTreesWhenClonedThenCreateListOfClones() {

    // create instances of PlasticTree and PineTree

    List<Tree> trees = Arrays.asList(plasticTree, pineTree);
    List<Tree> treeClones = trees.stream().map(Tree::copy).collect(toList());

    // ...

    assertEquals(height, plasticTreeClone.getHeight());
    assertEquals(position, plasticTreeClone.getPosition());
}

Notice that we are able to do a deep copy of the list here without being dependent on the concrete implementations of Tree.

请注意,我们能够在这里对列表进行深度复制,而不依赖于Tree.的具体实现。

6. Advantages & Disadvantages

6.优势和劣势

This pattern is handy when our new object is only slightly different from our existing one. In some cases, instances may have only a few combinations of state in a class. So instead of creating new instances, we may create the instances with the appropriate state beforehand and then clone them whenever we want.

当我们的新对象与我们现有的对象只有轻微的不同时,这种模式就很方便。在某些情况下,一个类中的实例可能只有几种状态的组合。因此,与其创建新的实例,我们可以事先创建具有适当状态的实例,然后在我们想要的时候克隆它们

Sometimes, we might encounter subclasses that differ only in their state. We can eliminate those subclasses by creating prototypes with the initial state and then cloning them.

有时,我们可能会遇到一些只在状态上有差异的子类。我们可以通过创建具有初始状态的原型,然后克隆它们来消除这些子类。

Prototype pattern, just like every other design pattern, should be used only when it’s appropriate. Since we are cloning the objects, the process could get complex when there are many classes, thereby resulting in a mess. Additionally, it’s difficult to clone classes that have circular references.

原型模式,就像其他的设计模式一样,应该只在适当的时候使用。由于我们是在克隆对象,当有很多类时,这个过程可能会变得很复杂,从而导致混乱。此外,克隆有循环引用的类也很困难。

7. Conclusion

7.结语

In this tutorial, we learned the key concepts of the Prototype pattern and saw how to implement it in Java. We also discussed some of its pros and cons.

在本教程中,我们学习了Prototype模式的关键概念,并看到了如何在Java中实现它。我们还讨论了它的一些优点和缺点。

As usual, the source code for this article is available over on Github.

像往常一样,本文的源代码可以在Github上获得over