zoukankan      html  css  js  c++  java
  • 大话设计模式读书笔记(七) 原型模式

    原型模式(Prototype):

    书中通过小菜要去面试,需要打印简历。而引出了需求。 需要小菜写一份简历类,要求有姓名,年龄,性别。可以设置工作经历,并且需要三份简历。

    未使用设计模式代码:

     1 package Prototype.NoPattern;
     2 
     3 public class Resume {
     4     private String name;//姓名
     5     private int age; //年龄
     6     private String sex; //性别
     7     
     8     private String timeAree; //工作时长
     9     private String company; //公司
    10     
    11     public void setPersonInfo(int age,String sex){//设置个人信息
    12         this.age = age;
    13         this.sex = sex;
    14     }
    15     
    16     public void setWorkExperience(String timeAree,String company){//设置工作经历
    17         this.timeAree = timeAree;
    18         this.company = company;
    19     }
    20     
    21     public void display(){//显示
    22         System.out.println(name+"  "+age+"  "+sex);
    23         System.out.println("工作经历:"+company +";工作年限:"+timeAree);
    24     }
    25     public Resume(String name) {
    26         super();
    27         this.name = name;
    28     }
    29 }
    主程序代码:
     1 public class Main {
     2     public static void main(String[] args) {
     3         Resume a = new Resume("大鸟");
     4         a.setPersonInfo(25, "男");
     5         a.setWorkExperience("2008-2010", "xx公司");
     6         
     7         Resume b = new Resume("大鸟");
     8         b.setPersonInfo(25, "男");
     9         b.setWorkExperience("2008-2010", "xx公司");
    10         
    11         Resume c = new Resume("大鸟");
    12         c.setPersonInfo(25, "男");
    13         c.setWorkExperience("2008-2010", "xx公司");
    14         
    15         a.display();
    16         b.display();
    17         c.display();
    18     }
    19 }
    按照大鸟的话来说,这就相当于 在以前没有打印的年代,手写代码一样。 当需要三份简历的时候,就需要些三次,如果需要二十份,则需要实例化二十次。而且当有一个地方出现错误的时候,需要改20处。很显然这样是很不好的。于是引出了设计模式:原型模式。

    原型模式(Prototype):

    原型模式:用原型实例制定创建类型的种类,并通过克隆这些原型创建新的对象。

    原型模式UML类图:



    源代码:

    1 public interface Prototype extends Cloneable{//抽象克隆对象
    2     public Object clone();
    3 }
    1 public class ConcretePrototype implements Prototype{//具体克隆对象    
    2     public Object clone(){//最简单的克隆方法,由于没有属性就不复制值了
    3         Prototype prototype = new ConcretePrototype();
    4         return prototype;
    5     }
    6 }
     1 public class Client {
     2     private Prototype prototype;//持有需要使用的原型接口对象
     3 
     4     public Client(Prototype prototype) {//构造方法,需要传入一个需要克隆的对象
     5         super();
     6         this.prototype = prototype;
     7     }
     8     public Prototype operation(){
     9         ConcretePrototype concretePrototype =(ConcretePrototype) prototype.clone();
    10         return concretePrototype ;
    11     }
    12 }

    Java中的克隆:

    Java的所有类都是从java.lang.Object类继承而来的,而Object类提供protected Object clone()方法对对象进行复制,子类当然也可以把这个方法置换掉,提供满足自己需要的复制方法。对象的复制有一个基本问题,就是对象通常都有对其他的对象的引用。当使用Object类的clone()方法来复制一个对象时,此对象对其他对象的引用也同时会被复制一份

      Java语言提供的Cloneable接口只起一个作用,就是在运行时期通知Java虚拟机可以安全地在这个类上使用clone()方法。通过调用这个clone()方法可以得到一个对象的复制。由于Object类本身并不实现Cloneable接口,因此如果所考虑的类没有实现Cloneable接口时,调用clone()方法会抛出CloneNotSupportedException异常。

    克隆满足的条件:

    clone()方法将对象复制了一份并返还给调用者。所谓“复制”的含义与clone()方法是怎么实现的。一般而言,clone()方法满足以下的描述:

    1.在派生类中覆盖基类的clone()方法,并声明为public【Object类中的clone()方法为protected的】。 
    2.在派生类的clone()方法中,调用super.clone()。 
    3.在派生类中实现Cloneable接口。 

    克隆后的对象与原对象比较:

      (1)对任何的对象x,都有:x.clone()!=x。换言之,克隆对象与原对象不是同一个对象。

      (2)对任何的对象x,都有:x.clone().getClass() == x.getClass(),换言之,克隆对象与原对象的类型一样。

      (3)如果对象x的equals()方法定义其恰当的话,那么x.clone().equals(x)应当成立的。

      在JAVA语言的API中,凡是提供了clone()方法的类,都满足上面的这些条件。JAVA语言的设计师在设计自己的clone()方法时,也应当遵守着三个

    条件。一般来说,上面的三个条件中的前两个是必需的,而第三个是可选的。

    利用原型模式实现简历打印:

     1 public class Resume implements Cloneable{
     2     private String name;
     3     private int age;
     4     private String sex;
     5     
     6     private String timeAree;
     7     private String company;
     8     
     9     public void setPersonInfo(int age,String sex){
    10         this.age = age;
    11         this.sex = sex;
    12     }
    13     
    14     public void setWorkExperience(String timeAree,String company){
    15         this.timeAree = timeAree;
    16         this.company = company;
    17     }
    18     
    19     public void display(){
    20         System.out.println(name+"  "+age+"  "+sex);
    21         System.out.println("工作经历:"+company +";工作年限:"+timeAree);
    22     }
    23     
    24     public Resume() {
    25         super();
    26     }
    27     
    28     public Resume(String name) {
    29         super();
    30         this.name = name;
    31     }
    32     
    33     public Resume(String name, int age, String sex, String timeAree, String company) {
    34         this.name = name;
    35         this.age = age;
    36         this.sex = sex;
    37         this.timeAree = timeAree;
    38         this.company = company;
    39     }
    40 public Object clone() throws CloneNotSupportedException{
    41 return super.clone();
    42 }
    43 }
    44  
    45 public class Client {//打印方法,通过该方法实现克隆简历,相当于打印功能
    46     Resume resume ;
    47 
    48     public Client(Resume resume) {
    49         super();
    50         this.resume = resume;
    51     }
    52     
    53     public Resume getResume() throws CloneNotSupportedException{
    54         return  (Resume)resume.clone();
    55     }
    56 }
     1 public class Main {
     2     public static void main(String[] args) throws CloneNotSupportedException{
     3         Resume resume = new Resume("小菜");
     4         resume.setPersonInfo(18, "男");
     5         resume.setWorkExperience("2016-2017", "XX公司");
     6         Client c = new Client(resume);
     7         
     8         Resume resume2=c.getResume();
     9         Resume resume3=c.getResume();
    10         resume.display();
    11         resume2.display();
    12         resume3.display();
    13     }
    14 }

    深克隆与潜克隆:

    A:浅复制(浅克隆): 浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。 
    b:深复制(深克隆):深复制把要复制的对象所引用的对象都复制了一遍。

    浅克隆示例:
    
    
     1 public class Dept implements Cloneable{//部门
     2     String id;
     3     String name;
     4     public Dept(String id, String name) {
     5         this.id = id;
     6         this.name = name;
     7     }
     8     
     9     public Dept clone() throws CloneNotSupportedException{
    10         return (Dept) super.clone();
    11     }
    12 }
     1 public class Emp implements Cloneable{
     2     String name ; 
     3     String id;
     4     Dept dept;
     5     public Emp(String name, String id, Dept dept) {
     6         super();
     7         this.name = name;
     8         this.id = id;
     9         this.dept = dept;
    10     }
    11     public Emp clone() throws CloneNotSupportedException{
    12         return (Emp) super.clone();
    13     }
    14 }
     1 public class Main {
     2     public static void main(String[] args) throws CloneNotSupportedException {
     3         Dept d = new Dept("1", "技术部");
     4         Dept d2 = d.clone();
     5         System.out.println(d == d2);
     6         
     7         Emp e = new Emp("小明", "1", d);
     8         Emp e2 = e.clone();
     9         
    10         System.out.println(e==e2);
    11         System.out.println(e.dept==e2.dept);
    12     }
    13 }
    结果为:
    false
    false 
    true

    因为浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。 所以复制员工时,员工所引用的部门还是原来员工的部门。
    如果要深复制,则只需要将员工中的clone方法修改成如下:
    public class Emp implements Cloneable{
        String name ; 
        String id;
        Dept dept;
        public Emp(String name, String id, Dept dept) {
            super();
            this.name = name;
            this.id = id;
            this.dept = dept;
        }
        public Emp clone() throws CloneNotSupportedException{
            Emp emp = (Emp) super.clone();
            emp.dept=dept.clone();
            return emp;
        }
    }
     
    也就是在复制员工的同时,将员工所在的部门同时复制一遍。这就进行了深复制。

    通过序列化实现深克隆:

    1 public class Dept implements Serializable{
    2     private static final long serialVersionUID = 6798532648769041110L;
    3     String id;
    4     String name;
    5     public Dept(String id, String name) {
    6         this.id = id;
    7         this.name = name;
    8     }
    9 }

     1 public class Emp implements Serializable{
     2     String name ; 
     3     String id;
     4     Dept dept;
     5     public Emp(String name, String id, Dept dept) {
     6         super();
     7         this.name = name;
     8         this.id = id;
     9         this.dept = dept;
    10     }
    11     public Object deepClone() throws Exception{
    12         //将该对象序列化成流,因为写在流里的是对象的一个拷贝,
    13         //而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝 
    14         ByteArrayOutputStream bos = new ByteArrayOutputStream();
    15         ObjectOutputStream oos = new ObjectOutputStream(bos);
    16         oos.writeObject(this);
    17         //将流序列化成对象
    18         ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
    19         ObjectInputStream ois = new ObjectInputStream(bis);
    20         return ois.readObject();
    21     }
    22 }
     1 public class Main {
     2     public static void main(String[] args) throws Exception {
     3         Dept d = new Dept("1", "技术部");
     4         
     5         Emp e = new Emp("小明", "1", d);
     6         Emp e2 = (Emp) e.deepClone();
     7         
     8         System.out.println(e==e2);
     9         System.out.println(e.dept==e2.dept);
    10     }
    11 }

    此时,输出的结果一样是false,false.


  • 相关阅读:
    学习PyQt5(二):PyQt5布局管理
    学习PyQt5(一):安装PyQt5以及在PyCharm上配置PyQt5
    Day037--Python--线程的其他方法,GIL, 线程事件,队列,线程池,协程
    Day036--Python--线程
    Day035--Python--管道, Manager, 进程池, 线程切换
    Day034--Python--锁, 信号量, 事件, 队列, 生产者消费者模型, joinableQueue
    Day033--Python--进程
    Day032--Python--操作系统, process进程
    Day30--Python--struct, socketserver
    Day29--Python--缓冲区, 粘包
  • 原文地址:https://www.cnblogs.com/xsyfl/p/6842515.html
Copyright © 2011-2022 走看看