原型模式的使用场景

原型模式的使用场景

(1)类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗。
(2)通过new一个对象需要非常繁琐的数据准备或访问权限,可以使用原型模式。
(3)一个对象需要提供给其他对象访问,而且各个调用者可能需要修改其值,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝

**注:**通过实现Cloneable接口的原型模式,在调用clone函数实例时比一定比new 操作要块,只有new构造对象非常耗时或者成本较高时,通过clone方法才能得到效率上的提升。因此在使用Cloneable时需要考虑构建对象成本以及效率上的一些测试。

文件类-WordDocument
    public class WordDocument implements Cloneable {
    //文本
    private String mText;
    //图片名列表
    private ArrayList mImages = new ArrayList();
    //原始拷贝方法 浅拷贝
//    @Override
//    protected WordDocument clone() {
//        WordDocument document = null;
//        try {
//            document = (WordDocument) super.clone();
//            document.mText = this.mText;
//            document.mImages = this.mImages;
//            return document;
//        } catch (CloneNotSupportedException e) {
//            e.printStackTrace();
//        }
//        return null;
//    }
    //深拷贝--建议使用
    @Override
    protected WordDocument clone() {
        WordDocument document = null;
        try {
            document = (WordDocument) super.clone();
            document.mText = this.mText;
            //对yimage函数使用coloen
            document.mImages = (ArrayList) this.mImages.clone();
            return document;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
    public WordDocument() {
        System.out.println("----------WordDocument构造函数");
    }
    public String getmText() {
        return mText;
    }
    public void setmText(String mText) {
        this.mText = mText;
    }
    public ArrayList getmImages() {
        return mImages;
    }
    public void addImages(String img) {
        this.mImages.add(img);
    }
    public void showDocument() {
        System.out.println("word content start");
        System.out.println("Text:" + mText);
        System.out.println("imagesList:");
        for (String imageName : mImages) {
            System.out.println("image name:" + imageName);
        }
        System.out.println("word content end");
    }
}
输出类-client
    public class Client {
    public static void main(String[] args) {
        //1构建文档对象
        WordDocument wordDocument = new WordDocument();
        //2编辑文档添加图片等
        wordDocument.setmText("这是一篇文档");
        wordDocument.addImages("图片一");
        wordDocument.addImages("图片二");
        wordDocument.addImages("图片三");
        wordDocument.showDocument();
        //以原始文档为原型,拷贝一份副本
        WordDocument doc = wordDocument.clone();
        doc.showDocument();
        //修改副本不会影响原始文档
        doc.setmText("修改doc文本");
        doc.addImages("哈哈.jpg");
        doc.showDocument();
        wordDocument.showDocument();
    }
}

总结
浅拷贝遇到的问题
说明 WordDocument的clone方法只是简单的进行浅拷贝,引用类型的新对象doc的mImage只是单纯的指向了this.mImages的引用,并没有重新构造mImages对象,然后将原始文档中的图片添加到新的mImages对象中,这样导致doc中的mImages与原始文件中的是同一个对象,因此修改了其中一个文档中的图片,另一个文档也会受到影响.
解决方法:采用深拷贝 即–在拷贝对象时,对引用型的字段也要采用拷贝的形式,而不是单纯的引用形式.

源码使用场景-Intent

Intent用于跳转Activity、启动服务、发布广播等功能,它是Android系统各个组件之间的纽带,也是组件之间传递数据的载体,正式Intent的存在才使得Android各个组件之间的耦合性很低。
发信息操作
    Uri uri = Uri.parse("smsto:13426074245");
                Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
                intent.putExtra("sms_body", "send content");
                Intent sendTo = (Intent) intent.clone();
                startActivity(sendTo);
上述代码中构建了一个发短信的Intent对象,并设置了发短信的内容,通过原始Intent的clone()方法构建一个Intent副本,在使用副本进行发短信操作。
源码
     @Override
    public Object clone() {
        return new Intent(this);
    }
    /**
     * Copy constructor.
     */
    public Intent(Intent o) {
        this(o, COPY_MODE_ALL);
    }

    private Intent(Intent o, @CopyMode int copyMode) {
        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<>(o.mCategories);
        }
        if (copyMode != COPY_MODE_FILTER) {
            this.mFlags = o.mFlags;
            this.mContentUserHint = o.mContentUserHint;
            this.mLaunchToken = o.mLaunchToken;
            if (o.mSourceBounds != null) {
                this.mSourceBounds = new Rect(o.mSourceBounds);
            }
            if (o.mSelector != null) {
                this.mSelector = new Intent(o.mSelector);
            }
            if (copyMode != COPY_MODE_HISTORY) {
                if (o.mExtras != null) {
                    this.mExtras = new Bundle(o.mExtras);
                }
                if (o.mClipData != null) {
                    this.mClipData = new ClipData(o.mClipData);
                }
            } else {
                if (o.mExtras != null && !o.mExtras.maybeIsEmpty()) {
                    this.mExtras = Bundle.STRIPPED;
                }
                // Also set "stripped" clip data when we ever log mClipData in the (broadcast)
                // history.
            }
        }
    }

通过源码可以看到clone方法实际上在内部并没有调用super.clone()方法来实现对象拷贝,而是调用了new Intent(this)。这个在上文中提到过,使用clone 和new 需要根据构造对象的成本来决定,如果对象的构造成本比较高或者构造比较麻烦,那么使用clone()函数效率较高,否则可以使用new 的形式。(这就和C++的拷贝构造函数完全一致),将原始对象作为构造函数的参数,然后在构造函数内将原始对象的数据逐个拷贝一遍。到此克隆过程就完成了。

Android系统启动之后就会注册各种系统服务,入WindowManagerService、ActivityManagerService等,其中就有一个PackageManagerService(简称PMS)。PMS启动后会扫描系统中已安装的apk目录,例如系统APP的安装目录为/system/aPP,第三方应用的目录为/data/aPP,PMS会解析apk包下的AndroidManifest.xml文件得到APP相关信息,而每个AndroidManifest.xml又包含了Activity、Service等组件的注册信息,当PMS扫描并且解析完这些信息之后就构建好了这个app的信息树.

案例demo

public class User{
    public int age;
    public String name;
    public String phoneNum;
    public Adress adress;
    @Override
    public String toString() {
        return "User[aget=" + age + ",name=" + name + ",phoneNum=" + phoneNum + "adress=" + adress + "]";
    }
}
public class LoginImpl implements Login {
    public LoginImpl() {
        longin();
    }
    //登录成功调用 为了简化,在初始化时就调用了
    @Override
    public void longin() {
        User user = new User();
        user.age = 22;
        user.name = "Mr.Simple";
        user.adress = new Adress("北京", "朝阳", "四惠");
        LoginSession.getLoginSession().setLoginUser(user);
    }
}

    public class LoginSession {
    static LoginSession loginSession = null;
    private User loingUser;
    private LoginSession() {}
    public static LoginSession getLoginSession() {
        if (loginSession == null) {
            loginSession = new LoginSession();
        }
        return loginSession;
    }
    void setLoginUser(User user) {
        loingUser = user;
    }
    public User getLoingUser() {
        return loingUser;
    }
}

    执行
new LoginImpl();
System.out.println(LoginSession.getLoginSession().getLoingUser().toString());
User user = LoginSession.getLoginSession().getLoingUser();
user.adress = new Adress("name", "obname", "ssname");
System.out.println(LoginSession.getLoginSession().getLoingUser().toString());
输出:
User[aget=22,name=Mr.Simple,phoneNum=nulladress=Adress[ city=北京,district=朝阳,street=四惠]]
User[aget=22,name=Mr.Simple,phoneNum=nulladress=Adress[ city=name,district=obname,street=ssname]]
    

修改数据的引用是一样的 即便没有通过构造修改参数也会生效
使用原型模式clone出一个备份使用,即便客户端无意识修改了,对保存参数的类也不会影响修改如下:

user修改
public class User implements Cloneable {
    public int age;
    public String name;
    public String phoneNum;
    public Adress adress;
    //添加clone方法
    @Override
    protected User clone() {
        User user = null;
        try {
            user = (User) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return user;
    }
    @Override
    public String toString() {
        return "User[aget=" + age + ",name=" + name + ",phoneNum=" + phoneNum + "adress=" + adress + "]";
    }
}
public class LoginSession {
    static LoginSession loginSession = null;
    private User loingUser;
    private LoginSession() {
    }
    public static LoginSession getLoginSession() {
        if (loginSession == null) {
            loginSession = new LoginSession();
        }
        return loginSession;
    }
    void setLoginUser(User user) {
        loingUser = user;
    }
    //使用clone函数
    public User getLoingUser() {
        return loingUser.clone();
    }
}

优点缺点对比

优点: 原型模式是在内存中二进制流的拷贝,要比直接new 一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好的体现其有点
缺点: 它的优点也是缺点,直接在内存汇总拷贝,构造函数是不会执行的,在实际开发中,应该注意这个潜在的问题.优点是减少了约束,缺点也是减少了约束,需要再时机应用的时候考虑

参考–android源码设计模式解析与实战

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值