图解Java设计模式学习笔记——设计模式概述、创建型模式(单例模式、工厂模式)

本文深入探讨了设计模式中的单例模式(包括饿汉式、懒汉式及其线程安全实现),并比较了多种实现方式。接着详细讲解了工厂模式(简单工厂、工厂方法、抽象工厂),展示了它们在披萨项目中的应用和优劣。最后总结了工厂模式的原理和在JDK中的体现,以及设计模式在提高代码灵活性和可维护性中的关键作用。
摘要由CSDN通过智能技术生成

一、设计模式概述

1、设计模式介绍

设计模式是程序员在面对同类软件设计问题总结出来的有用经验,模式不是代码而是某类问题的通用解决方案,设计模式(Design pattern)代表了最佳的实践。这些解决方案是众多软件开发人员经过相当长的一段实践的试验和错误总结出来的。

设计模式的本质是提高软件的维护性、通用性、扩展性,并降低软件的复杂度

《设计模式》是经典的书,作者是Erich Gamma、Richard Helm、Ralph Johnson 和John Vlissides Design(俗称“四人组GOF”)。

设计模式并不局限于某种语言,Java、Php、C++都有设计模式。

2、设计模式类型

设计模式分为三种类型,共23种

  • 创建型模式单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式
  • 结构型模式:适配器模式、桥接模式、装饰者模式、组合模式、外观模式、享元模式、代理模式
  • 行为型模式:模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式)、状态模式、策略模式、职责链模式(责任链模式)。

注意:不同的书籍上对分类和名称略有差别

二、单例模式

1、介绍

所谓类的单例模式,就是采取一定的方法保证在整个系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。

比如 Hibernate 的 SessionFactory ,它充当数据存储源的代理,并负责创建Session对象。SessionFactory 并不是轻量级的,一般情况下,一个项目只需要一个 SessionFactory 就足够了,这就会使用到单例模式。

2、单例(饿汉式) - 静态常量

步骤如下:

  1. 构造器私有化
  2. 类的内部创建对象
  3. 向外暴露一个静态的公共方法。getInstance

代码实现 

public class SingletonTest {
    public static void main(String[] args) {
        //测试
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2); // true
        System.out.println("instance.hashCode=" + instance.hashCode());
        System.out.println("instance2.hashCode=" + instance2.hashCode());
    }
}

class Singleton {
    // 本类内部创建实例
    private final static Singleton instance = new Singleton();

    // 构造函数私有化
    private Singleton() {
    }

    //提供一个公有的静态方法,返回实例对象
    public static Singleton getInstance(){
        return instance;
    }
}
  • 优点:这种写法比较 简单,就是在类装载的时候就完成实例化。避免了线程同步问题
  • 缺点:在类装载的时候就完成实例化,没有达到 Lazy Loading 的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
  • 这种方式给予 classloader 机制避免了多线程的同步问题,instance 在类装载时就实例化。在单例模式中大多数都是调研 getInstance 方法,但是导致类装载的原因有很多种,因此不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 就没有达到 lazy loading 的效果。
  • 结论:这种单例模式可用,但是可能会造成内存浪费

3、单例(饿汉式) - 静态代码块

public class SingletonTest {
    public static void main(String[] args) {
        //测试
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2); // true
        System.out.println("instance.hashCode=" + instance.hashCode());
        System.out.println("instance2.hashCode=" + instance2.hashCode());
    }
}

class Singleton {
    // 本类内部创建实例
    private static Singleton instance;
    //在静态代码块中,创建单例对象
    static {
        instance = new Singleton();
    }

    // 构造函数私有化
    private Singleton() {
    }

    //提供一个公有的静态方法,返回实例对象
    public static Singleton getInstance(){
        return instance;
    }
}

 这种方式和上面的方式类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面一样。

结论:这种单例模式可用,但是可能会造成内存浪费

4、单例(懒汉式) - 线程不安全

public class SingletonTest {
    public static void main(String[] args) {
        //测试
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2); // true
        System.out.println("instance.hashCode=" + instance.hashCode());
        System.out.println("instance2.hashCode=" + instance2.hashCode());
    }
}

class Singleton {
    private static Singleton instance;

    // 构造函数私有化
    private Singleton() {
    }

    //提供一个静态的公有方法,当使用到该方法时,才去创建instance
    public static Singleton getInstance(){
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

起到了 Lazy Loading 的效果,但是只能在单线程下使用。

如果在多线程下,一个线程进入了 if(instance == null)  判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。

结论:在实际开发中不要使用这种方式

5、单例(懒汉式) - 方法同步线程安全

public class SingletonTest {
    public static void main(String[] args) {
        //测试
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2); // true
        System.out.println("instance.hashCode=" + instance.hashCode());
        System.out.println("instance2.hashCode=" + instance2.hashCode());
    }
}

class Singleton {
    private static Singleton instance;

    // 构造函数私有化
    private Singleton() {
    }

    //提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
    public synchronized static Singleton getInstance(){
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 解决了线程安全问题。
  • 效率太低,每个线程在想获得类的实例的时候,执行 getInstance() 方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想要获得该类的实例,直接 return 就行了。方法进行同步效率太低。
  • 结论:在实际开发中,不推荐使用这种方式。 

6、单例(懒汉式) - 同步代码块

public class SingletonTest {
    public static void main(String[] args) {
        //测试
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2); // true
        System.out.println("instance.hashCode=" + instance.hashCode());
        System.out.println("instance2.hashCode=" + instance2.hashCode());
    }
}

class Singleton {
    private static Singleton instance;

    // 构造函数私有化
    private Singleton() {
    }

    public static Singleton getInstance(){
        if(instance == null) {
            synchronized (Singleton.class) {
                instance = new Singleton();
            }
        }
        return instance;
    }
}

这种方式,本意是想对上面方法同步方式的改进,因为前面同步方法效率太低,改为同步产生实例化的代码块。

但是这种同步并不能起到线程同步的作用。假如一个线程进入了 if(instance == null) 判断语句块,还未来得及往下执行,另外一个线程也通过了这个判断语句,这时便会产生多个实例。

结论:在实际的开发中,不能使用这种方式

7、单例(懒汉式) -双重检查

public class SingletonTest {
    public static void main(String[] args) {
        //测试
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2); // true
        System.out.println("instance.hashCode=" + instance.hashCode());
        System.out.println("instance2.hashCode=" + instance2.hashCode());
    }
}

class Singleton {
    private static Singleton instance;

    // 构造函数私有化
    private Singleton() {
    }

    //提供一个静态的公有方法,加入双重检查代码,解决线程安全问题, 同时解决懒加载问题。同时保证了效率, 推荐使用
    public static Singleton getInstance(){
        if(instance == null) {
            synchronized (Singleton.class) { //对象枷锁
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

双重检查(Double Check)概念是多线程开发中经常使用到的,如代码中所示,我们进行了两次 if(singleton == null)检查,这样 就可以保证线程安全了。

这样,实例化代码只用执行一次,后面再次访问时,判断 if(instance == null),直接 return 实例化对象,也避免了反复进行方法同步。

线程安全、延迟加载、效率较高。

结论:在实际开发中,推荐使用这种单例设计模式

8、单例(懒汉式) - 静态内部类

public class SingletonTest {
    public static void main(String[] args) {
        //测试
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2); // true
        System.out.println("instance.hashCode=" + instance.hashCode());
        System.out.println("instance2.hashCode=" + instance2.hashCode());
    }
}

class Singleton {
    private static Singleton instance;

    // 构造函数私有化
    private Singleton() {
    }

    //写一个静态内部类,该类中有一个静态属性Singleton
    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }

    //提供一个静态的公有方法,直接返回SingletonInstance.INSTANCE
    public static Singleton getInstance(){
        return SingletonInstance.INSTANCE;
    }
}

这种方式采用了类加载的机制来保证初始化实例时只有一个线程。

静态内部类方式在 Singleton 类被加载时并不会立即实例化,而是在需要实例化时,调用 getInstance 方法,才会装载 SingletonInstance 类,从而完成 Singleton 的实例化。

类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM 帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。

优点:避免了线程不安全,利用静态内部类特点实现延迟加载、效率高。

结论:推荐使用。 

9、单例(懒汉式) - 枚举方式

public class SingletonTest {
    public static void main(String[] args) {
        //测试
        MyResource instance = Singleton.INSTANCE.getInstance();
        MyResource instance2 = Singleton.INSTANCE.getInstance();
        System.out.println(instance == instance2); // true
        System.out.println("instance.hashCode=" + instance.hashCode());
        System.out.println("instance2.hashCode=" + instance2.hashCode());
    }
}

//使用枚举,可以实现单例, 推荐
enum Singleton {
    INSTANCE;
    //MyResource是我们要应用单例模式的资源,比如网络连接、数据库、线程池等等。
    private MyResource instance;

    private Singleton() {
        instance = new MyResource();
    }
    public MyResource getInstance() {
        return instance;
    }
}

class MyResource {}

这借助JDK1.5 中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。

这种方式是 Effective Java 作者 Josh Bloch 提倡的方式。

结论:推荐使用

10、单例模式 JDK 源码分析

在 JDK 中,java.lang.Runtiom 就是经典的单例模式(饿汉式)。

代码如下图

11、单例模式注意事项

  • 单例模式保证了系统内存中只存在一个对象,节省了资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
  • 当想实例化一个单例类时,必须要记住使用相应的获取对象的方法,而不是使用 new。

 单例模式使用的场景:

  • 需要频繁创建销毁的对象
  • 创建对象耗时过多或耗费资源过多(重量级对象),但有经常用到的对象
  • 工具类对象
  • 频繁访问数据库或文件的对象(比如数据源、session 工厂等)

三、工厂模式

1、简单工厂模式

一个具体需求

看一个披萨项目:要便于披萨种类的扩展,要便于维护:

  • 披萨的种类很多(比如 GreekPizza、CheesePizza等)
  • 披萨的制作有:prepare、bake、cut、box
  • 完成披萨店的订购功能

使用传统的方式来完成

类图

各种 Pizza

public abstract class Pizza {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    /**
     * 准备原材料,不同的披萨的原材料不一样,所以做成抽象方法
     */
    public abstract void prepare();

    public void bake() {
        System.out.println(name + "烘焙");
    }

    public void cut() {
        System.out.println(name + "切割");
    }

    public void box() {
        System.out.println(name + "包装");
    }
}

public class CheesePizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("给制作奶酪披萨准备原材料");
    }
}

public class GreekPizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("给制作希腊披萨准备原材料");
    }
}

编写 OrderPizza去订购各种

public class OrderPizza {
    public OrderPizza() {
        Pizza pizza = null;
        String orderType; //订购披萨的类型
        do {
            orderType = getType();
            if ("greek".equals(orderType)) {
                pizza = new GreekPizza();
                pizza.setName("希腊披萨");
            } else if ("cheese".equals(orderType)) {
                pizza = new CheesePizza();
                pizza.setName("奶酪披萨");
            } else {
                break;
            }
            //输出 pizza 制作过程
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        } while (true);

        System.out.println("退出订购---");
    }

    /**
     * 写一个方法,可以获取客户端希望订购的披萨类型
     */
    private String getType() {
        BufferedReader strIn = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请输入披萨类型:");
        try {
            String str = strIn.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}

编写客户端,即披萨店

public class PizzaStore {
    public static void main(String[] args) {
        new OrderPizza();
    }
}
  • 传统方式优点是比较好理解,简单易操作。
  • 缺点是违反了设计模式的ocp原则,即对扩展开放,对修改关闭。当我们给类增加新功能的时候,尽量不要修改代码,或者尽可能少修改代码。
  • 比如这时如果要增加一个 Pizza 的种类(PepperPizza),订购披萨则需要做如下修改

分析:修改代码可以接受,但是如果我们再其他的地方也有创建 Pizza的代码,就意味着,也需要修改,而创建Pizza的代码,往往有多处

思路:把创建Pizza对象封装到一个类中,这样就我们有新的Pizza种类时,只需要修改该类就可,其他的创建Pizza对象的代码就不需要了->简单工厂模式。

使用简单工厂模式

简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建哪一种产品类的实例。简单工厂模式是家族中最简单实用的模式。

简单工厂模式定义了一个创建对象的类,由这个类来封装实例化对象的行为。

在软件开发中,当我们会大量的创建某种、某类或者某批对象时,就会使用到工厂模式。

工厂类代码

/**
 * 简单工厂类
 */
public class SimpleFactory {
    /**
     * 根据 orderType 返回对应的 Pizza
     */
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        System.out.println("使用简单工厂模式");
        if ("greek".equals(orderType)) {
            pizza = new GreekPizza();
            pizza.setName("希腊披萨");
        } else if ("cheese".equals(orderType)) {
            pizza = new CheesePizza();
            pizza.setName("奶酪披萨");
        } else if ("pepper".equals(orderType)) {
            pizza = new PepperPizza();
            pizza.setName("胡椒披萨");
        }
        return pizza;
    }
}

订购蛋糕代码

public class OrderPizza {
    //定义一个简单工厂对象
    private SimpleFactory simpleFactory;

    public OrderPizza(SimpleFactory simpleFactory) {
        setFactory(simpleFactory);
    }

    public void setFactory(SimpleFactory simpleFactory) {
        this.simpleFactory = simpleFactory;
        Pizza pizza = null;
        String orderType; //订购披萨的类型
        do {
            orderType = getType();
            pizza = this.simpleFactory.createPizza(orderType);
            if (pizza == null) {
                System.out.println(" 订购披萨失败,退出 ");
                break;
            }
            //输出 pizza 制作过程
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        } while (true);
    }

    /**
     * 写一个方法,可以获取客户端希望订购的披萨类型
     */
    private String getType() {
        BufferedReader strIn = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请输入披萨类型:");
        try {
            String str = strIn.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}

后续如果新增Pizza类,订购类就不需要进行修改了。 

使用简单工厂模式补充-静态工厂

/**
 * 简单工厂类
 */
public class SimpleFactory {
    /**
     * 静态方法创建对象
     * 根据 orderType 返回对应的 Pizza
     */
    public static Pizza createPizza(String orderType) {
        Pizza pizza = null;
        System.out.println("使用简单工厂模式");
        if ("greek".equals(orderType)) {
            pizza = new GreekPizza();
            pizza.setName("希腊披萨");
        } else if ("cheese".equals(orderType)) {
            pizza = new CheesePizza();
            pizza.setName("奶酪披萨");
        } else if ("pepper".equals(orderType)) {
            pizza = new PepperPizza();
            pizza.setName("胡椒披萨");
        }
        return pizza;
    }
}
public class OrderPizza {
    public OrderPizza() {
        Pizza pizza = null;
        String orderType; //订购披萨的类型
        do {
            orderType = getType();
            pizza = SimpleFactory.createPizza(orderType);
            if (pizza == null) {
                System.out.println(" 订购披萨失败,退出 ");
                break;
            }
            //输出 pizza 制作过程
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        } while (true);
    }

    /**
     * 写一个方法,可以获取客户端希望订购的披萨类型
     */
    private String getType() {
        BufferedReader strIn = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请输入披萨类型:");
        try {
            String str = strIn.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}
public class PizzaStore {
    public static void main(String[] args) {
        new OrderPizza();
    }
}

2、工厂方法模式

新需求

披萨项目的新需求:客户在点披萨时,可以点不同口味的披萨,比如北京的奶酪pizza、北京的胡椒pizza或者是伦敦的奶酪pizza、伦敦的胡椒pizza。

思路1:使用简单工厂模式,创建不同的简单工厂,比如 BJPizzaSimpleFactory、LDPizzaSimpleFactory 等等。从当前这个案例来说,也是可以的,但是考虑到项目的规模,以及软件的可维护性、可扩展性并不是特别好。

思路2:使用工厂方法模式。

工厂方法模式介绍

工厂方法模式设计方案:将披萨项目的实例化功能抽象成抽象方法,在不同的口味点餐子类中实现。

工厂方法模式:定义一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类

类图

代码实现

pizza类

public abstract class Pizza {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    /**
     * 准备原材料,不同的披萨的原材料不一样,所以做成抽象方法
     */
    public abstract void prepare();

    public void bake() {
        System.out.println(name + "烘焙");
    }

    public void cut() {
        System.out.println(name + "切割");
    }

    public void box() {
        System.out.println(name + "包装");
    }
}

public class BJCheesePizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("给制作奶酪披萨准备原材料");
    }
}

public class BJPepperPizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("给制作胡椒披萨准备原材料");
    }
}

public class LDCheesePizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("给制作奶酪披萨准备原材料");
    }
}

public class LDPepperPizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("给制作胡椒披萨准备原材料");
    }
}

 工厂类

/**
 * 工厂方法模式
 */
public abstract class Factory {
    /**
     * 抽象方法创建Pizza
     */
    public abstract Pizza createPizza(String orderType);
}

public class BJFactory extends Factory {
    @Override
    public Pizza createPizza(String orderType) {
            Pizza pizza = null;
            if ("cheese".equals(orderType)) {
                pizza = new BJCheesePizza();
                pizza.setName("北京口味-奶酪披萨");
            } else if ("pepper".equals(orderType)) {
                pizza = new BJPepperPizza();
                pizza.setName("北京口味-胡椒披萨");
            }
            return pizza;
    }
}

public class LDFactory extends Factory {
    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if ("cheese".equals(orderType)) {
            pizza = new BJCheesePizza();
            pizza.setName("伦敦口味-奶酪披萨");
        } else if ("pepper".equals(orderType)) {
            pizza = new BJPepperPizza();
            pizza.setName("伦敦口味-胡椒披萨");
        }
        return pizza;
    }
}

 订购披萨

public class OrderPizza {
    private Factory factory;
    public OrderPizza(Factory factory) {
        setFactory(factory);
    }

    public void setFactory(Factory factory) {
        this.factory = factory;
        Pizza pizza = null;
        String orderType; //订购披萨的类型
        do {
            orderType = getType();
            pizza = this.factory.createPizza(orderType);
            if (pizza == null) {
                System.out.println(" 订购披萨失败,退出 ");
                break;
            }
            //输出 pizza 制作过程
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        } while (true);
    }

    /**
     * 写一个方法,可以获取客户端希望订购的披萨类型
     */
    private String getType() {
        BufferedReader strIn = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请输入披萨类型:");
        try {
            String str = strIn.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}

披萨店

public class PizzaStore {
    public static void main(String[] args) {
        //只要设置不同口味的工厂,就可以得到不同口味的pizza
//        new OrderPizza(new BJFactory());
        new OrderPizza(new LDFactory());
    }
}

3、抽象工厂模式

基本介绍

  • 抽象工厂模式:定义了一个 interface 用于创建相关或有依赖关系的对象簇,而无需知名具体的类。
  • 抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。
  • 从设计层面来看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步抽象)。
  • 将工厂抽象程两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据对象类型使用对应的工厂子类。这样将单个简单的工厂变成了工厂簇,利于代码的维护和扩展。

类图

代码实现

披萨类、OrderPizza、PizzaStore 都与 《工厂方法模式》章节一致,这里主要贴下工厂的类

public interface AbsFactory {
    Pizza createPizza(String orderType);
}

public class BJFactory implements AbsFactory {
    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if ("cheese".equals(orderType)) {
            pizza = new BJCheesePizza();
            pizza.setName("北京口味-奶酪披萨");
        } else if ("pepper".equals(orderType)) {
            pizza = new BJPepperPizza();
            pizza.setName("北京口味-胡椒披萨");
        }
        return pizza;
    }
}

public class LDFactory implements AbsFactory {
    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if ("cheese".equals(orderType)) {
            pizza = new BJCheesePizza();
            pizza.setName("伦敦口味-奶酪披萨");
        } else if ("pepper".equals(orderType)) {
            pizza = new BJPepperPizza();
            pizza.setName("伦敦口味-胡椒披萨");
        }
        return pizza;
    }
}

4、工厂模式在 JDK-Calendar 应用的源码分析

JDK 中的 Calendar 类中,就使用了简单工厂模式。

如下代码

public class JDKFactoryDemo {
    public static void main(String[] args) {
        // getInstance 是 Calendar 静态方法
        Calendar cal = Calendar.getInstance();//Calendar类的源码
        // 注意月份下标从0开始,所以取月份要+1
        System.out.println("年:" + cal.get(Calendar.YEAR));
        System.out.println("月:" + (cal.get(Calendar.MONTH) + 1));
        System.out.println("日:" + cal.get(Calendar.DAY_OF_MONTH));
        System.out.println("时:" + cal.get(Calendar.HOUR_OF_DAY));
        System.out.println("分:" + cal.get(Calendar.MINUTE));
        System.out.println("秒:" + cal.get(Calendar.SECOND));
    }
}

我们可以单步调试,跟踪到  【Calendar.getInstance()】方法内部

    public static Calendar getInstance()
    {
        return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
    }

继续进入【createCalendar()】方法,发现当 provider 为null 时,就会走到建档工厂模式的代码。和我们的披萨订购类似,会根据不同的条件创建不同种类的对象。

private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale)
    {
        CalendarProvider provider =
            LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                 .getCalendarProvider();
        if (provider != null) {
            try {
                return provider.getInstance(zone, aLocale);
            } catch (IllegalArgumentException iae) {
                // fall back to the default instantiation
            }
        }

        Calendar cal = null;

        if (aLocale.hasExtensions()) {
            String caltype = aLocale.getUnicodeLocaleType("ca");
            if (caltype != null) {
                switch (caltype) {
                case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                    break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                    break;
                case "gregory":
                    cal = new GregorianCalendar(zone, aLocale);
                    break;
                }
            }
        }
        if (cal == null) {
            // If no known calendar type is explicitly specified,
            // perform the traditional way to create a Calendar:
            // create a BuddhistCalendar for th_TH locale,
            // a JapaneseImperialCalendar for ja_JP_JP locale, or
            // a GregorianCalendar for any other locales.
            // NOTE: The language, country and variant strings are interned.
            if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                cal = new BuddhistCalendar(zone, aLocale);
            } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                       && aLocale.getCountry() == "JP") {
                cal = new JapaneseImperialCalendar(zone, aLocale);
            } else {
                cal = new GregorianCalendar(zone, aLocale);
            }
        }
        return cal;
    }

5、总结

1、工厂模式的意义

将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展性和维护性。

2、三种工厂模式:简单工厂模式、工厂方法模式、抽象方法模式。

3、设计模式的依赖抽象原则如下:

  • 创建对象实例时,不要直接 new 对象,而是把这个 new 类的动作放在一个工厂方法中,并返回。有的书上说,变量不要直接持有具体类的引用。
  • 不要让类继承具体类,而是继承抽象类或这是实现接口。
  • 不要覆盖类中已实现的方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值