• 6、原型模式


    原型模式:

    用原型实例指定创建对象的种类,并且通过拷贝这种原型创建新的对象。(从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节)

    使用场景: 1、资源优化场景。 2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。 3、性能和安全要求的场景。 4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。 5、一个对象多个修改者的场景。 6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。 7、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。

    注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。

    原型模式UML结构图:

    对于深复制与浅复制(深拷贝与浅拷贝)的简单区别:

    浅拷贝:只负责克隆按值传递的数据(比如基本数据类型、String类型),所有引用的对象仍然指向原来的对象,即修改该引用对象时修改的是最初的那个对象。
    深拷贝:除了浅拷贝要克隆的值外,还负责克隆引用类型(属性的类型也是对象)的数据,即把引用对象的变量指向复制过的新对象,而不是,而不是原有被引用的对象,即修改该引用对象时不会影响最初的那个对象。

    我们在执行Clone函数本身时,实际其内部实现是浅复制,当我们需要的属性值是引用对象并需要作出修改时,我们就应该采用深复制的手段(重写Clone函数,使得每一步都是浅复制)

    例(原型模式基本代码):

    	abstract class Prototype
        {
            private string id;
    
            // Constructor 
            public Prototype(string id)
            {
                this.id = id;
            }
    
            // Property 
            public string Id
            {
                get { return id; }
            }
    
            public abstract Prototype Clone();  //关键在于此接口
        }
    
        class ConcretePrototype1 : Prototype
        {
            // Constructor 
            public ConcretePrototype1(string id)
                : base(id)
            {
            }
    
            public override Prototype Clone()
            {
                // Shallow copy 
                return (Prototype)this.MemberwiseClone();  //创建当前对象的浅复制副本,方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象,如果字段是值类型的,则对该字段执行逐位复制,如果该字段是引用类型,则复制其引用但是不复制引用的对象。
            }
        }
    
    
        class ConcretePrototype2 : Prototype
        {
            // Constructor 
            public ConcretePrototype2(string id)
                : base(id)
            {
            }
    
            public override Prototype Clone()
            {
                // Shallow copy 
                return (Prototype)this.MemberwiseClone();
            }
        }
    
    	//Client:
    	ConcretePrototype1 p1 = new ConcretePrototype1("I");
        ConcretePrototype1 c1 = (ConcretePrototype1)p1.Clone();
    

    由于克隆实在太过常用,在.NET中System命名空间提供了ICloneable接口,其中唯一的方法就是Clone(),我们只需要实现这个接口就好了,就不用再新定义Prototype原型象类了。

    例(简历复制——C#):

    	//简历
        class Resume : ICloneable
        {
            private string name;
            private string sex;
            private string age;
            private string timeArea;
            private string company;
    
            public Resume(string name)
            {
                this.name = name;
            }
    
            //设置个人信息
            public void SetPersonalInfo(string sex, string age)
            {
                this.sex = sex;
                this.age = age;
            }
            //设置工作经历
            public void SetWorkExperience(string timeArea, string company)
            {
                this.timeArea = timeArea;
                this.company = company;
            }
    
            //显示
            public void Display()
            {
                Console.WriteLine("{0} {1} {2}", name, sex, age);
                Console.WriteLine("工作经历:{0} {1}", timeArea, company);
            }
    
            public Object Clone()
            {
                return (Object)this.MemberwiseClone();
            }
        }
    

    上面的案例都是浅复制实现,如果我们涉及到引用对象时,就要采用深复制(有的时候引用里面嵌套这引用,导致引用有很多层,操作就会比较复杂,深复制需要深入到多少层需要事先就要考虑好,同时必须注意避免循环引用的问题,否则程序将会一直执行复制操作,陷入死循环)。

    例(简历复制(深复制)——C#):

     	//简历
        class Resume : ICloneable
        {
            private string name;
            private string sex;
            private string age;
    
            private WorkExperience work;
    
            public Resume(string name)
            {
                this.name = name;
                work = new WorkExperience();
            }
    
            private Resume(WorkExperience work)  //提供给Clone函数调用的私有构造函数,以便克隆“工作经历”数据。
            {
                this.work = (WorkExperience)work.Clone();  //完成工作经历的内部克隆
            }
    
            //设置个人信息
            public void SetPersonalInfo(string sex, string age)
            {
                this.sex = sex;
                this.age = age;
            }
            //设置工作经历
            public void SetWorkExperience(string workDate, string company)
            {
                work.WorkDate = workDate;
                work.Company = company;
            }
    
            //显示
            public void Display()
            {
                Console.WriteLine("{0} {1} {2}", name, sex, age);
                Console.WriteLine("工作经历:{0} {1}", work.WorkDate, work.Company);
            }
    
            public Object Clone()
            {
                Resume obj = new Resume(this.work);  //调用私有的构造方法,让工作经历克隆完成,然后再给这个“简历”对象相关字段赋值,最终返回一个深复制的简历对象。
    
                obj.name = this.name;
                obj.sex = this.sex;
                obj.age = this.age;
                return obj;
            }
    
        }
    
        //工作经历
        class WorkExperience : ICloneable
        {
            private string workDate;
            public string WorkDate
            {
                get { return workDate; }
                set { workDate = value; }
            }
            private string company;
            public string Company
            {
                get { return company; }
                set { company = value; }
            }
    
            public Object Clone()
            {
                return (Object)this.MemberwiseClone();
            }
        }
    
    

    在某些特定场合,会经常用到深复制和浅复制,比如在数据集对象DataSet,就有Clone()方法和Copy()方法,Clone()方法用来复制DataSet的结构但是不复制数据,实现了原型模式的浅复制,Copy()方法不但复制结构也复制数据,其实就是实现了原型模式的深复制。

    Prototype模式与Factory模式的关系 :

    虽然原型引入的初衷是像上面所说,但实现起来,却完全可以达到工厂模式的效果;而且,用起来甚至比工厂模式更方便、灵活。对于工厂模式与原形模式在功能上 的这点巧合,也许是因为本来工厂模式和 原型模式都是创建型模式,这样,他们的基本功能都能生成对象,因而使得原型模式在功能上可以代替工厂模式。

    在原型模式中,你完全可以同样定义一个这样的 “抽象产品——具体产品”层次,再利用具体产品本身的clone功能来产生具体产品本身。从而达到实现工厂模 式功能的目的。实际上,在原型模式中,每个具体产品就扮演了工厂模 式里的具体工厂的角色(因为每个具体产品都具有生成自己拷贝的功能,从这种意义上讲,这正是工厂的作用)。
    原型模式提供了简化的创建结构。工厂方法模式常常需要有一个与产品类等级结构相同的等级结构,而原型模式就不需要这样。

    Prototype模式的最主要缺点就是每个原 型的子类都必须实现clone的操作,尤其在包含引用类型的对象时,clone方法会比较麻烦,必须要能够递归地让所有的相关对象都要正确地实现克隆。

    下面我们对上面的内容强化练习一下(这次我们采用Java版本):

    例(订单处理系统(深复制)——Java):

    现在有一个订单处理系统,里面有一个保存订单的业务 功能,需求:每当订单的预定产品数量超过1000的时候,就需要把订单拆成两份订单来保存。如果拆成两份后还是超过1000,则继续拆分,直到每份产品预订数量不超过1000。
    根据业务,目前的订单系统分成两种,一种是个人订单、一种是公司订单。
    客户名称、产品对象(ID,Name),订购产品数量。
    公司名称、产品对象(ID,Name),订购产品数量。

    UML图:

    //Product(产品类):
    
    /*
     * 浅克隆(浅复制)是对要克隆的对象,其中的基本类型复制一份新的产生给对象。但是对于非基本类型的数据类型,仅仅是复制一份引用给新产生的对象。
     * 即基本类型是新产生的,非基本类型是复制一份内存引用。
     * 
     * 实现步骤:
     * 1.实现Cloneable接口
     * 要clone方法,为什么还要实现Cloneable接口呢?我们可以看看,Cloneable其中是一个空方法,它和RandomAccess一样都属于,标识接口。
     * 针对的是Object里的clone方法,进行重写。告诉Object而已。
     * 如果调用了super.clone()方法。没有实现Cloneable接口。那么会抛出异常CloneNotSupportedException异常。
     * 
     * 2.重写clone方法。
     * JDK中的API文档也说明了,这是一个对象拷贝的过程,不是对象的初始化过程。他包含了一些原有对象的信息。
     * Object中的clone的返回值是native。
     * native属于一般是由C语言实现,效率一般都高于java代码,所以这里我们没有选择new一个新的对象,而是选择对象拷贝。
     * 
     * Object中的clone修饰符是protected,我们要把它改成public。
     */
    
    /*
     * 深度克隆,其实就是对于浅度克隆的一种完善,浅度克隆只能完成基本类型的克隆,对于非基本类型无法克隆,只能复制内存地址到新的对象上。
     * 而深度克隆不同,深度克隆是完成了,基本数据类型和非基本数据类型,都可以克隆。
     * 深复制重点在于:在主类上添加子类的Clone方法。(即多层浅复制)
     */
    
    public class Product implements Cloneable {
    
    	private int ID;
    	private String name;
    	public Product() {
    		super();
    	}
    	public Product(int iD, String name) {
    		super();
    		ID = iD;
    		this.name = name;
    	}
    	public int getID() {
    		return ID;
    	}
    	public void setID(int iD) {
    		ID = iD;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	
    	@Override
    	public Object clone() throws CloneNotSupportedException {
    		//数据类型都是基本数据类型,执行浅复制
    		return super.clone();	
    	}
    	
    	@Override
    	public String toString() {
    		return "  ID:"+ID+" Name:"+name;
    	}
    }
    
    //Order(订购顺序):
    
    public class Order implements Cloneable {
    
    	private int num;
    	private String name;
    	private Product product;
    	public Order() {
    		super();
    	}
    	public Order(int num, String name, Product product) {
    		super();
    		this.num = num;
    		this.name = name;
    		this.product = product;
    	}
    	public int getNum() {
    		return num;
    	}
    	public void setNum(int num) {
    		this.num = num;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public Product getProduct() {
    		return product;
    	}
    	public void setProduct(Product product) {
    		this.product = product;
    	}
    	@Override
    	public Object clone() throws CloneNotSupportedException {
    		//首层浅复制
    		Order order = (Order) super.clone();
    		//相对于order的深复制(相对于product的浅复制)
    		//深复制实质是多层浅复制
    		order.setProduct((Product)product.clone());
    		return order;
    	}
    	@Override
    	public String toString() {
    		return "订单数目:"+num+"  订单类型:"+name+product.toString();
    	}
    }
    
    
    //main:
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Test {
    	 
        public static void main(String[] args) throws CloneNotSupportedException {
            int MAX=1000;
        	List<Order> list = new ArrayList<Order>();
            Product product = new Product(1, "产品1");
            Order order = new Order(3000,"公司订单",product);
            int y=order.getNum();
            while (y>MAX) {
                Order tmp = (Order) order.clone();
                tmp.setNum(MAX);
                list.add(tmp);
                y=order.getNum()-MAX;
                order.setNum(y);       
            }
            list.add(order);
            System.out.println("共有"+list.size()+"份订单");
            for (int i=0; i<list.size(); i++) {
                System.out.println(list.get(i).toString());
            } 
        }
    }
    
  • 相关阅读:
    SQL Server 之 内部连接
    SQL Server 之 子查询与嵌套查询
    ASP.NET MVC5高级编程 之 视图
    ASP.NET MVC5高级编程 之 路由
    jQuery返回顶部实用插件YesTop
    jQuery照片墙相册
    js功能实现的特效--距离新年还有多少天
    圣杯布局小结
    等高分栏布局小结
    jQuery设置内容和属性方
  • 原文地址:https://www.cnblogs.com/study-hard-forever/p/13160972.html
Copyright © 2020-2023  润新知