Gof23-Portotype模式

原型模式

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一。

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。

例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

介绍

意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。也就是根据现有的实例来生成新的实例。

主要解决:在运行期根据原型创建实例或删除原型。

何时使用: 

1、对象种类繁多,无法将它们整合到一个类中时

需要处理的类太多,如果将它们分别作为一个类,那么就需要编写很多类文件。

2、难以根据类生成实例时

生成实例的过程太为复杂,难以根据类生成新的实例。

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

生成实例的框架不依赖具体的类,可以先注册一个原型到map中,然后通过复制该原型生成新的实例。

关键代码:

1、实现克隆操作,在 JAVA 实现 Cloneable 接口,重写 clone()

2、原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些"易变类"拥有稳定的接口。

优点:

1、性能提高。

2、逃避构造函数的约束。

缺点:

1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。

2、必须实现 Cloneable 接口。

使用场景:

1、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。

2、性能和安全要求的场景。

3、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。

4、一个对象多个修改者的场景。

5、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。

6、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。

注意事项:

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

角色:

  • Prototype(抽象原型)
  • 负责定义类中应该有哪些方法,以及定义复制现有实例生成新实例的方法。
  • ConcreatePrototype(具体的原型)
  • 负责实现上面接口定义的方法。
  • Client(使用者)
  • 负责使用复制实例的方法生吃新的实例。

UML类图:

实例

Product接口,(抽象原型角色)

/**
 * 复制功能的接口,继承了Cloneable接口,实现了该接口的类的实例,可以调用clone方法实现自动复制实例
 */
public interface Product extends Cloneable{

    /**
     * 使用的方法,具体怎么使用,不用关心,子类自己实现
     * @param s
     */
    public abstract void use(String s);

    /**
     * 用于复制实例的方法
     * @return
     */
    public abstract Product createProduct();
}

Manager类,(使用者角色)

public class Manager {
    /**
     * 用于保存实例和名字之间的对应关系
     */
    private Map<String, Product> showCase = new HashMap<>();

    /**
     * 接收到一组名字和Product接口的子类,注册到showCase中
     * @param name 类名
     * @param proto Product的子类,它实现了Product,也就可以调用它的use方法和createClone方法
     */
    public void register(String name, Product proto) {
        showCase.put(name, proto);
    }

    /**
     * 通过该方法复制实例,Manager类和Product接口没有出现它们的子类,就可以独立修改这两个类,而不受它们子类的影响
     * 降低了代码的耦合性
     * @param protoName 模板名字
     * @return
     */
    public Product create(String protoName) {
        Product product = showCase.get(protoName);
        return product.createProduct();
    }
}

MeeageBox:(具体的原型角色)

public class MessageBox implements Product{
    /**
     * 用于装饰的字符
     */
    private char decoChar;

    public MessageBox(char decoChar) {
        this.decoChar = decoChar;
    }

    /**
     * 自定义use方法
     * @param s
     * @throws UnsupportedEncodingException
     */
    @Override
    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();
    }

     /**
     * 实现复制的方法,通过调用父类的clone方法实现复制
     * @return
     */
    @Override
    public Product createProduct() {
        Product product = null;
        try {
            product = (Product)clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return product;
    }
}

Main类,测试类

public class Main {
    public static void main(String[] args) {
        Manager manager = new Manager();
        MessageBox messageBox = new MessageBox('*');
        // 将具体的原型类注册到manager中
        manager.register("warning", messageBox);

        // 通过manager获得MessageBox的实例
        Product product = manager.create("warning");
        product.use("Hello, world");
    }
}
/**
 * ****************
 * * Hello, world *
 * ****************
 */

#设计模式#
设计模式心得 文章被收录于专栏

笔者学习设计模式的记录与心得。

全部评论

相关推荐

01-14 11:51
已编辑
门头沟学院 FPGA工程师
华为 ict计算硬件工程师 薪资13a
点赞 评论 收藏
分享
雨夜迈巴赫:哪个厂呀
点赞 评论 收藏
分享
2024-12-11 11:40
海南大学 Java
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务