原型设计模型(深拷贝浅拷贝)

.使用背景

(1)类初始化需要消化非常多的资源,这个资源包括数据,硬件资源,通过原型拷贝可以避免这些消耗

(2)通过new产生一个对象需要非常繁琐的数据准备或访问权限,这时可以使用原型模式

(3)一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。(避免别人改变对象的值,但是可以使用它的值)

注意:

<1>.通过cloneable接口的原型模式在调用clone函数构造实例时并不一定比通过new操作速度快只有通过new构造对象较为耗时或者成本比较高时,通过clone方法才能够获得效率上的提升,因此,在使用cloneable时需要考虑构建对象的成本以及做一些效率上的尝试。

<2>.实现原型模式也不一定非要实现Cloneable接口也有其他的实现方式

 

二.角色

Client:客户端用户

Prototype:抽象类或者接口,声明具备clone能力

ConcretePrototype:具体的原型类。

 

//client.java  

/**
 * Created by Administrator on 2016/3/13 0013.
 */
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        //1.构建文档对象
        WordDocument originDoc = new WordDocument();
        //2.编辑文档,添加图片等
        originDoc.setmText("这是一篇文档");
        originDoc.addmImages("图片1");
        originDoc.addmImages("图片2");
        originDoc.addmImages("图片3");
        originDoc.showDocument();  //原始打印
 
        //以原始文档为原型,拷贝一份副本
        WordDocument doc2 = (WordDocument)originDoc.clone();
        doc2.showDocument();       //拷贝的打印
        //修改文档副本,不会影响原始文档
        doc2.setmText("这是修改过的Doc2文本");
        doc2.showDocument();	//拷贝的打印
        originDoc.showDocument(); 	//原始打印
 
    }
}

//WordDocument.java

 

import java.util.ArrayList;
 
/**
 * Created by Administrator on 2016/3/13 0013.
 */
public class WordDocument implements Cloneable {
    //文本
    private String mText;
    //图片名列表
    private ArrayList<String> mImages = new ArrayList<>();
    public  WordDocument(){
        System.out.println("------WordDocument-----");
    }
 
    //clone这个方法是Object中的,如果没有Cloneable,却调用这个方法会报异常!!!
    @Override
    protected Object clone() throws CloneNotSupportedException {
       try{
           WordDocument doc = (WordDocument)super.clone();
           doc.mText = this.mText;
           doc.mImages = this.mImages;
           return doc;
       }catch(Exception e){
           return null;
       }
    }
 
    public String getmText() {
        return mText;
    }
 
    public void setmText(String mText) {
        this.mText = mText;
    }
 
    public ArrayList<String> getmImages() {
        return mImages;
    }
 
    public void addmImages(String img) {
        this.mImages.add(img);
    }
 
    public void showDocument(){
        System.out.println("--------word content start --------");
        System.out.println("Text :"+mText);
        System.out.println("images List : ");
        for (String imgName : mImages){
            System.out.println("image name :"+ imgName);
        }
        System.out.println("--------word content End --------");
    }
}

//打印结果

------WordDocument-----

--------word content start --------

Text :这是一篇文档

images List : 

image name :图片1

image name :图片2

image name :图片3

--------word content End --------

--------word content start --------

Text :这是一篇文档

images List : 

image name :图片1

image name :图片2

image name :图片3

--------word content End --------

--------word content start --------

Text :这是修改过的Doc2文本

images List : 

image name :图片1

image name :图片2

image name :图片3

--------word content End --------

--------word content start --------

Text :这是一篇文档

images List : 

image name :图片1

image name :图片2

image name :图片3

--------word content End --------

 

 

从上述的结果可以看出  doc2是通过originDoc.clone创建的

然后doc2修改了文本内容以后并不会影响originDoc的文本内容,这就保证了orginDoc的安全性。还要注意,通过clone拷贝对象时,并不会执行构造函数


 三.浅拷贝和深拷贝

上面的例子是浅拷贝,也叫影子拷贝,这份拷贝实际上并不是将原始文档的所有字段都重新构造了一份,而是副本文档的字段引用原始文档的字段。

 

 

也就是说如果A引用B就是说两个对象指向同一个地址,当修改AB也会改变,B修改时A同样改变。

//Client.java(修改了一条)

/**
 * Created by Administrator on 2016/3/13 0013.
 */
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        //1.构建文档对象
        WordDocument originDoc = new WordDocument();
        //2.编辑文档,添加图片等
        originDoc.setmText("这是一篇文档");
        originDoc.addmImages("图片1");
        originDoc.addmImages("图片2");
        originDoc.addmImages("图片3");
        originDoc.showDocument();
 
        //以原始文档为原型,拷贝一份副本
        WordDocument doc2 = (WordDocument)originDoc.clone();
        doc2.showDocument();
        //修改文档副本,不会影响原始文档
        doc2.setmText("这是修改过的Doc2文本");
        doc2.addmImages("哈哈。jpg");  //这一段时新加的
        doc2.showDocument();
        originDoc.showDocument();
 
    }
}
 

结果

------WordDocument-----

--------word content start --------

Text :这是一篇文档

images List : 

image name :图片1

image name :图片2

image name :图片3

--------word content End --------

--------word content start --------

Text :这是一篇文档

images List : 

image name :图片1

image name :图片2

image name :图片3

--------word content End --------

--------word content start --------

Text :这是修改过的Doc2文本

images List : 

image name :图片1

image name :图片2

image name :图片3

image name :哈哈。jpg

--------word content End --------

--------word content start --------

Text :这是一篇文档

images List : 

image name :图片1

image name :图片2

image name :图片3

image name :哈哈。jpg (看这里,这个结果就改变了

--------word content End --------

 

这是因为上文中WordDocumentclone方法中只是简单地进行浅拷贝,引用了新对象doc2mImages只是单纯地指向了this.mImages引用,并没有重新构造一个mImages对象,这样就导致doc2mImages与原始文档中的是同一个对象,因此,修改了其中一个文档中的图片,另一个文档也会受影响。

 

 

解决方法,就是采用深拷贝!!!

 //clone这个方法是Object中的,如果没有Cloneable,却调用这个方法会报异常!!!
    @Override
    protected Object clone() throws CloneNotSupportedException {
       try{
           WordDocument doc = (WordDocument)super.clone();
           doc.mText = this.mText;
//           doc.mImages = this.mImages;
           	//对mImages对象也调用Clone()函数,进行深拷贝
           doc.mImages = (ArrayList<String>)this.mImages.clone(); //这个地方是修改了
           return doc;
       }catch(Exception e){
           return null;
       }
    }

结果

------WordDocument-----

--------word content start --------

Text :这是一篇文档

images List : 

image name :图片1

image name :图片2

image name :图片3

--------word content End --------

--------word content start --------

Text :这是一篇文档

images List : 

image name :图片1

image name :图片2

image name :图片3

--------word content End --------

--------word content start --------

Text :这是修改过的Doc2文本

images List : 

image name :图片1

image name :图片2

image name :图片3

image name :哈哈。jpg

--------word content End --------

--------word content start --------

Text :这是一篇文档

images List : 

image name :图片1

image name :图片2

image name :图片3   (现在就没有被改变

--------word content End --------

分析

可以看出,Doc()Doc2()mText改变内容互不影响,但是mImages引用的都是同一个对象,所以第一个例子doc.mImages = this.mImages;所以引用的都是同一个对象,改变了就都改变了

 

四.Android源码中使用到的原型模式

下面是一个发送短信的Intent对象

Uri uri = Uri.parse("smsto:08880000123");
Intent shareIntent = new Intent(Intent.ACTION_SENDTO,uri);
shareIntent.putExtra("sms_body","The SMS text");
//克隆副本
Intent intent = (Intent)shareIntent.clone();
startActivity(intent);

来看看Intentclone方法是如何实现的:

@Override
public Object clone() {
    return new Intent(this);
}

private Intent(Intent o, boolean all) {
    this.mAction = o.mAction;
    this.mData = o.mData;
    this.mType = o.mType;
    this.mPackage = o.mPackage;
    this.mComponent = o.mComponent;
    if (o.mCategories != null) {
        this.mCategories = new ArraySet<String>(o.mCategories);
    }
}

可以发现clone方法实际上在内部并没有调用super.clone()方法来实现对象拷贝,而是调用了new Intent(this);

(2)Intent的查找与匹配

<1>PackageManageService会在启动后,扫描已安装的apk目录,例如系统App的安装目录为/system/app,第三方的目录/data/app,然后解析apk包下的AndroidManifest.xml文件得到app的相关信息,而每个AndroidManifest.xml又包含了Activity,Service等组件的注册信息,当PMS扫描并且解析这些信息之后就构建整个apk的信息树,大致流程如下图

 

最后会把解析到的Activity,Service,添加到mActivities,mService中,这些类型定义是PackageManagerService的字段。

所以整个已安装apk的信息树就建立了,每个apk的应用名,包名,图标,activity,service等信息都存储在系统中,当用户使用Intent跳转到Activity或者启动某个Service系统则会到这个信息表中进行查找,符合要求的组件则会被启动起来

总结:

在系统启动时PackageManagerService会启动,此时PMS将解析所有已安装的应用的信息,构建一个信息表,当用户通过Intent来跳转所有已安装的应用的信息,构建一个信息表,当用户通过Intent来跳转到某个组件时,会根据Intent中包含的信息到PMS中查找对应的组件列表,最后跳转到目标组件中

 

五.原始模式实践

//login.java

/**
 * Created by Administrator on 2016/3/13 0013.
 */
public interface Login {
    void login();
}

//loginImpl.java

/**
 * Created by Administrator on 2016/3/13 0013.
 */
public class LoginImpl implements Login {
    private  User loginedUser;
    @Override
    public void login() {
        //登陆到服务器,获取用户信息
        User loginedUser = new User();
        //将服务器返回完整信息设置给LoginedUser对象
        loginedUser.age = 22;
        loginedUser.name = "Mr.Simple";
        loginedUser.address = new MyAddress("北京市","海淀区","花园东路");


        //登陆完之后将用户信息设置到Session 中LoginSession.getLoginSession()里
        LoginSession.getLoginSession().setLoginedUser(loginedUser);
    }
}

//LoginSession.java

/**
 * Created by Administrator on 2016/3/13 0013.
 */
public class LoginSession {
    static  LoginSession sLoginSession = null;
    //已登录用户
    private User LoginedUser;
    private LoginSession(){};
    public static  LoginSession getLoginSession(){
        if(sLoginSession == null){
            sLoginSession = new LoginSession();
        }
        return sLoginSession;
    }
    //设置已登录的用户信息,不对外开放
    void setLoginedUser(User user){
        LoginedUser = user;
    }

    public User getLoginedUser(){
//        return LoginedUser;
        //这样只拿到原始对象的拷贝而已,不能改变原来的值
        return LoginedUser.clone();

    }
}

//MyAddress.java

/**
 * Created by Administrator on 2016/3/13 0013.
 */
public class MyAddress {
    //城市
    public String city;
    //区
    public String district;
    //街道
    public String street;
    public MyAddress(String aCity,String aDist,String aStreet){
        city = aCity;
        district = aDist;
        street = aStreet;
    }

    @Override
    public String toString() {
        return "Address [ city = "+city+", district="+district+", street="+street+"]";
    }
}

//User.java

/**
 * Created by Administrator on 2016/3/13 0013.
 */
public class User implements Cloneable {

    public int age ;
    public String name ;
    public String phoneNum;
    public MyAddress address;

    @Override
    public String toString() {
        return "User [ age = "+age+", name="+name+", address="+address+"]";
    }

    @Override
    protected User clone()  {
        User user = null;
        try {
            user = (User)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return user;
    }
}

//MainActivity.java

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //登陆
        LoginImpl loginImpl = new LoginImpl();
        loginImpl.login();
        //获取登陆的user
        User newUser = LoginSession.getLoginSession().getLoginedUser();
        //打印现在用户的信息
        System.out.println("xcqw原始用户"+newUser.toString());
        //改变get获取用户的信息
        newUser.address = new MyAddress("江西省","九江市","大树下");
        System.out.println("xcqw直接改变数据"+LoginSession.getLoginSession().getLoginedUser().toString());
        //通过set改变用户的信息
        newUser.address = new MyAddress("江西省","九江市","大树下");
        LoginSession.getLoginSession().setLoginedUser(newUser);
        System.out.println("xcqw改变数据后,set一下" + LoginSession.getLoginSession().getLoginedUser().toString());

    }

}


结果

xcqw原始用户User [ age = 22, name=Mr.Simple, address=Address [ city = 北京市, district=海淀区, street=花园东路]]

xcqw直接改变数据User [ age = 22, name=Mr.Simple, address=Address [ city = 北京市, district=海淀区, street=花园东路]]

xcqw改变数据后,set一下User [ age = 22, name=Mr.Simple, address=Address [ city = 江西省, district=九江市, street=大树下]]

 

可以看出效果达到了!!!

 

 

优点:

原型模式是在内存中二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。

缺点:

直接在内存中拷贝,构造函数是不会执行的,这个既是优点又是缺点,在实际开发当中注意这个潜在的问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值