生成实例

一、Singleton 模式-只有一个实例

1. 需求

这里 Singleton 作为学习 javase 的一部分,不再强调实例代码,而是这种模式的思想。
Singleton 单例模式,我们在 spring framework 中可以 通过 scope 指定 一个 Bean 的作用域,就包含了这个思想。
实现的关键的是,将构造器私有化,禁止从 Singleton 类之外调用构造器。

2.Code

2.1 饿汉式【Eager Singleton】 (线程安全,调用效率高,但是不能延时加载)

/**
 * 饿汉式:迫切
 * @author CSZ
 */
public class EagerSingleton {
    /**
     * 私有化静态属性,随着类的加载而加载,所以是饿汉式
     */
    private static final EagerSingleton INSTANCE = new EagerSingleton();

    /**
     * 私有化构造器
     */
    private EagerSingleton(){}

    /**
     * 给外界提供一个获取类实例的方法
     * @return 类的实例
     */
    public static EagerSingleton getInstance(){
        return INSTANCE;
    }
}

2.2 懒汉式【Lazy Singleton】(线程安全,调用效率不高,但是能延时加载)

/**
 * 懒汉式:不着急
 * @author CSZ
 */
public class LazySingleton {

    /**
     * 类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)
     */
    private static LazySingleton instance;

    /**
     * 构造器私有化
     */
    private LazySingleton(){}

    /**
     * 方法使用 synchronized 同步,调用效率低
     * @return
     */
    public static synchronized LazySingleton getInstance() {
        // 这里做了简单判断,如果为空说明首次创建,如果不为空,单例对象,直接返回即可
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

2.3 静态内部类实现模式(线程安全,调用效率高,可以延时加载)

/**
 * 使用 静态内部类
 * @author CSZ
 */
public class LazySingletonPlus {

    private static class SingletonClassInstance{
        private static final LazySingletonPlus instance=new LazySingletonPlus();
    }

    private LazySingletonPlus(){}

    public static LazySingletonPlus getInstance(){
        return SingletonClassInstance.instance;
    }
}

2.4 枚举类实现(线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化调用)

/**
 * @author CSZ
 */
public enum SingletonEnum {

    /**
     * 枚举元素本身就是单例
     */
    INSTANCE;

    /**
     * 添加自己需要的操作
     */
    public void singletonOperation(){
    }
}

简单测试

/**
 * @author CSZ
 */
public class MainTest {
    public static void main(String[] args) {
        System.out.println("start.");
        EagerSingleton instance1 = EagerSingleton.getInstance();
        EagerSingleton instance2 = EagerSingleton.getInstance();
        if (instance1 == instance2) {
            System.out.println("相同实例");
        }else {
            System.out.println("不同实例");
        }
        System.out.println("End.");
    }
}


二、Prototype 模式-通过复制生成实例

1. 场景分析

Prototype 模式,原型模式,以往我们实例的创建是通过调用 类的构造器,并且为属性赋值的方式,但是在开发中,我们也会出现“不指定类名的前提下,生成实例”的需求:

  1. 对象种类繁多,无法整合到一个类中
  2. 难以根据类生成实例,比如一个复杂的图形界面
  3. 想解耦框架与生成的实例时
    书中举了一个栗子:

我们使用实例生成实例的过程类似于,通过复制的方式,复制文档。此时即使我们不了解原来文档的内容,我们也可以使用 复印机复制出相同的文档。

2. 案例分析

image.png

3. UML

image.png

4. Code

4.1 Manager(使用者),负责使用复制实例的方法生成新的实例。

/**
 * @author CSZ
 */
public class Manager {
    // 回顾一下Java的知识点,final 修饰成员变量,变量可以分为两种,一种是基本变量,一种是引用变量
    // 基本变量 要在构造器或者代码块中赋值
    // 引用变量,则是规定了引用地址在运行时不许改变。也就是不会引用新的对象
    private final HashMap<String,Product> showcase = new HashMap<>();

    public void register(String name, Product proto){
        showcase.put(name,proto);
    }
    public Product create(String protoName){
        Product product = showcase.get(protoName);
        return product.createClone();
    }
}

4.2 Product(抽象的原型)接口负责定义复制现有实例来生成新的实例的方法

/**
 * @author CSZ
 */
public interface Product extends Cloneable {
    // public abstract 默认可省略,显示写出方便大家理解
    public abstract void use(String s);
    // 看到下面的方法有没有感觉很妙啊,Product 继承了Cloneable,所以Product 具备了 clone() 的能力。
    // 1)抽象了这个方法,要求所有的实现类必须重写该方法,延迟了实现。
    // 2)返回的类型必须是 Product 类型,所有的实现类,在内部必须创建 Product 对象的"实例"。
    public abstract Product createClone();
}

4.3 MessageBox(具体的原型)针对抽象原型的定义方法做实现。

public class MessageBox implements Product{

    private final char decochar;
    public MessageBox(char decochar){
        this.decochar = decochar;
    }

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

    @Override
    public Product createClone() {
        Product product = null;
        try {
           product = (Product)clone();
        }catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
        return product;
    }
}

UnderlinePen(具体的原型)针对抽象原型的定义方法做实现。


/**
 * @author CSZ
 */
public class UnderlinePen implements Product {

    private final char ulchar;

    public UnderlinePen(char ulchar) {
        this.ulchar = ulchar;
    }

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

    @Override
    public Product createClone() {
        Product product = null;
        try {
            // 跟另一个实现类稍有区别,但是没有写错,编译运行通过的哦
            // 这里想强调的就是,我们的模板就是对象本身。
            product = (Product)this.clone();
        }catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
        return product;
    }
}

4.5 MainTest 测试方法

public class MainTest {
    public static void main(String[] args) {
        // 准备工作
        // 创建原型对象,是不是感觉很熟悉,有点像 spring 中声明注册bean的过程。
        // 进一步的体会,
        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 product1 = manager.create("strong message");
        product1.use("Hello World.");
        Product product2 = manager.create("warning box");
        product2.use("Hello World.");
        Product product3 = manager.create("slash box");
        product3.use("Hello World.");
    }
}

5. 结合图片思考与分析

image.png
  1. 在这个设计模式中,我们说明了 Manager 的作用是负责使用复制实例的方法生成新的实例:
    showcase 字段 是一个 Map 集合,里面保存了名字和实例之间的映射关系。
    register 方法 将接受的一组 <名字,Product接口的实现类对象> 注册到 showcase 中。没有规定成具体的类,这就为我们的解耦提供了可能,所以我们在 framework 包中的类和接口中完全不涉及两个实现类的信息。
  2. 在进一步,在 javase 的学习中,我们最习惯的方式 是 new 一个对象,可是如果是一个复杂的对象,参数需要大量时间准备(比如需要在数据库获取),此时如果有一个类似复印机的方式是不是更简单,也更容易实现。
  3. 最后对于 clone() 做一个简单的说明,clone() 可以得到是一个浅复制,如果想要实现深复制还需要重写
    clone() 方法可以参考 https://blog.csdn.net/qq_41409138/article/details/86762163

三、Builder 模式-组装复杂的实例

1. 需求分析

image.png

2. UML

image.png

3. Code

3.1 Builder 扮演建造者的角色,定义了用于生成实例的方法(接口API)

比如:Builder 就是土木工程里面的图稿设计者

/**
 * @author CSZ
 */
public abstract class Builder {
    public abstract void makeTitle(String title);
    public abstract void makeString(String string);
    public abstract void makeItems(String[] items);
    public abstract void close();
}

3.2 Director 扮演监工的角色,通过has-a 的形式,获取到 Builder 对象

比如:Director 作为工程师,根据图稿设计者选取合适的材料。

/**
 * @author CSZ
 */
public class Director {

    private final Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    public void construct(){
        builder.makeTitle("Greeting");
        builder.makeString("从早上至下午");
        builder.makeItems(new String[]{"早上好","下午好"});
        builder.makeString("晚上");
        builder.makeItems(new String[]{"晚上好","晚安","再见"});
        builder.close();
    }
}

3.3 TextBuilder 具体建造者1

相当于包工头:工程师根据图纸和材料进行现场调度,真正的执行需要通过包工头实现。

/**
 * @author CSZ
 */
public class TextBuilder extends Builder {
    private StringBuffer buffer = new StringBuffer();

    @Override
    public void makeTitle(String title) {
        buffer.append("========================\n");
        buffer.append("[").append(title).append("]\n");
        buffer.append("\n");
    }

    @Override
    public void makeString(String string) {
        buffer.append("■").append(string).append("\n");
        buffer.append("\n");
    }

    @Override
    public void makeItems(String[] items) {
        for (String item : items) {
            buffer.append("  ·").append(item).append("\n");
        }
        buffer.append("\n");
    }

    @Override
    public void close() {
        buffer.append("========================\n");
    }

    public String getResult(){
        return buffer.toString();
    }
}

3.4 HTMLBuilder 具体建造者2

/**
 * @author CSZ
 */
public class HTMLBuilder extends Builder {

    private String fileName;
    private PrintWriter writer;

    @Override
    public void makeTitle(String title) {
        fileName = title + ".html";
        try {
            PrintWriter writer = new PrintWriter(new FileWriter(fileName));
        }catch (IOException e){
            e.printStackTrace();
        }
        writer.println("<html><head><title>" + title + "</title></head><body>");
    }

    @Override
    public void makeString(String string) {
        writer.println("<p>" + string + "</p>" );
    }

    @Override
    public void makeItems(String[] items) {
        writer.println("<ul>");
        for (String item : items) {
            writer.println("<li>" + item + "</li>");
        }
        writer.println("</ul>");
    }

    @Override
    public void close() {
        writer.println("</body></html>");
        writer.close();
    }
    public String getResult(){
        return fileName;
    }
}

3.5 测试类

/**
 * @author CSZ
 */
public class MainTest {

    public static void main(String[] args) {
        if (args.length != 1){
            usage();
            System.exit(0);
        }
        if ("plain".equals(args[0])){
            TextBuilder textBuilder = new TextBuilder();
            Director director = new Director(textBuilder);
            director.construct();
            String result = textBuilder.getResult();
            System.out.println(result);
        }else if ("html".equals(args[0])){
            HTMLBuilder htmlBuilder = new HTMLBuilder();
            Director director = new Director(htmlBuilder);
            director.construct();
            String result = htmlBuilder.getResult();
            System.out.println("文档编写完成");
        }else {
            usage();
            System.exit(0);
        }
    }

    public static void usage(){
        System.out.println("Usage:java Main plain 纯文本");
        System.out.println("Usage:java Main HTML 网页");
    }
}

4. 思考

// ToDo 暂时还没有特别深的印象,只是记得在实习的时候,前辈有个模块使用了该模式。



四、Abstract Factory(抽象工厂)-- 将关联零件组装成产品

1. 场景分析

image.png

2. UML

image.png

3. 最终效果

image.png

4. Code

4.1 包结构

image.png

4.2 Factory 抽象工厂

/**
 * @author CSZ
 */
public abstract class Factory {

    public static Factory getFactory(String className){
        Factory factory = null;
        try {
            factory = (Factory)Class.forName(className).getDeclaredConstructor().newInstance();
        }catch (ClassNotFoundException | NoSuchMethodException ignored){
            System.out.println("没有找到" + className + "类");
        } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return factory;
    }

    public abstract Link createLink(String caption,String url);
    public abstract Tray createTray(String caption);
    public abstract Page createPage(String title ,String author);
}

4.3 整合了其中的零件,每个public 类是一个 .java 文件

/**
 * 抽象Item
 * @author CSZ
 */
public abstract class Item {
    protected String caption;

    public Item(String caption) {
        this.caption = caption;
    }
    public abstract String makeHTML();
}
/**
 * Link 继承自 Item
 * @author CSZ
 */
public abstract class Link extends Item{

    protected String url;

    public Link(String caption,String url){
        super(caption);
        this.url = url;
    }
}

/**
 * Tray 也继承自 Item
 * @author CSZ
 */
public abstract class Tray extends Item{
    protected ArrayList<Item> tray = new ArrayList<>();
    public Tray(String caption) {
        super(caption);
    }
    public void add(Item item){
        tray.add(item);
    }
}
/**
 * 用于整合填充的HTML 页面
 * @author CSZ
 */
public abstract class Page {

    protected String title;
    protected String author;
    protected ArrayList<Item> content = new ArrayList<>();

    public Page(String title,String author){
        this.title = title;
        this.author = author;
    }

    public void add(Item item){
        content.add(item);
    }
    // 定义了最终组装成页面逻辑。
    public abstract String makeHTML();
    // 页面保存逻辑。
    public void output(){
        try {
            String filename = title + ".html";
            FileWriter writer = new FileWriter(filename);
            writer.write(this.makeHTML());
            writer.close();
            System.out.println(filename + "编写完成");
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

4.4 ListFactory 实际工厂

/**
 * @author CSZ
 */
public class ListFactory extends Factory{

    @Override
    public Link createLink(String caption, String url) {
        return new ListLink(caption,url);
    }

    @Override
    public Tray createTray(String caption) {
        return new ListTray(caption);
    }

    @Override
    public Page createPage(String title, String author) {
        return new ListPage(title,author);
    }
}

4.5 整合各种组件 每个 public 类是一个 .java 文件

/**
 * ListLink 调用父类的构造器,并且重写了 makeHTML 方法
 * @author CSZ
 */
public class ListLink extends Link {
    public ListLink(String caption,String url){
        super(caption,url);
    }

    @Override
    public String makeHTML() {
        return "<li<a href=\"" + url + ">" + caption + "</a></li>\n";
    }
}

/**
 * ListTray 继承自Tray,Tray 又继承自 Item
 * @author CSZ
 */
public class ListTray extends Tray {

    public ListTray(String caption) {
        super(caption);
    }

    @Override
    public String makeHTML() {
        StringBuffer buffer = new StringBuffer();
        buffer.append("<li\n");
        buffer.append(caption).append("\n");
        buffer.append("<ul>\n");
        for (Item item : tray) {
            buffer.append(item.makeHTML());
        }
        buffer.append("</ul>\n");
        buffer.append("</li>\n");
        return buffer.toString();
    }
}

/**
 * ListPage 继承自 Page,重写了页面组装逻辑
 * @author CSZ
 */
public class ListPage extends Page{

    public ListPage(String title, String author) {
        super(title, author);
    }

    @Override
    public String makeHTML() {
        StringBuffer buffer = new StringBuffer();
        buffer.append("<html><head><title>").append(title).append("</title></head>\n");
        buffer.append("<body>\n");
        buffer.append("<h1>").append(title).append("</h1>\n");
        buffer.append("<ul>\n");
        for (Item item : content) {
            buffer.append(item.makeHTML());
        }
        buffer.append("<ul>\n");
        buffer.append("<hr><address>").append(author).append("</address>");
        buffer.append("</body></html>\n");
        return buffer.toString();
    }
}

4.6 测试类

/**
 * @author CSZ
 */
public class Main {
    public static void main(String[] args) {
        if (args.length != 1){
            System.out.println("Usage:java Main class.name.of.ConcreteFactory");
            System.out.println("Example 1: java Main listfactory.ListFactory");
            System.out.println("Example 2: java Main tablefactory.TableFactory");
            System.exit(0);
        }
        Factory factory = Factory.getFactory(args[0]);
        Link people = factory.createLink("人民日报", "http://www.people.com.cn/");
        Link gmw = factory.createLink("光明日报", "http://www.gmw.cn/");

        Link us_yahoo = factory.createLink("Yahoo!", "http://www.yahoo.com/");
        Link jp_yahoo = factory.createLink("Yahoo!Japan", "http://www.yahoo.jp/");

        Link excite = factory.createLink("Excite", "http://www.excite.com/");
        Link google = factory.createLink("google", "http://www.google.com/");

        Tray trayNews = factory.createTray("日报");
        trayNews.add(people);
        trayNews.add(gmw);

        Tray trayYahoo = factory.createTray("Yahoo!");
        trayYahoo.add(us_yahoo);
        trayYahoo.add(jp_yahoo);

        Tray traySearch = factory.createTray("搜索引擎");
        traySearch.add(trayYahoo);
        traySearch.add(excite);
        traySearch.add(google);

        Page page = factory.createPage("LinkPage", "chang");
        page.add(trayNews);
        page.add(traySearch);
        page.output();
    }
}

4. 总结与思考

image.png

其实大家看到这个场景也很容易理解这种设计模式的优缺点:
优点:当我们想扩展新的模块,比如想支持华为手机的市场,我们只需要创建一个似于 listfactory 包下的内容就可以。其中的 main 方法的入参变成你期待的 新工厂就好了,抽象的工厂也不需要修改。
缺点:假设有一天,我们手机大变革,之前的步骤不适用了,即 抽象工厂的步骤有问题,比如多了一道工序,旗下的所有工厂都需要进行返工,都添加上这步操作。

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

推荐阅读更多精彩内容