设计模式-原型模式(Prototype Pattern)
一、定义
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
二、概念解释
一个比较简单和直观的例子就是盗版,当正版出现时,会出现基于正版的N种盗版产品,盗版就是拷贝正版再加上与正版有差异的部分而创建的新的对象
三、场景
很多游戏都有武器系统,武器不仅区分种类,有的游戏对于同一种类的同一件武器还会有伤害值的差异,这些武器具有相同的名称、相同的被动技能,但是他们所能造成的伤害会在一个范围内波动,所以对于同一把武器来说如果想爆出一件具有高伤害属性的那就全得靠脸
四、实现
原型模式比较简单,本文通过其与抽象工厂的组合来进行代码示例
1、类图
2、代码实现
首先定义一个生产各种武器类型的工厂接口
public interface WeaponFactory {
/**
* 生产斧子
* @return 斧子
*/
Axe createAxe(Integer damage);
/**
* 生产剑
* @return 剑
*/
Sword createSword(Integer damage);
/**
* 生产法杖
* @return 法杖
*/
Staff createStaff(Integer damage);
}
定义一个工厂的实现类 通过定义的createXxx去进行原型实例对象的拷贝操作
// 通过lombok创建构造三个原型武器实例的构造方法
@RequiredArgsConstructor
public class WeaponFactoryImpl implements WeaponFactory{
private final Axe axe;
private final Sword sword;
private final Staff staff;
@Override
public Axe createAxe(Integer damage) {
return axe.copy(damage);
}
@Override
public Sword createSword(Integer damage) {
return sword.copy(damage);
}
@Override
public Staff createStaff(Integer damage) {
return staff.copy(damage);
}
定义一个接口 提供拷贝方法
public interface Prototype {
/**
* 拷贝方法
* @return 拷贝对象
*/
Object copy(Integer damage);
}
各个武器种类抽象起来 并实现具有拷贝方法的接口
@EqualsAndHashCode
@NoArgsConstructor
public abstract class Axe implements Prototype {
// 定义构造方法用来实现拷贝方法返回的拷贝对象
public Axe(Axe axe) {}
@Override
public abstract Axe copy(Integer damage);
}
@EqualsAndHashCode
@NoArgsConstructor
public abstract class Sword implements Prototype{
public Sword(Sword sword){
}
@Override
public abstract Sword copy(Integer damage);
}
@EqualsAndHashCode
@NoArgsConstructor
public abstract class Staff implements Prototype{
public Staff(Staff staff) {
}
@Override
public abstract Staff copy(Integer damage);
}
具体的武器种类继承抽象武器类 并实现拷贝方法
@EqualsAndHashCode(callSuper = true)
@RequiredArgsConstructor
@ToString
public class CopperAxe extends Axe{
private final String name;
private final String passiveSkill;
private Integer damage;
public CopperAxe(CopperAxe copperAxe, Integer damage) {
super(copperAxe);
this.name = copperAxe.name;
this.passiveSkill = copperAxe.passiveSkill;
this.damage = damage;
}
// 同一件武器具有相同名称和被动 武器伤害在一定的范围内波动
@Override
public CopperAxe copy(Integer damage) {
return new CopperAxe(this, damage);
}
}
@EqualsAndHashCode(callSuper = true)
@RequiredArgsConstructor
@ToString
public class DarkSword extends Sword{
private final String name;
private final String passiveSkill;
private Integer damage;
public DarkSword(DarkSword darkSword, Integer damage) {
super(darkSword);
this.name = darkSword.name;
this.passiveSkill = darkSword.passiveSkill;
this.damage = damage;
}
@Override
public DarkSword copy(Integer damage) {
return new DarkSword(this, damage);
}
}
@EqualsAndHashCode(callSuper = true)
@RequiredArgsConstructor
@ToString
public class HolySword extends Sword{
private String name;
private String passiveSkill;
private Integer damage;
public HolySword(HolySword holySword, Integer damage) {
super(holySword);
this.name = holySword.name;
this.passiveSkill = holySword.passiveSkill;
this.damage = damage;
}
@Override
public HolySword copy(Integer damage) {
return new HolySword(this, damage);
}
}
针对各种不同类型的武器 先创建一批原型 然后再原型基础上做对象的拷贝
@SpringBootTest
public class PrototypeTest {
@Test
public void test() {
// 通过工厂先创建原型对象
WeaponFactory weaponFactory = new WeaponFactoryImpl(new CopperAxe("铜斧", "流血效果"),
new DarkSword("黑暗之剑", "造成伤害提高2倍,收到伤害提高2倍"),
new FireStaff("火焰法杖", "火焰伤害提高100%"));
// 拷贝原型对象 创建多把不同伤害值的武器
for (int i = 0; i < 3; i++) {
Axe axe = weaponFactory.createAxe(new Random().nextInt(10));
Sword sword = weaponFactory.createSword(new Random().nextInt(20));
Staff staff = weaponFactory.createStaff(new Random().nextInt(30));
System.out.println( i + "------" + axe);
System.out.println( i + "------" + sword);
System.out.println( i + "------" + staff);
}
}
}
五 总结
- 原型模式是个比较简单的设计模式,本文提供的实例是自己实现了一套对象的拷贝方法,在Java中,我们可以通过重写clone方法去进行对象的拷贝,当然在重写时,还需要注意根据自己的业务需要选择进行浅拷贝还是深拷贝
- 如果想创建多个属性值一样的对象,通过java的clone方式所创建的对象比new的方式创建的对象性能上要好很多,因为java的clone是基于内存二进制流的拷贝
- 如本文示例,原型模式的应用场景就是根据自己实际的业务需要,对具有相同属性信息的对象不需要去重复的new操作,针对差异属性进行原型实例对象的拷贝创建即可