原型模式--简单看法和实例

1)概述
原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的新的对象。其实就是从一个对象再创建另一个可定制的对象,而且不需要知道任何创建的细节,核心就是克隆。
相似的情形有
1.鸣人使用影分身之术,一下子出现无数的鸣人来
2.每当市场出现了一款新的iphone手机,各大厂家就会争先去模仿出一模一样的手机出来,只是它们和我们的原来有很大的区别。我们说的原型模式理论上是一样的,但是细分的还是有区别。

2)疑问
在常规的编程中,我们一般都是用new 构造一个对象出来,这种方式非常简单使用的非常频繁,但是呢这种方式比较适合一两个new对象出来,因为这种方式创建多的话会对程序的空间和时间造成一定的负担,那么在创建较为多的而相似对象时,该怎么处理呢?
3)方案
我们针对这种情况,我们都会通过copy一个对象来创建更多的一个对象,我们称copy的对象为原型对象,也就是我们说的原型模式。

4)使用场景
原型模式主要依据克隆的思想,通过原型对象创建更多的克隆对象,同时要求的是对象的内部必须要有相应的克隆的method,这个方法会返回一个对象的副本。这种方式和之前的工厂模式有很大的局别,之前的工厂模式是按照new创建新的对象的,这种对象虽然使用十分简单,但是在以下的几种情形十分不适合,会对程序的空间和时间造成挺大的负担。而使用原型模式会大大减少对运行效率和空间减轻。
• 1)当一个系统应该独立于它的产品创建、构成和表示时,要使用 Prototype模式
• 2)当要实例化的类是在运行时刻指定时,例如,循环实例化;
• 3)为了避免创建一个与产品类层次平行的工厂类层次时
• 4)当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。(也就是当我们在处理一些对象比较简单,并且对象之间的区别很小,可能只是很固定的几个属性不同的时候,可能我们使用原型模式更合适)。

5)结构
Prototype:抽象类型;给出所有具有原型类所需的接口,定义克隆自己的方法。
ConcreteProtot:具体原型,被复制的对象。实现抽象原型接口,实现具体克隆方法。
Client:客户,提出创建对象的请求,通过调用具体原型,克隆生成一个新的对象。

6)原型模式XML图–浅复制
这里写图片描述

7)代码

//抽象接口
public interface Prototype extends Cloneable{

    public Object clone();
}

//具体接口
public class ConcretePrototype implements Prototype{
    private String name;
    private int age;
    private String sex;

    public ConcretePrototype(){
        this.name = "家兴";
        this.age = 25;
        this.sex = "man";
    }


    public String getName() {
        return name;
    }


    public void setName(String name) {
        this.name = name;
    }


    public int getAge() {
        return age;
    }


    public void setAge(int age) {
        this.age = age;
    }


    public String getSex() {
        return sex;
    }


    public void setSex(String sex) {
        this.sex = sex;
    }


    public Object clone(){
        try {
            return super.clone();
        } catch (Exception e) {
            // TODO: handle exception
            return null;
        }
    }


    /*@Override
    public String toString() {
        return "ConcretePrototype [name=" + name + ", age=" + age + ", sex="
                + sex + "]";
    }*/


}

//客户端
public class Client {
    private Prototype prototype;
    public Prototype operation(Prototype concretePrototype){
        prototype = (Prototype)concretePrototype.clone();
        return prototype;
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Prototype prototype = new ConcretePrototype();
        Client client = new Client();
        System.out.println(client.operation(prototype));

    }

}

输出结果:
com.yatming.prototype.ConcretePrototype@3e2de41d
com.yatming.prototype.ConcretePrototype@3e2de41d
从这个输出结果可以看出他们指向原来的对象,只负责拷贝当前的实例,对引用没有进行拷贝。如果对ConcretePrototype的方法toString进行注释清掉,你就可以看到他们的对象属性都一样的。

8)原型模式的优缺点
Prototype模式有工厂模式和建造者模式都是创建模式,只要是对类和对象创建,所以它们对客户端隐藏了具体的产品类。减少客户对产品的了解
,达到解耦;以此同时这些模式无须改变即可使用与其相关的类等。
下面列出Prototype模式的另外一些优点。
1. 因为增加和删除产品都是在客户端调用产生,他比其他的创建模式更加灵活。
2. 改变值以指定新对象: 高度动态的系统允许你通过对象复合定义新的行为—例如,通过为一个对象变量指定值—并且不定义新的类。你通过实例化已有类并且将这些实例注册为客户对象的原型,就可以有效定义新类别的对象。客户可以将职责代理给原型,从而表现出新的行为。这种设计使得用户无需编程即可定义新“类” 。实际上,克隆一个原型类似于实例化一个类。Prototype模式可以极大的减少系统所需要的类的数目。
3. 改变结构以指定新对象:许多应用由部件和子部件来创建对象。
4. 减少子类的构造 Factory Method 经常产生一个与产品类层次平行的 Creator类层次。Prototype模式使得你克隆一个原型而不是请求一个工厂方法去产生一个新的对象。因此你根本不需要Creator类层次。这一优点主要适用于像 C + +这样不将类作为一级类对象的语言。像Smalltalk和Objective C这样的语言从中获益较少,因为你总是可以用一个类对象作为生成者。在这些语言中,类对象已经起到原型一样的作用了。
5. 用类动态配置应用 一些运行时刻环境允许你动态将类装载到应用中。在像 C + +这样的语言中,Prototype模式是利用这种功能的关键。一个希望创建动态载入类的实例的应用不能静态引用类的构造器。而应该由运行环境在载入时自动创建每个类的实例,并用原型管理器来注册这个实例(参见实现一节) 。这样应用就可以向原型管理器请求新装载的类的实例,这些类原本并没有和程序相连接。 E T + +应用框架[ W G M 8 8 ]有一个运行系统就是使用这一方案的。
6. 一般在初始化的信息不发生改变的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,又对性能是大大的提高,这不是很符合情况么?

Prototype的主要缺陷是每一个Prototype的子类都必须实现clone操作,这可能很困难。
例如,当所考虑的类已经存在时就难以新增 clone操作。当内部包括一些不支持拷贝或有循环引用的对象时,实现克隆可能也会很困难的。

9)再次提出疑问????
clone()方法是这样的,如果字段的数据都是值类型的,则对该字段执行逐位复制,如果字段是引用类型的,则复制引用不复制引用的对象,那么引用的对象数据是不会被克隆过来的。
通过程序来显示结果:

//在ConcretePrototype类中添加这一段代码
 private BasketballExperience be = new BasketballExperience();; 
public BasketballExperience getBe() {
    return be;
}

public void setBe(String playDate,String ballTeam) {
    be.setPlayDate(playDate);
    be.setBallTeam(ballTeam);
}

//添加一个这样的类BasketballExperience,用来给ConcretePrototype来做引用类型
public class BasketballExperience {
    private String playDate;
    private String ballTeam;
    public BasketballExperience(){
    }
    public String getPlayDate() {
        return playDate;
    }
    public void setPlayDate(String playDate) {
        this.playDate = playDate;
    }
    public String getBallTeam() {
        return ballTeam;
    }
    public void setBallTeam(String ballTeam) {
        this.ballTeam = ballTeam;
    }
    @Override
    public String toString() {
        return "BasketballExperience [playDate=" + playDate + ", ballTeam="
                + ballTeam + "]";
    }
}

//客户端的类也要做出相应的改变
public class Client {
    private Prototype prototype; 
    public Prototype operation(Prototype concretePrototype){
        prototype = (Prototype)concretePrototype.clone();
        return prototype; 
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Client client = new Client();
        ConcretePrototype prototype = new ConcretePrototype("家兴","man");
        prototype.setBe("2012-2016", "吉林大学校队");

        ConcretePrototype cp = (ConcretePrototype) client.operation(prototype);
        cp.setBe("2009-2011", "吴川三中校队");
        cp.setAge(90);

        ConcretePrototype cp1 = (ConcretePrototype) client.operation(prototype);
        cp1.setAge(30);
        cp1.setBe("2005-2008", "吴阳中学校队");

        System.out.println(prototype);
        System.out.println(cp);
        System.out.println(cp1);
    }

}

程序执行的结果
这里写图片描述
这个结果说明了这种方式只是浅表复制,所以对于值类型能进行复制更改,但是对于引用类型,就只是复制引用,对引用的对象还是指向原来的对象,所以就出现以上的篮球经验都是最后设置的数据。
深复制与浅复制
浅复制的概念: 被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。
但是存在这样一种情况,就像我刚才哪种情况,三种对象都是不一样的。这种复制的方式我们称为深复制;
深复制的概念: 把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。
下面在重新看一下更改后的代码:

//把BasketballExperience 引用对象时间Cloneable接口
public class BasketballExperience implements Cloneable
//同时在类BasketballExperience 添加克隆方法
public Object Clone(){
        try {
            return this.clone();
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return null;
        }
    }
//在ConcretePrototype修改clone方法
public Object clone(){
            ConcretePrototype cp = new ConcretePrototype(be);
            cp.name = this.name;
            cp.sex = this.sex;
            cp.age = this.age;
            return cp;      
    }
//客户端不做任何修改

程序执行的结果是:
ConcretePrototype [name=家兴, age=0, sex=man, be=BasketballExperience [playDate=2012-2016, ballTeam=吉林大学校队]]
ConcretePrototype [name=家兴, age=90, sex=man, be=BasketballExperience [playDate=2009-2011, ballTeam=吴川三中校队]]
ConcretePrototype [name=家兴, age=30, sex=man, be=BasketballExperience [playDate=2005-2008, ballTeam=吴阳中学校队]]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值