• 设计模式 — 工厂模式


    女娲造人的故事

    东汉《风俗通》 记录了一则神话故事: “开天辟地,未有人民,女娲捧黄土做人”,讲述的内容就是大家非常熟悉的女娲造人的故事。开天辟地之初。大地上并没有生物,只有苍茫大地,纯粹而洁净的自然环境,寂静而又寂寞,于是女娲决定创造一个新物种(即人类)来增加世界的繁荣,怎么制造呢?

    别忘了女娲是神仙,没有办不到的事情,造人的过程是这样的:首先,女娲采集黄土捏成人的形状,然后放到八卦炉中烧制,最后放置到大地上生长,工艺过程是没有错的,但是意外随时都会发生:

    	第一次烧泥人,感觉应该熟了,往大地上一放,哇,没烤熟!于是一个白人诞生了(这也是缺乏经验的最好证明)
    	第二次烧泥人,上一次没烧熟,这次多烧一会儿,放到世间一看,嘿,熟过头了,于是黑人诞生了!
    	第三次烤泥人,一边烧制一边察看,直到表皮微黄,嘿,真正好,于是黄色人种出现了!
    
    • 1
    • 2
    • 3

    实例 一

    这个造人过程是比较有意思的,是不是可以通过软件开发来实现这个过程呢?古人云:“三人行,必有我师焉”,在面向对象的思维种,万物皆对象,是对象我们就可以通过软件设计来实现。首先对造人过程进行分析,该过程涉及三个对象:女娲、八卦炉、三种不同肤色的人。女娲可以使用场景类Client来表示,八卦炉类似于一个工厂,负责制造生产产品(即人类),三种不同肤色的人,他们都是同一个接口下的不同实现类,都是人嘛,只是肤色、语言不同,对于八卦炉来说都是它生产出的产品。类图如下:

    在这里插入图片描述

    AbstractHumanFactory是一个抽象类,定义了一个八卦炉具有的整体功能,HumanFactory为实现类,完成具体的任务 — 创建人类;Human接口是人类的总称,其三个实现类分别为三类人种;NvWa类是一个场景类,负责模拟这个场景,执行相关任务。

    我们定义的每个人种都有两个方法:getColor(获得人的皮肤颜色)和talk(交谈),其源代码如下

    人类总称:

    public interface Human {
    
        //每个人种的皮肤都有相应的颜色
        public void getColor();
        //人类会说话
        public void talk();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    接口Human是对人类的总称,每个人种都至少具有两个方法,黑色人种、黄色人种、白色人种的代码分别如下:

    黑色人种:

    public class BlackHuman implements Human{
        @Override
        public void getColor() {
            System.out.println("黑色人种的皮肤颜色是黑色的!");
        }
    
        @Override
        public void talk() {
            System.out.println("黑色人说话,一般人听不懂。");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    黄色人种:

    public class YellowHuman implements Human{
        @Override
        public void getColor() {
            System.out.println("黄色人种的皮肤颜色是黄色的!");
        }
    
        @Override
        public void talk() {
            System.out.println("黄色人说话,一般人说的都是双字节。");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    白色人种:

    public class WhiteHuman implements Human{
        @Override
        public void getColor() {
            System.out.println("白色人种的皮肤颜色是白色的!");
        }
    
        @Override
        public void talk() {
            System.out.println("白色人说话,一般人说的都是单字节。");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    所有人种定义完毕,下一步就是定义一个八卦炉,然后烧制人类。我们想象一下,女娲最可能给八卦炉下达什么样的生产命令呢?应该是 “给我生产出一个黄色人种(YellowHuman类)”,而不是 “给我生产一个会走、会跑、会说话、皮肤是黄色的人种”,因为这样的命令增加了交流的成本,作为一个生产的管理者,只要知道生产什么就可以了,而不需要事物的具体信息。通过分析,我们发现八卦炉生产人类的方法输入参数类型应该是Human接口的实现类,这也解释了为什么类图上的AbstractHumanFactory抽象类中createHuman方法的参数为Class类型。其源代码如下:

    抽象人类创建工厂

    public abstract class AbstractHumanFactory {
        public abstract <T extends Human> T createHuman(Class<T> c);
    }
    
    
    • 1
    • 2
    • 3
    • 4

    注意:我们在这里采用了泛型(Generic),通过定义泛型对createHuman的输入参数产生两层限制:

    必须是Class类型;
    必须是Human的实现类。
    
    • 1
    • 2

    其中的 “?” 表示的是,只要实现了Human接口的类都可以作为参数,泛型是JDK 1.5中的一个非常重要的新特性,它减少了对象间的转换,约束其输入参数类型,对Collection集合下的实现类都可以定义泛型

    人类创建工厂:

    public class HumanFactory extends AbstractHumanFactory{
        @Override
        public <T extends Human> T createHuman(Class<T> c) {
            //定义个生产的人种
            Human human = null;
    
            try{
                //产生一个人种
                human = (Human)Class.forName(c.getName()).newInstance();
            }catch (Exception e) {
                System.out.println("人种生产错误!");
            }
    
    
            return (T) human;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    人种有了,八卦炉也有了,剩下的工作就是女娲采集黄土,然后命令八卦炉开始生产,其过程代码如下:
    女娲类Client:

    public class NvWa {
    
        public static void main(String[] args) {
    
            //声明阴阳八卦炉
            AbstractHumanFactory YinYangLu = new HumanFactory();
    
            //女娲第一次造人,火候不足,于是白人产生了
            System.out.println("--造出的第一批人是白色人种--");
            Human whiteHuman  = YinYangLu.createHuman(WhiteHuman.class);
            whiteHuman.getColor();
            whiteHuman.talk();
    
            //女娲第二次造人,火候过猛,于是黑人产生了
            System.out.println("--造出的第二批人是黑色人--");
    
            Human blackHuman  = YinYangLu.createHuman(BlackHuman.class);
            blackHuman.getColor();
            blackHuman.talk();
    
            //女娲第三次造人,火候刚刚好,于是黄色人种产生了
            System.out.println("--造出的第二批人是黄色人--");
    
            Human yellowHuman  = YinYangLu.createHuman(YellowHuman.class);
            yellowHuman.getColor();
            yellowHuman.talk();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    人种有了,八卦炉有了,负责生产的女娲也有了,激动人心的时刻到来了,我们运行一下,结果如下所示:

    	--造出的第一批人是白色人种--
    	白色人种的皮肤颜色是白色的!
    	白色人说话,一般人说的都是单字节。
    	--造出的第二批人是黑色人--
    	黑色人种的皮肤颜色是黑色的!
    	黑色人说话,一般人听不懂。
    	--造出的第二批人是黄色人--
    	黄色人种的皮肤颜色是黄色的!
    	黄色人说话,一般人说的都是双字节。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    哇,人类的生产过程就展现出来了!这个世界就热闹起来了,黑人、白人、黄人都可以运动了,这也正是我们现在的真实世界。以上就是工厂方法模式

    实例 二

    工厂方法模式使用的频率非常高,在我们日常的开发中总能见到它的身影,其定义为:

    定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。
    
    • 1

    在工厂方法模式中,抽象产品类Product负责定义产品的共性,实现对事物最抽象的定义;Creator为抽象创建类,也就是抽象工厂,具体如何创建产品类是由具体的实现工厂ConreateCreator完成的。工厂方法模式的变种较多,我们来看一个比较实用的通用源码。

    抽象产品类代码如下:

    工厂方法模式通过类图
    如上工厂方法模式通过类图

    抽象产品类

    public abstract class Product {
        //产品类的公共方法
        public void method1() {
            //业务逻辑处理
            System.out.println("产品类的公共方法。。。。");
        }
    
        //抽象方法
        public abstract void method2();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    具体的产品类可以有多个,都继承于抽象产品类,其源代码如下:

    public class ConCreateProduct1 extends Product{
        @Override
        public void method2() {
            //业务逻辑处理
            System.out.println("业务逻辑一");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    抽象工厂负责定义产品对象的产生,源代码如下:

    public abstract class Creator {
    
        /**
         * 创建一个产品对象,其被输入参数类型可以自行设置
         * 通过为String、Enum、Class等,当然也可以为空
         * @param c
         * @return
         * @param 
         */
        public abstract <T extends Product> T createProduct(Class<T> c);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    具体如何产生一个产品的对象,是由具体的工厂类实现的,代码如下:

    public class ConCreateCreator extends Creator{
        @Override
        public <T extends Product> T createProduct(Class<T> c) {
            Product product = null;
    
            try {
                product = (Product) Class.forName(c.getName()).newInstance();
            }catch (Exception e) {
                //异常处理
                e.printStackTrace();
            }
            return (T)product;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    场景类的调用方法如下:

    public class Client {
    
        public static void main(String[] args) {
            Creator creator = new ConCreateCreator();
    
            Product product = creator.createProduct(ConCreateProduct1.class);
            product.method1();
    
            /**
             * 继续业务处理
             */
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    该通用代码是一个比较实用、易扩展的框架,可以根据实际项目需要进行扩展。

    工厂方法模式的应用

    工厂方法模式的优点

    首先,良好的封装性,代码结构清晰。一个对象创建时有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的艰辛过程,降低模块间的耦合。

    其次,**工厂方法模式的扩展性非常优秀。在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以完成“拥抱变化”。**例如在我们的例子中,增加一个棕色人种,则只需要增加一个BrownHuman类,工厂类不用任何修改就可完成系统扩展。

    再次,屏蔽产品类。这一特点非常重要,产品类的实现如何变化,调用者都不需要关系,它只需要关系产品的接口,只要接口保持不变,系统中的上层模块就不要发生变化。因为产品类的实例化工作是由工厂类负责的,一个产品对象具体由哪一个产品生成是由工厂类决定的。在数据库开发中,大家应该能深刻体会到工厂方法模式的好处:如果使用JDBC连接数据库,数据库从MYSQL切换到Oracle,需要改动的地方就是切换一下驱动名称(前提条件是SQL语句是标准语句),其他的都不需要修改,这是工厂方法模式灵活性的一个直接案例

    最后,工厂方法模式是典型的解耦框架。高层模块值需要知道产品的抽象类,其他的实现类都不用关系,符合迪米特法则,我不需要的就不要去交流;也符合依赖倒置原则,只依赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类,没问题!

    工厂方法模式的使用场景

    首先,工厂方法模式是new一个对象的替代品,所以在所有需要生成对象的地方都可以使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度。

    其次,需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式。万物皆对象,那万物也就皆产品类。例如需要设计一个连接邮件服务器的框架,有三种网络协议可供选择:POP3、IMAP、HTTP,我们就可以把这三种连接方法作为产品类,定义一个接口为IConnectMail,然后定义对邮件的操作方法,用不同的方法实现三个具体的产品类(也就是连接方式)再定义一个工厂方法,按照不同的传入条件,选择不同的连接方法。如此设计,可以做到完成的扩展,如某些邮件服务器提供了WebService接口,很好,我们只要增加一个产品类就可以了。

    再次,工厂方法模式可以用在异构项目中,例如通过WebService与一个非Java的项目交互,虽然WebService号称是可以做到异构系统的同构化,但是在实际的开发中,还是会碰到很多问题,如类型问题、WSDL文件的支持问题,等等。从WSDL中产生的对象都认为是一个产品,然后由一个具体的工厂类进行管理,减少与外围系统的耦合。

    工厂方法模式的扩展

    工厂方法模式有很多扩展,而且与其他模式结合使用威力更大,下面介绍4种扩展

    1. 缩小为简单工厂模式
    我们这样考虑一个问题:一个模块仅需要一个工厂类,没有必要把它产生出来,使用静态的方法就可以了,根据这一要求,把上例中的AbstarctHumanFactory修改一下,类图如下所示:
    
    • 1

    在这里插入图片描述
    我们在类图中去掉了AbstractHumanFactory抽象类,同时把createHuman方法设置为静态类型,简化了类的创建过程,变更代码仅仅是HumanFactory和NvWa类,HumanFactory如下代码

    public class HumanFactory1{
    
    
        public static  <T extends Human> T createHuman(Class<T> c) {
            //定义个生产的人种
            Human human = null;
    
            try{
                //产生一个人种
                human = (Human)Class.forName(c.getName()).newInstance();
            }catch (Exception e) {
                System.out.println("人种生产错误!");
            }
    
    
            return (T) human;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    HumanFactory1类仅有两个地方发生变化:去掉继承抽象类,并在createHuman前增加static关键字;工厂类发生变化,也同时引起了调用者NvWa的变化,代码如下:

    简单工厂模式中的场景类:

    public class NvWa1 {
    
        public static void main(String[] args) {
    
    
    
            //女娲第一次造人,火候不足,于是白人产生了
            System.out.println("--造出的第一批人是白色人种--");
            Human whiteHuman  = HumanFactory1.createHuman(WhiteHuman.class);
            whiteHuman.getColor();
            whiteHuman.talk();
    
            //女娲第二次造人,火候过猛,于是黑人产生了
            System.out.println("--造出的第二批人是黑色人--");
    
            Human blackHuman  = HumanFactory1.createHuman(BlackHuman.class);
            blackHuman.getColor();
            blackHuman.talk();
    
            //女娲第三次造人,火候刚刚好,于是黄色人种产生了
            System.out.println("--造出的第二批人是黄色人--");
    
            Human yellowHuman  = HumanFactory1.createHuman(YellowHuman.class);
            yellowHuman.getColor();
            yellowHuman.talk();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    运行结果没有发生变化,但是我们的类图变简单了,而且调用者也比较简单,该模式是工厂方法模式的弱化,因为简单,所以称为简单工厂模式 (Simple Factory Pattern) ,也叫做静态工厂模式。在实际项目中,采用该方法的案例还是比较多的,其缺点是工厂类的扩展比较困难,不符合开闭原则,但它仍然是一个非常实用的设计模式。

    2. 升级为多个工厂类

    当我们在做一个比较复杂的项目时,经常会遇到初始化一个对象比较耗费精力的情况,所有产品类都放到一个工厂方法中进行初始化会使代码结构不清晰。例如,一个产品类有5个具体实体,每个实现类的初始化 (不仅仅是new,初始化包括new一个对象,并对对象设置一定的初始值) 方法都不相同,如果写在一个工厂方法中,势必会导致该方法巨大无比,那该怎么办?

    考虑到需要结构清晰,我们就为每个产品定义一个创造者,然后由调用者自己去选择与哪个工厂方法关联。我们还是以女娲造人为例,每个人种都有一个固定的八卦炉,分别造出黑色人种、白色人种、黄色人种,修改后的类图如下所示:

    在这里插入图片描述

    每个人种(具体的产品类)都对应了一个创建者,每个创建者都独立负责创建对应的产品对象,非常符合单一职责原则,按照这种模式我们来看看代码变化。

    实现类如下:

    public class BlackHuman implements Human {
        @Override
        public void getColor() {
            System.out.println("黑色人种的皮肤颜色是黑色的!");
        }
    
        @Override
        public void talk() {
            System.out.println("黑色人说话,一般人听不懂。");
        }
    }
    
    public class WhiteHuman implements Human {
        @Override
        public void getColor() {
            System.out.println("白色人种的皮肤颜色是白色的!");
        }
    
        @Override
        public void talk() {
            System.out.println("白色人说话,一般人说的都是单字节。");
        }
    }
    
    public class YellowHuman implements Human {
        @Override
        public void getColor() {
            System.out.println("黄色人种的皮肤颜色是黄色的!");
        }
    
        @Override
        public void talk() {
            System.out.println("黄色人说话,一般人说的都是双字节。");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    多工厂模式的抽象工厂类代码如下:

    public abstract class AbstractHumanFactory {
        public abstract Human createHuman();
    }
    
    • 1
    • 2
    • 3

    注意:抽象方法中已经不再需要传递相关参数了,因为每一个具体的工厂都已经非常明确自己的职责:创建自己负责的产品类对象。

    黑色人种的创建工厂代码如下:

    public class BlackHumanFactory extends AbstractHumanFactory{
        @Override
        public Human createHuman() {
            return new BlackHuman();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    黄色人种的创建工厂代码如下:

    public class YellowHumanFactory extends AbstractHumanFactory{
        @Override
        public Human createHuman() {
            return new YellowHuman();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    白色人种的创建工厂代码如下:

    白色人种的创建类

    public class WhiteHumanFactory extends AbstractHumanFactory{
        @Override
        public Human createHuman() {
            return new WhiteHuman();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    三个具体的创建工厂都非常简单,但是,如果一个系统比较复杂时工厂类也会相应地变复杂,场景类NvWa修改后的代码如下:

    public class NvWa {
    
        public static void main(String[] args) {
    
            //女娲第一次造人,火候不足,于是白人产生了
            System.out.println("--造出的第一批人是白色人种--");
            Human whiteHuman  = (new WhiteHumanFactory()).createHuman();
            whiteHuman.getColor();
            whiteHuman.talk();
    
            //女娲第二次造人,火候过猛,于是黑人产生了
            System.out.println("--造出的第二批人是黑色人--");
    
            Human blackHuman  = (new BlackHumanFactory()).createHuman();
            blackHuman.getColor();
            blackHuman.talk();
    
            //女娲第三次造人,火候刚刚好,于是黄色人种产生了
            System.out.println("--造出的第二批人是黄色人--");
    
            Human yellowHuman  = (new YellowHumanFactory()).createHuman();
            yellowHuman.getColor();
            yellowHuman.talk();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    运行结果:

    --造出的第一批人是白色人种--
    白色人种的皮肤颜色是白色的!
    白色人说话,一般人说的都是单字节。
    --造出的第二批人是黑色人--
    黑色人种的皮肤颜色是黑色的!
    黑色人说话,一般人听不懂。
    --造出的第二批人是黄色人--
    黄色人种的皮肤颜色是黄色的!
    黄色人说话,一般人说的都是双字节。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    运行结果还是相同。我们回顾一下,每一个产品类都对应了一个创建类,好处就是创建类的职责清晰,而且结构简单,但是给可扩展性和可维护性带来了一定的影响。为什么这么说呢?如果要扩展一个产品类,就需要建立一个相应的工厂类,这样就增加了扩展的难度。因为工厂类和产品类的数量相同,维护时需要考虑两个对象之间的关系。

    当然,在复杂的应用中一般采用工厂的方法,然后再增加一个协调类,避免调用者与各个子工厂交流,协调类的作用是封装子工厂类,对高层模块提供统一的访问接口。

    3. 替代单例模式

    单例模式的核心要求就是在内存中只有一个对象,通过工厂方法模式也可以只在内存中生产一个对象
    在这里插入图片描述
    非常简单的类图,Singleton定义了一个private的无参构造函数,目的是不允许通过new的方式创建一个对象,代码如下:

    public class Singleton {
        
        //不允许通过new产生一个对象
        private Singleton() {}
        
        public void doSomething(){
            //业务处理
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Singleton保证不能通过正常的渠道建立一个对象,那SingletonFactory如何建立一个单例对象呢?答案是通过反射方式创建,如代码清单如下:

    负责生成单例的工厂类

    public class SingletonFactory {
    
        private static Singleton singleton;
    
        static {
            try {
                Class aClass = Class.forName(Singleton.class.getName());
    
                //获得无参构造函数
                Constructor constructor = aClass.getDeclaredConstructor();
    
                //设置无参构造是可访问的
                constructor.setAccessible(true);
    
                //产生一个实例
                singleton = (Singleton) constructor.newInstance();
            }catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
        public static Singleton getSingleton() {
            return singleton;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    通过获得类构造器,然后设置访问权限,生成一个对象,然后提供外部访问,保证内存中的对象唯一。当然,其他类也可以通过反射的方式建立一个单例对象,确实如此,但是一个项目或团队是有章程和规范的,何况已经提供了一个获得单例对象的方法,为什么还要重新创建一个新对象呢?除非是有人作恶。

    以上通过工厂方法模式创建了一个单例对象,该框架可以继续扩展,在项目中可以产生一个单例构造器,所有需要产生单例的类都遵循一定的规则(构造方法是private),然后通过扩展该框架,只要输入一个类型就可以获得唯一的一个实例。

    4. 延迟初始化

    何为延迟初始化(Lazy initialization)?一个对象被消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用。延迟初始化是工厂方法模式的一个扩展应用,其通过类图如下:
    在这里插入图片描述
    ProductFactory负责产品类对象的创建工作,并且通过prMap变量产生一个缓存,对需要再次被重用的对象保留。ProductFactory代码如下:

    延迟加载的工厂类

    public class ProductFactory {
        
        private static final Map<String,Product> prMap = new HashMap();
        
        public static synchronized Product createProduct(String type) throws Exception {
            Product product = null;
            
            //如果Map中已经有这个对象
            if(prMap.containsKey(type)) {
                product = prMap.get(type);
            } else {
                if(type.equals("Product1")) {
                    product = new ConCreateProduct1();
                } else {
                    product = new ConCreateProduct2();
                    //同时把对象放到缓存容器中
                    prMap.put(type,product);
                }
            }
            return product;
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    Client场景类如下:

    public class Client {
    
        public static void main(String[] args) throws Exception {
            Creator creator = new ConCreateCreator();
    
            Product product = creator.createProduct(ConCreateProduct1.class);
            product.method1();
    
            /**
             * 继续业务处理
             */
            Product product1 = ProductFactory.createProduct("Product1");
            product1.method2();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    运行结果:

    产品类的公共方法。。。。
    业务逻辑一
    
    • 1
    • 2

    通过定义一个Map容器,容纳所有产生的对象,如果在Map容器中已经有的对象,则直接取出返回;如果没有,则根据需要的类型产生一个对象并放入到Map容器中,以方便下次调用。

    延迟加载框架是可以扩展的,例如限制某一个产品类的最大实例化数量,可以通过判断Map中已有的对象数量来实现,这样的处理是非常有意义的,例如JDBC连接数据库,都会要求设置一个MaxConnection最大连接数量,该数量就是内存中最大实例化的数量。

    延迟加载还可以用在对象初始化比较复杂的情况下,例如硬件访问,涉及多方面的交互,则可以通过延迟加载降低对象的产生和销毁带来的复杂性。

    工厂方法模式在项目中使用得非常频繁,以至于很多代码中都包含工厂方法模式。工厂方法模式还可以与其他模式混合使用(例如模板方法模式、单例模式、原型模式等)。

    设计模式 — 抽象工厂模式

  • 相关阅读:
    通过Web网管切换到命令行界面【华为路由器】
    Redis缓存相关问题
    SMT贴片制造:发挥的作用和价值]
    C语言入门(一)常量、变量
    穿仓与均摊
    My Eighty-fourth Page - 买卖股票的最佳时机 - By Nicolas
    数据结构:AVL树
    化合物应用-动物给药方式
    【Leetcode刷题笔记06】454. 四数相加 II 383. 赎金信 15. 三数之和 18. 四数之和
    redis 分布式锁的实现原理
  • 原文地址:https://blog.csdn.net/weixin_43934626/article/details/127833563