“副本”是网络游戏任务的一种形式,它最初出现于无尽的任务,现在运用得最有名的就是魔兽世界的副本了,我们将它称为副本任务。副本任务一般是一个队伍的人一起闯关的,假设有队伍A、队伍B、队伍C,分别对同一个副本任务发出挑战,他们在副本任务中的行为是不相干的。也就是说,A队伍在副本任务中消灭boss、开启宝箱,不会对B、C两支队伍造成任何影响,B、C同理。因此,“副本”可以理解成,从任务的原型中拷贝,然后给不同的队伍去执行。
现在从“原型模式”来思考一下副本的实现(真正的魔兽世界是如何实现的我不知道)。原型模式的意图是从原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。以魔兽世界中的“死亡矿井”副本为例,它是一个最基本的副本,玩家达到10级就可以接受此副本任务。为了简化模型,我把副本任务的属性分为任务名、敌人数量和难度等级。若玩家进入了一个副本任务,游戏则先会寻找这个副本任务的原型(也就是一个对象),接下来把这个对象复制(clone)一次,再把复制好的对象返回给玩家,告诉玩家:你的副本任务我已经复制好了,由于是从原型中拷贝下来的,所以你在这个任务里怎么样都不会影响到别人。
以下是Java代码:
interface Cloneable{
Object clone();
}
class Deadmines implements Cloneable {
String mapName = "死亡矿井";
int enemies ;
int difficulty ;
public Deadmines(int _difficulty, int _enemies){
difficulty = _difficulty;
enemies = _enemies;
}
public Object clone(){
return new Deadmines(difficulty, enemies);
}
public void print(){
System.out.printf("副本名: %s, 敌人数目: %d, 难度: %d\n", mapName, enemies, difficulty);
}
}
class Prototype
{
public static void main(String[] args) {
Deadmines deadminesPrototype = new Deadmines(10, 200);
Deadmines playerDeadmines = (Deadmines)deadminesPrototype.clone();
playerDeadmines.print();
System.out.println(deadminesPrototype == playerDeadmines);
}
}
输出结果:
副本名: 死亡矿井, 敌人数目: 200, 难度: 15
false
稍微说明一下。main方法中的最后一行是用于判断原型和副本是否为同一引用,结果显然是false,因为副本是被拷贝出来的,并不是直接返回原型。通俗地来讲,原型模式就是以一个对象为模板,来构造与它一样的对象。它可以隐藏具体的克隆细节(如我们在使用时只需要调用clone方法,而不需要知道它是怎么clone的)。
另外还要提到的是,在Java中本身就存在Cloneable接口,在上例中我们是自己重新定义了一个Cloneable。另外,Object类中本身也有一个受保护的方法clone,它可用于改写。在Java文档中写道,Object的clone方法没有继承Cloneable接口,如果直接调用,则会触发CloneNotSupportedException异常。能够调用clone方法的对象一定要继承Cloneable接口。(CloneNotSupportedException - if the object's class does not support the Cloneable interface. Subclasses that override the clone method can also throw this exception to indicate that an instance cannot be cloned.)
总结一下,当一个系统应该独立于它产品创建、构成时、实例化的对象是运行时加载(如副本任务)时、为了避免创建一个与产品类层次平行的工厂类层次时、当一个类的实例只能有几个不同状态组合中的一种时,可以考虑使用原型模式。它在Java、C#中得到了广泛的应用。