原型模式详解

原型模式(Prototype pattern)
定义

通过已有对象,拷贝出一个新对象,具有一模一样的属性,存放在不同的内存地址上

模式总结过程

游戏DOTA,简化规则如下:

  1. 分上中下三路,每路、每30秒就出现一波小兵,各路小兵沿种路向对方进发。
  2. 每波小兵由一个远程小兵和三个进战小兵组成。
  3. 当攻破一路兵营后,小兵会升级成中后,当攻破三路兵营后,会升级成大兵。
  4. 小中大兵的区别在于血量、攻击力和体积不同。
  5. 每个小兵有自己的状态,所以得有一个小兵一个对象。
  6. 游戏分近卫、天灾两大阵营。

  1. 初步的设计,可能是这样子的
// 士兵类
public class Soldier {

    // 等级,1 2 3级
    private int level;

    // 血量
    private double blood;

    // 类型,进战、远程
    private String classify;

    // 攻击力
    private int attack;

    // 体积,大、中、小
    private String display;

    // 阵营,近卫、天灾
    private String camp;

    // 路线,上路、中路、下路
    private String route;
    
    // 其它共有属性没有列出,如速度、仇恨等

    // get set constructor toString
}
// 客户端,以近卫中路作为演示
public class Client {

    // 以近卫中路作为演示
    public static void main(String[] args) {
        System.out.println("游戏开始,时间:00:00");

        System.out.println("近卫中路创建一波小兵,3个近战、1个远程");

        Soldier soldier0001 = new Soldier(1, 600, "进战", 30, "小兵", "近卫", "中路");
        Soldier soldier0002 = new Soldier(1, 600, "进战", 30, "小兵", "近卫", "中路");
        Soldier soldier0003 = new Soldier(1, 600, "进战", 30, "小兵", "近卫", "中路");
        Soldier soldier0004 = new Soldier(1, 300, "远程", 28, "小兵", "近卫", "中路");
        print(soldier0001, soldier0002, soldier0003, soldier0004);

        System.out.println("游戏时间:00:30");

        System.out.println("近卫中路创建一波小兵,3个近战、1个远程");

        Soldier soldier0005 = new Soldier(1, 600, "进战", 30, "小兵", "近卫", "中路");
        Soldier soldier0006 = new Soldier(1, 600, "进战", 30, "小兵", "近卫", "中路");
        Soldier soldier0007 = new Soldier(1, 600, "进战", 30, "小兵", "近卫", "中路");
        Soldier soldier0008 = new Soldier(1, 300, "远程", 28, "小兵", "近卫", "中路");
        print(soldier0005, soldier0006, soldier0007, soldier0008);

        System.out.println("每30秒创建一波小兵,每波小兵会进行攻击、也会被攻击,血量会减少、增多(加血)");

        System.out.println("...");

        System.out.println("当天灾中路兵营被攻破后,近卫中路的小兵升级为中兵(新创建的)");

        System.out.println("近卫中路创建一波中兵,3个近战、1个远程");

        Soldier soldier0009 = new Soldier(2, 1200, "进战", 60, "中兵", "近卫", "中路");
        Soldier soldier0010 = new Soldier(2, 1200, "进战", 60, "中兵", "近卫", "中路");
        Soldier soldier0011 = new Soldier(2, 1200, "进战", 60, "中兵", "近卫", "中路");
        Soldier soldier0012 = new Soldier(2, 600, "远程", 56, "中兵", "近卫", "中路");
        print(soldier0009, soldier0010, soldier0011, soldier0012);

        System.out.println("...");

        System.out.println("当天灾三路兵营被攻破后,近卫中路的中兵升级为大兵(新创建的)");

        System.out.println("近卫中路创建一波大兵,3个近战、1个远程");

        Soldier soldier0013 = new Soldier(3, 2400, "进战", 60, "大兵", "近卫", "中路");
        Soldier soldier0014 = new Soldier(3, 2400, "进战", 60, "大兵", "近卫", "中路");
        Soldier soldier0015 = new Soldier(3, 2400, "进战", 60, "大兵", "近卫", "中路");
        Soldier soldier0016 = new Soldier(3, 1200, "远程", 56, "大兵", "近卫", "中路");
        print(soldier0013, soldier0014, soldier0015, soldier0016);
    }

    // 方便打印
    private static void print(Soldier... soldiers) {
        for (Soldier s : soldiers) {
            System.out.println(s);
        }
        System.out.println();
    }
}
/*
游戏开始,时间:00:00
近卫中路创建一波小兵,3个近战、1个远程
Soldier{level=1, blood=600.0, classify='进战', attack=30, display='小兵', camp='近卫', route='中路'}
Soldier{level=1, blood=600.0, classify='进战', attack=30, display='小兵', camp='近卫', route='中路'}
Soldier{level=1, blood=600.0, classify='进战', attack=30, display='小兵', camp='近卫', route='中路'}
Soldier{level=1, blood=300.0, classify='远程', attack=28, display='小兵', camp='近卫', route='中路'}

游戏时间:00:30
近卫中路创建一波小兵,3个近战、1个远程
Soldier{level=1, blood=600.0, classify='进战', attack=30, display='小兵', camp='近卫', route='中路'}
Soldier{level=1, blood=600.0, classify='进战', attack=30, display='小兵', camp='近卫', route='中路'}
Soldier{level=1, blood=600.0, classify='进战', attack=30, display='小兵', camp='近卫', route='中路'}
Soldier{level=1, blood=300.0, classify='远程', attack=28, display='小兵', camp='近卫', route='中路'}

每30秒创建一波小兵,每波小兵会进行攻击、也会被攻击,血量会减少、增多(加血)
...
当天灾中路兵营被攻破后,近卫中路的小兵升级为中兵(新创建的)
近卫中路创建一波中兵,3个近战、1个远程
Soldier{level=2, blood=1200.0, classify='进战', attack=60, display='中兵', camp='近卫', route='中路'}
Soldier{level=2, blood=1200.0, classify='进战', attack=60, display='中兵', camp='近卫', route='中路'}
Soldier{level=2, blood=1200.0, classify='进战', attack=60, display='中兵', camp='近卫', route='中路'}
Soldier{level=2, blood=600.0, classify='远程', attack=56, display='中兵', camp='近卫', route='中路'}

...
当天灾三路兵营被攻破后,近卫中路的中兵升级为大兵(新创建的)
近卫中路创建一波大兵,3个近战、1个远程
Soldier{level=3, blood=2400.0, classify='进战', attack=60, display='大兵', camp='近卫', route='中路'}
Soldier{level=3, blood=2400.0, classify='进战', attack=60, display='大兵', camp='近卫', route='中路'}
Soldier{level=3, blood=2400.0, classify='进战', attack=60, display='大兵', camp='近卫', route='中路'}
Soldier{level=3, blood=1200.0, classify='远程', attack=56, display='大兵', camp='近卫', route='中路'}
*/

可以看到,对象的创建很频繁、复杂,而且容易出错,这时,原型模式就有用武之地了

  1. 在Soldier类中,提供对象复制的功能,再加一个管理类,管理常用的对象(如大中小兵)
// 士兵类
public class Soldier implements Cloneable {

    // 等级
    private int level;

    // 血量
    private double blood;

    // 类型,进战、远程
    private String classify;

    // 攻击力
    private int attack;

    // 体积
    private String display;

    // 阵营
    private String camp;

    // 路线
    private String route;

    // 其它共有属性没有列出,如速度、仇恨等

    // 提供对象复制的方法
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    
    // get set constructor toString
}
// 士兵管理类
public class SoldierManager {

    // 存放各类小兵,开始就把士兵定义好,后面都要从这里取
    private Map<String, Soldier> map = new HashMap<>();

    // 添加士兵
    public void setSoldier(String classify, Soldier soldier) {
        map.put(classify, soldier);
    }

    // 获取士兵
    public Soldier getSoldier(String classify) {
        return map.get(classify);
    }

    // 移除士兵,并把目标返回
    public Soldier removeSoleier(String classify) {
        return map.remove(classify);
    }
}
// 客户端,以近卫中路作为演示
public class Client {

    // 以近卫中路作为演示
    public static void main(String[] args) throws CloneNotSupportedException {
        System.out.println("游戏开始前,先把各类士兵定义好,并保存到士兵管理类中");
        SoldierManager manager = new SoldierManager();

        Soldier soldier1 = new Soldier(1, 600, "进战", 30, "小兵", "近卫", "中路");
        Soldier soldier2 = new Soldier(1, 300, "远程", 30, "小兵", "近卫", "中路");
        manager.setSoldier("1级近战小兵", soldier1);
        manager.setSoldier("1级远程小兵", soldier2);

        Soldier soldier3 = new Soldier(2, 1200, "进战", 30, "中兵", "近卫", "中路");
        Soldier soldier4 = new Soldier(2, 600, "远程", 30, "中兵", "近卫", "中路");
        manager.setSoldier("2级近战中兵", soldier3);
        manager.setSoldier("2级远程中兵", soldier4);

        Soldier soldier5 = new Soldier(3, 2400, "进战", 30, "大兵", "近卫", "中路");
        Soldier soldier6 = new Soldier(3, 1200, "远程", 30, "大兵", "近卫", "中路");
        manager.setSoldier("3级近战大兵", soldier5);
        manager.setSoldier("3级远程大兵", soldier6);

        System.out.println("游戏开始,时间:00:00");

        System.out.println("近卫中路创建一波小兵,3个近战、1个远程");

        Soldier soldier0001 = manager.getSoldier("1级近战小兵");
        Soldier soldier0002 = (Soldier) soldier0001.clone(); // 这里为什么不用manager.getSoldier("1级近战小兵")?因为这样返回的对象就是同一个了(引用指向一个内存地址)
        Soldier soldier0003 = (Soldier) soldier0001.clone();
        Soldier soldier0004 = manager.getSoldier("1级远程小兵");
        print(soldier0001, soldier0002, soldier0003, soldier0004);

        System.out.println("游戏时间:00:30");

        System.out.println("近卫中路创建一波小兵,3个近战、1个远程");

        Soldier soldier0005 = (Soldier) soldier0001.clone();
        Soldier soldier0006 = (Soldier) soldier0001.clone();
        Soldier soldier0007 = (Soldier) soldier0001.clone();
        Soldier soldier0008 = (Soldier) soldier0004.clone();
        print(soldier0005, soldier0006, soldier0007, soldier0008);

        System.out.println("每30秒创建一波小兵,每波小兵会进行攻击、也会被攻击,血量会减少、增多(加血)");

        System.out.println("...");

        System.out.println("当天灾中路兵营被攻破后,近卫中路的小兵升级为中兵(新创建的)");

        System.out.println("近卫中路创建一波中兵,3个近战、1个远程");

        Soldier soldier0009 = manager.getSoldier("2级近战中兵");
        Soldier soldier0010 = (Soldier) soldier0009.clone();
        Soldier soldier0011 = (Soldier) soldier0009.clone();
        Soldier soldier0012 = manager.getSoldier("2级远程中兵");
        print(soldier0009, soldier0010, soldier0011, soldier0012);

        System.out.println("...");

        System.out.println("当天灾三路兵营被攻破后,近卫中路的中兵升级为大兵(新创建的)");

        System.out.println("近卫中路创建一波大兵,3个近战、1个远程");

        Soldier soldier0013 = manager.getSoldier("3级近战大兵");
        Soldier soldier0014 = (Soldier) soldier0013.clone();
        Soldier soldier0015 = (Soldier) soldier0013.clone();
        Soldier soldier0016 = manager.getSoldier("3级远程大兵");
        print(soldier0013, soldier0014, soldier0015, soldier0016);
    }

    // 方便打印
    private static void print(Soldier... soldiers) {
        for (Soldier s : soldiers) {
            System.out.println(s);
        }
        System.out.println();
    }
}
/*
游戏开始前,先把各类士兵定义好,并保存到士兵管理类中
游戏开始,时间:00:00
近卫中路创建一波小兵,3个近战、1个远程
Soldier{level=1, blood=600.0, classify='进战', attack=30, display='小兵', camp='近卫', route='中路'}
Soldier{level=1, blood=600.0, classify='进战', attack=30, display='小兵', camp='近卫', route='中路'}
Soldier{level=1, blood=600.0, classify='进战', attack=30, display='小兵', camp='近卫', route='中路'}
Soldier{level=1, blood=300.0, classify='远程', attack=30, display='小兵', camp='近卫', route='中路'}

游戏时间:00:30
近卫中路创建一波小兵,3个近战、1个远程
Soldier{level=1, blood=600.0, classify='进战', attack=30, display='小兵', camp='近卫', route='中路'}
Soldier{level=1, blood=600.0, classify='进战', attack=30, display='小兵', camp='近卫', route='中路'}
Soldier{level=1, blood=600.0, classify='进战', attack=30, display='小兵', camp='近卫', route='中路'}
Soldier{level=1, blood=300.0, classify='远程', attack=30, display='小兵', camp='近卫', route='中路'}

每30秒创建一波小兵,每波小兵会进行攻击、也会被攻击,血量会减少、增多(加血)
...
当天灾中路兵营被攻破后,近卫中路的小兵升级为中兵(新创建的)
近卫中路创建一波中兵,3个近战、1个远程
Soldier{level=2, blood=1200.0, classify='进战', attack=30, display='中兵', camp='近卫', route='中路'}
Soldier{level=2, blood=1200.0, classify='进战', attack=30, display='中兵', camp='近卫', route='中路'}
Soldier{level=2, blood=1200.0, classify='进战', attack=30, display='中兵', camp='近卫', route='中路'}
Soldier{level=2, blood=600.0, classify='远程', attack=30, display='中兵', camp='近卫', route='中路'}

...
当天灾三路兵营被攻破后,近卫中路的中兵升级为大兵(新创建的)
近卫中路创建一波大兵,3个近战、1个远程
Soldier{level=3, blood=2400.0, classify='进战', attack=30, display='大兵', camp='近卫', route='中路'}
Soldier{level=3, blood=2400.0, classify='进战', attack=30, display='大兵', camp='近卫', route='中路'}
Soldier{level=3, blood=2400.0, classify='进战', attack=30, display='大兵', camp='近卫', route='中路'}
Soldier{level=3, blood=1200.0, classify='远程', attack=30, display='大兵', camp='近卫', route='中路'}
*/

这样,不用每个士兵对象都new出来,不容易出错,也提高了程序效率;还有,游戏开始前对各类士兵的定义,可以放在manager类,这样可以对客户端隐藏对象的创建过程,简化使用。

例子仅仅为了体现原型模式,没有做线程安全、代码优化、抽取接口等操作,就是怎么方便怎么来

注意

这个例子通过重写Object类的clone方法,实现对象的拷贝,这是一种方法,但是,当对象的属性为引用类型时,需要层层重写clone,这样,会很麻烦。所以,实现对象的拷贝,还可以通过对象序列化进行实现。具体可了解另一篇文章:java浅克隆与深克隆详解

优点
  1. 提供创建相同对象的方法,简单易用、安全高效
  2. 提供管理类,让对象可以理好地分类管理
  3. 让代码更简洁,提高易读性和可维护性
缺点

需要对对象序列化、深克隆和浅克隆的一定理解

应用

在一个程序中,需要大量的对象时,采用原型模式是不错的选择

原型模式是创建型模式,创建型模式还有四个,分别为单例模式工厂方法模式抽象工厂模式建造者模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值