设计模式解析与实战之原型模式

来源:互联网 发布:淘宝短网址转换 编辑:程序博客网 时间:2024/05/02 06:12

买了大神关爱民与何红辉所著书籍《设计模式解析与实战》,观后有所感、有所悟。

所谓的原型模式我更喜欢叫它复制拷贝,调用简单,通过类实现Cloneable空接口(该接口是一个标识接口,表示该类可以拷贝,如果不实现该接口,将会抛出异常)调用clone方法拷贝。性能比new 更好,但是开发中需要注意clone方法不会执行类的构造方法。下面看一段代码实例:

public class User implements Cloneable{    private String userName;    private String password;    private String icon;    private ArrayList<Image> mImageList=new ArrayList<Image>();    public String getUserName() {        return userName;    }    public void setUserName(String userName) {        this.userName = userName;    }    public String getPassword() {        return password;    }    public void setPassword(String password) {        this.password = password;    }    public String getIcon() {        return icon;    }    public void setIcon(String icon) {        this.icon = icon;    }    public ArrayList<Image> getmImageList() {        return mImageList;    }    public void setmImageList(ArrayList<Image> mImageList) {        this.mImageList = mImageList;    }    public void addImage(Image mImage){        this.mImageList.add(mImage);    }    @Override    protected User clone() throws CloneNotSupportedException {        return (User) super.clone();    }    @Override    public String toString() {        return "User [userName=" + userName + ", password=" + password                + ", icon=" + icon + ", mImageList=" + mImageList + "]";    }}

测试代码:

public class CloneTest {    public static void main(String[] args) throws CloneNotSupportedException {        User mUser=new User();        mUser.setIcon("xxxxx/icon/xxx.jpg");        mUser.setPassword("password123");        mUser.setUserName("usernameadmin");        Image mImage=new Image();        mImage.setImageUrl("image123434.jpg");        mImage.setName("imageName12334");        mImage.setUploadTime("2015-09-20 12:13:14");        mUser.addImage(mImage);        System.out.println("原型拷贝前:-----"+mUser.toString()+"\n");        User cloneUser=mUser.clone();        System.out.println("原型拷贝前:-----"+mUser.toString());        System.out.println("原型拷贝后:-----"+cloneUser.toString()+"\n");        cloneUser.setPassword("123456");        System.out.println("修改拷贝后原型:-----"+mUser.toString());        System.out.println("修改拷贝后cloneUser:-----"+cloneUser.toString()+"\n");    }}

测试结果:

原型拷贝前:-----User [userName=usernameadmin, password=password123, icon=xxxxx/icon/xxx.jpg, mImageList=[Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg]]]原型拷贝前:-----User [userName=usernameadmin, password=password123, icon=xxxxx/icon/xxx.jpg, mImageList=[Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg]]]原型拷贝后:-----User [userName=usernameadmin, password=password123, icon=xxxxx/icon/xxx.jpg, mImageList=[Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg]]]修改拷贝后原型:-----User [userName=usernameadmin, password=password123, icon=xxxxx/icon/xxx.jpg, mImageList=[Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg]]]修改拷贝后cloneUser:-----User [userName=usernameadmin, password=123456, icon=xxxxx/icon/xxx.jpg, mImageList=[Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg]]]

通过上述测试代码和结果,可以发现,调用clone方法拷贝了一份一模一样的对象,修改原型的值(simple:int)不会影响原型数据,即浅拷贝。再看一段代码测试结果:

        Image mImage2=new Image();        mImage2.setImageUrl("image123434.jpg");        mImage2.setName("imageName12334");        mImage2.setUploadTime("2015-09-20 12:13:14");        cloneUser.addImage(mImage2);原型拷贝前:-----User [userName=usernameadmin, password=password123, icon=xxxxx/icon/xxx.jpg, mImageList=[Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg]]]原型拷贝前:-----User [userName=usernameadmin, password=password123, icon=xxxxx/icon/xxx.jpg, mImageList=[Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg]]]原型拷贝后:-----User [userName=usernameadmin, password=password123, icon=xxxxx/icon/xxx.jpg, mImageList=[Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg]]]修改拷贝后原型:-----User [userName=usernameadmin, password=password123, icon=xxxxx/icon/xxx.jpg, mImageList=[Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg], Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg]]]修改拷贝后cloneUser:-----User [userName=usernameadmin, password=123456, icon=xxxxx/icon/xxx.jpg, mImageList=[Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg], Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg]]]

观察上面结果,发现修改mImageList后原型同时被修改了,造成这个问题的解决办法就需要用到深拷贝,即再拷贝对象时,对于引用字段也要采取拷贝形式,不只是单纯的引用的形式,clone方法修改如下:

@Override    protected User clone() throws CloneNotSupportedException {        User mCloneUser=(User) super.clone();        mCloneUser.setmImageList((ArrayList<Image>) this.mImageList.clone());        return mCloneUser;    }结果如下:原型拷贝前:-----User [userName=usernameadmin, password=password123, icon=xxxxx/icon/xxx.jpg, mImageList=[Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg]]]原型拷贝前:-----User [userName=usernameadmin, password=password123, icon=xxxxx/icon/xxx.jpg, mImageList=[Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg]]]原型拷贝后:-----User [userName=usernameadmin, password=password123, icon=xxxxx/icon/xxx.jpg, mImageList=[Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg]]]修改拷贝后原型:-----User [userName=usernameadmin, password=password123, icon=xxxxx/icon/xxx.jpg, mImageList=[Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg]]]修改拷贝后cloneUser:-----User [userName=usernameadmin, password=123456, icon=xxxxx/icon/xxx.jpg, mImageList=[Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg], Image [uploadTime=2015-09-20 12:13:14, name=imageName12334, imageUrl=image123434.jpg]]]

在源码中该模式应用的范围也挺广的,Simple ArrayList:

public class ArrayList<E> extends AbstractList<E>        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{            .............此处略.............   /**     * Returns a shallow copy of this <tt>ArrayList</tt> instance.  (The     * elements themselves are not copied.)     *     * @return a clone of this <tt>ArrayList</tt> instance     */    public Object clone() {        try {            @SuppressWarnings("unchecked")                ArrayList<E> v = (ArrayList<E>) super.clone();            v.elementData = Arrays.copyOf(elementData, size);            v.modCount = 0;            return v;        } catch (CloneNotSupportedException e) {            // this shouldn't happen, since we are Cloneable            throw new InternalError();        }    }}

下面是一个简单的登录实战实例:

public class User {    private String userName;    private String password;    /**登录成功后返回的信息*/    private UserSession mUserSession=new UserSession();    public String getUserName() {        return userName;    }    public void setUserName(String userName) {        this.userName = userName;    }    public String getPassword() {        return password;    }    public void setPassword(String password) {        this.password = password;    }    public UserSession getUserSession() {        return mUserSession;    }    public void setUserSession(UserSession mUserSession) {        this.mUserSession = mUserSession;    }    @Override    protected User clone(){        User cloneUser=null;        try {            cloneUser = (User) super.clone();            cloneUser.setUserSession(this.mUserSession);        } catch (CloneNotSupportedException e) {            e.printStackTrace();        }        return cloneUser;    }}

接口定义(别问我为什么要这么定义接口,想知道就去看高老师视频吧,我不会告诉你的):

/** * 用户接口 * @author LanYan * */public interface OnAccountListener {    /**     * 登录结果回调接口     */    interface OnLoginCallback{        /**         * 登录成功         * @param mUserSession         */        void onLoginSuccess(UserSession mUserSession);        /**         * 登录失败         * @param e         * @param code         * @param message         */        void onLoginFail(Exception e,int code,String message);    }    /**     * 登录接口     */    interface OnLoginListener{        /**         * 登录方法传入参数 用户名 和登录密码         * @param userName         * @param password         * @param onLoginCallback         */        void onLogin(String userName,String password,OnLoginCallback onLoginCallback);    }    /**     * http请求成功后如果需要数据同步,就调用此方法     */    interface OnSyncListener{        /**         * 同步方法         * @param mUserSession         */        void sync(UserSession mUserSession);    }}

登录实现类:

import clonable.simple.OnAccountListener;public class UserImpl implements OnAccountListener.OnLoginListener ,OnAccountListener.OnSyncListener{    private static UserImpl instance;    public static UserImpl getInstance() {        if(instance==null){            synchronized (UserImpl.class) {                instance=new UserImpl();            }        }        return instance;    }    @Override    public void onLogin(String userName, String password,            OnAccountListener.OnLoginCallback onLoginCallback) {        ............此处略...............    }    @Override    public void sync(UserSession mUserSession) {        ............此处略...............    }}

下面是测试代码:

public class LoginTest{    static User mCloneUser;    public static void main(String[] args) {        User mUser=new User();        mUser.setUserName("admin");        mUser.setPassword("123456");        mCloneUser=mUser.clone();        UserImpl.getInstance().onLogin(mCloneUser.getUserName(),mCloneUser.getPassword(),new OnLoginCallback() {            @Override            public void onLoginSuccess(UserSession mUserSession) {                mCloneUser.setUserSession(mUserSession);                UserImpl.getInstance().sync(mUserSession);            }            @Override            public void onLoginFail(Exception e, int code, String message) {            }        });    }}

OnAccountListener类还可以扩展注册、注销、修改用户信息等方法,原型模式就到这里了,逗比要去学重构啦啦啦。

0 0