设计模式之原型模式(Prototype 模式)

  • 引入原型模式
  • 原型模式的实例
  • 为什么需要使用原型模式

引入原型模式

如果读者很熟悉javascript的话,对原型这个词应该不会陌生。

原型是用来生成实例的,但不是利用类来生成实例,而是通过实例来生成实例。

为什么我们需要用过类来生成实例呢?

联想到浏览器中,如果我们生成了一个button实例,这个button实例经过一系列操作,携带了各种信息,比如button加颜色,加背景图,加文字,加事件等等。如果我们这时候需要和这个button实例完全一样的一个实例,仅仅通过类new 一个button出来是远远不够的,因为我们还要对它进行一系列的操作,所以这个生成一个完全一样的实例的过程是非常复杂的,所以这时候我们就想到可不可以直接根据这个实例,然后生成一个一模一样的实例呢?

实际上,这就是原型模式的基本思想,根据实例原型和实例模式来生成新的实例。

介绍完基本思想后,下面我们就通过一个实例来具体理解一下原型实例。

我们实现一段将字符串加上方框中打印出来或者是加入下划线显示出来。

Java中要实现原型模式,也就是实例的复制,我们可以直接利用clone方法,需要实现cloneable接口。

原型模式的实例

我们先定义一个接口,Product接口,这个接口是复制功能的接口,继承了cloneable接口

public interface Product extends Cloneable {
    public abstract void use(String s);
    public abstract Product createClone();
}

createClone是用来复制实例的方法,use方法是用来使用实例方法的。这些方法具体都交给子类去实现。

我们实现一个具体的子类,MessageBox,它的作用是可以给字符创添加类似方框的边界的图案。
这个类实现了product接口,createClone是用于复制自己,生成一个新的一模一样的实例,也就是原型模式的思想。use方法将结果显示出来。

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接口,并且可以复制自己生成一个新的实例。

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;
    }
}

最后我们新建一个管理类Manager来管理这些对象的创建,复制和使用。
Manager类中有一个map字段,用来存储product对象。create方法根据对象的名字在map找到对象的实例,然后调用实例的createClone方法,从而复制出一个实例。

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();
    }
}

我们来测试一下:

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.");
    }
}

运行结果:

image.png

上述代码一个简易的类图:

image.png

小结

下面我们来总结Prototype原型模式

首先,我们给出Prototype原型模式的类图

image.png

通常会有一个原型接口或者类,定义clone方法,就像此例中的Product接口,然后会有具体的原型实例去实现或者继承自这个接口,就像这个例子中的UnderlinePen类和MessageBox

学到这里我们基本把原型模式讲完了。

为什么需要使用原型模式

但读者可能还能会有疑问,我们直接通过类new出一个实例不就可以了,为什么要搞这么复杂?
new Sth();
在本文的开头,我们就举了需要用到原型模式的例子,现在我们结合实例程序再来谈一下:

  • 对象总类繁多,无法将他们整合到一个类中
    例如例子中的有用‘~’作为下划线,有的用‘*’或者‘/’。
    本例比较简单,只生成了三种样式,不过想要做,不论多少种样式都可以实现,但是如果为每个样式实现一个类,就会变得非常复杂。类的数量会非常多。

  • 难以根据类生成实例
    本例中可能感觉不到这一点。大家可以试想一下开发一个用户可以使用鼠标操作的,类似于图形编辑器的应用,假如我们想生成一个和用户通过一系列鼠标点击创建出来的实例,这个时候,显然无法根据类来生成实例,会变的非常复杂,但我们可以采取原型模式,根据实例生成实例,直接复制出一个实例就可以了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容