• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

图解设计模式(6):Prototype模式(通过复制生成实例)

设计模式 winrains 1年前 (2019-09-19) 41次浏览

不根据类来生成实例,而是根据实例来生成新实例称为Prototype模式。Prototype有“原型”、“模型”的意思,它是根据实例原型、实例模式来生成新的实例。
在Java中,可以使用new关键字指定类名来生成类的实例。但是,在开发过程中,有时也会有“在不指定类名的前提下生成实例”的需求。

1 Prototype模式中的角色

  • Prototype(原型)

负责定义用于复制现有实例来生成新实例的方法,在示例中,对应Product接口。

  • ConcretePrototype(具体的原型)

负责实现复制现在实例并生成新实例的方法,在示例中,MessageBox类和UnderlinePen类扮演此角色。

  • Client(使用者)

负责使用复制实例的方法生成新的实例。在示例中,对应Manager类。

2 Prototype模式类图

3 示例程序

3.1 类和接口一览表

名字 说明
framework Product 声明了抽象方法use和createClone的接口
framework Manager 调用createClone方法复制实例的类
无名 MessageBox 将字符串放入方框中并使其显示出来的类。实现了use和createClone方法
无名 UnderlinePen 给字符串加上下划线并使其显示出来的类。实现了use和createClone方法
无名 Main 测试程序行为的类

3.2 类图

3.3 示例代码

Product接口

是具有复制功能的接口。该接口继承了java.lang.Cloneable接口。use方法用于“使用”的方法,具体怎么“使用”,由子类去实现。createClone方法是用于复制实例的方法。

package framework;
import java.lang.Cloneable;
public interface Product extends Cloneable {
    public abstract void use(String s);
    public abstract Product createClone();
}

Manager类

使用Product接口来复制实例。
register方法会将接收到的“名字”和“Product接口”注册到showcase中。
请注意:在Product接口和Manager类的代码中完全没有出现MessageBox类和UnderlinePen类的名字,这也意味着我们可以独立地修改ProductManager,不受MessageBox类和UnderlinePen类的影响。这是非常重要的,因为一旦在类中使用到了别的类名,就意味着该类与其它类紧密地耦合在了一起。
Manager类中,并没有写明具体的类名,仅仅使用了Product这个接口名。也就是说,Product接口成为了连接Manager类与其它具体类之间的桥梁。

package framework;
import java.util.*;
public class Manager {
    private HashMap showcase = new HashMap();
    public void register(String name, Product proto) {
        showcase.put(name, proto);
    }
    public Product create(String protoname) {
        Product p = (Product)showcase.get(protoname);
        return p.createClone();
    }
}

MessageBox类

实现了Product接口。

import framework.*;
public class MessageBox implements Product {
    private char decochar;
    public MessageBox(char decochar) {
        this.decochar = decochar;
    }
    public void use(String s) {
        int length = s.getBytes().length;
        for (int i = 0; i < length + 4; i++) {
            System.out.print(decochar);
        }
        System.out.println("");
        System.out.println(decochar + " "  + s + " " + decochar);
        for (int i = 0; i < length + 4; i++) {
            System.out.print(decochar);
        }
        System.out.println("");
    }
    public Product createClone() {
        Product p = null;
        try {
            p = (Product)clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return p;
    }
}

UnderlinePen类

实现了Product接口。

import framework.*;
public class UnderlinePen implements Product {
    private char ulchar;
    public UnderlinePen(char ulchar) {
        this.ulchar = ulchar;
    }
    public void use(String s) {
        int length = s.getBytes().length;
        System.out.println("\""  + s + "\"");
        System.out.print(" ");
        for (int i = 0; i < length; i++) {
            System.out.print(ulchar);
        }
        System.out.println("");
    }
    public Product createClone() {
        Product p = null;
        try {
            p = (Product)clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return p;
    }
}

Main类

测试程序。

import framework.*;
public class Main {
    public static void main(String[] args) {
        // 准备
        Manager manager = new Manager();
        UnderlinePen upen = new UnderlinePen('~');
        MessageBox mbox = new MessageBox('*');
        MessageBox sbox = new MessageBox('/');
        manager.register("strong message", upen);
        manager.register("warning box", mbox);
        manager.register("slash box", sbox);
        // 生成
        Product p1 = manager.create("strong message");
        p1.use("Hello, world.");
        Product p2 = manager.create("warning box");
        p2.use("Hello, world.");
        Product p3 = manager.create("slash box");
        p3.use("Hello, world.");
    }
}

运行结果

"Hello, world."
 ~~~~~~~~~~~~~
*****************
* Hello, world. *
*****************
/////////////////
/ Hello, world. /
/////////////////

4 在什么情况下采用Prototype模式

  • 对象各类繁多,无法将它们整合到一个类中时

如果将每种模式都编写一个类,类的数量会非常庞大,源程序的管理也会变得非常困难。

  • 难以根据类生成实例时

比如想生成一个和用户通过一系列鼠标操作所创建出来的实例完全一样的实例。此时,与根据类名来生成实例相比,根据实例来生成实例要简单得多。

  • 想解耦框架与生成的实例时

Manager类的create方法中,并没有使用类名,取而代之使用了”strong message”等字符串为生成的实例命名。与new相比,这种方式具有更好的通用性,而且将框架从类名的束缚中解脱出来了。

5 为什么不直接通过new的方式创建类的实例

面向对象编程的目标之一,即“作为组件复用”。
在代码中要使用类的名字并非总是坏事。不过,一旦在代码中出现要使用的类的名字,就无法与该类分离开来,也就无法实现利用。
当多个类必须紧密结合时,代码中出现这些类的名字是没有问题的。但是如果那些需要被独立出来作为组件利用的类的名字出现在代码中,那就有问题了。
clone方法所进行的复制只是将被复制实例的字段直接复制到新的实例中,它并没有考虑字段中所保存的实例的内容。例如,当字段中保存的是数组时,如果使用clone方法进行复制,则只会复制该数组的引用,并不会一一复制数组中的元素。
像上面这样的字段对字段的复制被称为浅复制。clone方法所进行的复制就是浅复制。

摘自《图解设计模式》


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (1)