设计模式 之 工厂模式

项目源码:https://gitee.com/Jacob-gitee/DesignMode

个人博客:https://jacob.org.cn

女娲造人的故事

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

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

第一次烤泥人,感觉应该熟了,往大地上一放,哇,没烤熟!于是一个白人诞生了!(这也是缺乏经验的最好证明。)第二次烤泥人,上一次没烤熟,这次多烤一会儿,放到世间一看,嘿,熟过头了,于是黑人诞生了!

第三次烤泥人,一边烧制一边察看,直到表皮微黄,嘿,刚刚好,于是黄色人种出现了!

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

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

我们定义的每个人种都有两个方法:getColor(获得人的皮肤颜色)和talk(交谈),其 源代码如代码清单8-1所示。

image-20200817170437395

/*** @program: DesignMode* @description: 人类总称	* @author: Jacob* @create: 2020-08-17 15:14**/
public interface Human {//每个人种的皮肤都有相应的颜色public void getColor();//人类会说话public void talk();}

接口Human是对人类的总称,每个人种都至少具有两个方法。

/*** @program: DesignMode* @description: 黑色人种* @author: Jacob* @create: 2020-08-17 15:15**/
public class BlackHuman implements Human {@Overridepublic void getColor() {System.out.println("黑色人种的皮肤颜色是黑色的!");}@Overridepublic void talk() {System.out.println("黑人会说话,一般人听不懂。");}
}
/*** @program: DesignMode* @description: 黄色人种* @author: Jacob* @create: 2020-08-17 15:16**/
public class YellowHuman implements Human {@Overridepublic void getColor() {System.out.println("黄色人种的皮肤颜色是黄色的!");}@Overridepublic void talk() {System.out.println("黄色人种会说话,一般说的都是双字节。");}
}
/*** @program: DesignMode* @description: 白色人种* @author: Jacob* @create: 2020-08-17 15:17**/
public class WhiteHuman implements Human {@Overridepublic void getColor() {System.out.println("白色人种的皮肤颜色是白色的!");}@Overridepublic void talk() {System.out.println("白色人种会说话,一般说的都是单字节。");}}

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

/*** @program: DesignMode* @description:  抽象人类创建工厂* @author: Jacob* @create: 2020-08-17 15:18**/
public abstract class AbstractHumanFactory {public abstract <T extends Human> T createHuman(Class<T> c);}

注意,我们在这里采用了泛型(Generic),通过定义泛型对createHuman的输入参数产生两层限制:
● 必须是Class类型;
● 必须是Human的实现类。

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

目前女娲只有一个八卦炉,其实现生产人类的方法。

/*** @program: DesignMode* @description: 人类创建工厂* @author: Jacob* @create: 2020-08-17 15:18**/
public class HumanFactory extends AbstractHumanFactory {@Overridepublic <T extends Human> T createHuman(Class<T> c) {Human human = null;try {human = (T)Class.forName(c.getName()).newInstance();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return (T)human;}
}

人种有了,八卦炉也有了,剩下的工作就是女娲采集黄土,然后命令八卦炉开始生产

/*** @program: DesignMode* @description: 女娲类* @author: Jacob* @create: 2020-08-17 15:21**/
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("\n--造出的第二批人是黑色人种--");Human blackHuman = YinYangLu.createHuman(BlackHuman.class);blackHuman.getColor();blackHuman.talk();//第三次造人,火候刚刚好,于是黄色人种产生了System.out.println("\n--造出的第三批人是黄色人种--");Human yellowHuman = YinYangLu.createHuman(YellowHuman.class);yellowHuman.getColor();yellowHuman.talk();}
}

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

--造出的第一批人是白色人种--
白色人种的皮肤颜色是白色的!
白色人种会说话,一般说的都是单字节。--造出的第二批人是黑色人种--
黑色人种的皮肤颜色是黑色的!
黑人会说话,一般人听不懂。--造出的第三批人是黄色人种--
黄色人种的皮肤颜色是黄色的!
黄色人种会说话,一般说的都是双字节。

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

工厂方法模式的定义

工厂方法模式使用的频率非常高,在我们日常的开发中总能见到它的身影。其定义为:
Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclasses.(定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。)

image-20200817173120736

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

/*** @program: DesignMode* @description: 抽象产品类* @author: Jacob* @create: 2020-08-17 15:32**/
public abstract class Product {//产品类的公共方法public void method1() {//业务逻辑处理System.out.println("Product.method1");}//抽象方法public abstract void method2();}

具体的产品类可以有多个,都继承于抽象产品类。

/*** @program: DesignMode* @description: 具体产品类 1* @author: Jacob* @create: 2020-08-17 15:33**/
public class ConcreteProduct1 extends Product {@Overridepublic void method1() {//业务逻辑处理System.out.println("ConcreteProduct1.method1()");}@Overridepublic void method2() {//业务逻辑处理System.out.println("ConcreteProduct1.method2()");}
}
/*** @program: DesignMode* @description: 具体产品类 1* @author: Jacob* @create: 2020-08-17 15:33**/
public class ConcreteProduct2 extends Product {@Overridepublic void method2() {//业务逻辑处理System.out.println("ConcreteProduct2.method2()");}
}

抽象工厂类负责定义产品对象的产生。

/*** @program: DesignMode* @description: 抽象工厂类* @author: Jacob* @create: 2020-08-17 15:36**/
public abstract class Creator {/**创建一个产品对象,其输入参数类型可以自行设置* 通常为String、Enum、Class等,当然也可以为空*/public abstract <T extends Product> T createProduct(Class<T> c);
}

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

/*** @program: DesignMode* @description: 具体工厂类* @author: Jacob* @create: 2020-08-17 15:36**/
public class ConcreteCreator extends Creator {@Overridepublic <T extends Product> T createProduct(Class<T> c) {Product product = null;try {product = (T)Class.forName(c.getName()).newInstance();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return (T)product;}
}
/*** @program: DesignMode* @description: 场景类* @author: Jacob* @create: 2020-08-17 15:38**/
public class Client {public static void main(String[] args) {Creator creator = new ConcreteCreator();Product product = creator.createProduct(ConcreteProduct1.class);product.method1();product.method2();product = creator.createProduct(ConcreteProduct2.class);product.method1();product.method2();}}

我们运行一下,结果如下所示。

ConcreteProduct1.method1()
ConcreteProduct1.method2()
Product.method1
ConcreteProduct2.method2()

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

工厂方法模式的应用

工厂方法模式的优点

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

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

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

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

工厂方法模式的使用场景

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

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

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

最后,可以使用在测试驱动开发的框架下。例如,测试一个类A,就需要把与类A有关 联关系的类B也同时产生出来,我们可以使用工厂方法模式把类B虚拟出来,避免类A与类B 的耦合。目前由于JMockEasyMock的诞生,该使用场景已经弱化了,读者可以在遇到此种 情况时直接考虑使用JMockEasyMock

工厂方法模式的扩展

缩小为简单工厂模式

我们这样考虑一个问题:一个模块仅需要一个工厂类,没有必要把它产生出来,使用静 态的方法就可以了,根据这一要求,我们把上例中的AbstarctHumanFactory修改一下。

image-20200817174812028

我们在类图中去掉了AbstractHumanFactory抽象类,同时把createHuman方法设置为静态 类型,简化了类的创建过程,变更的源码仅仅是HumanFactoryNvWa类,HumanFactory如代如下。

/*** @program: DesignMode* @description:  工厂方法模式的扩展--1. 缩小为简单工厂模式 人类创建工厂* @author: Jacob* @create: 2020-08-17 15:18**/
public class HumanFactory {public static  <T extends Human> T createHuman(Class<T> c) {Human human = null;try {human = (T)Class.forName(c.getName()).newInstance();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return (T)human;}
}

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

/*** @program: DesignMode* @description:  工厂方法模式的扩展--1. 缩小为简单工厂模式 女娲类* @author: Jacob* @create: 2020-08-17 15:21**/
public class NvWa {public static void main(String[] args) {//女娲第一次造人,火候不足,于是白人产生了System.out.println("--造出的第一批人是白色人种--");Human whiteHuman = HumanFactory.createHuman(WhiteHuman.class);whiteHuman.getColor();whiteHuman.talk();//女娲第二次造人,火候过足,于是黑人产生了System.out.println("\n--造出的第二批人是黑色人种--");Human blackHuman = HumanFactory.createHuman(BlackHuman.class);blackHuman.getColor();blackHuman.talk();//第三次造人,火候刚刚好,于是黄色人种产生了System.out.println("\n--造出的第三批人是黄色人种--");Human yellowHuman = HumanFactory.createHuman(YellowHuman.class);yellowHuman.getColor();yellowHuman.talk();}
}

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

升级为多个工厂类

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

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

image-20200817175132053

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

/*** @program: DesignMode* @description:  工厂方法模式的扩展--2. 升级为多个工厂类 多工厂模式的抽象工厂类* @author: Jacob* @create: 2020-08-17 15:18**/
public abstract class AbstractHumanFactory {public abstract Human createHuman();}

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

/*** @program: DesignMode* @description: 工厂方法模式的扩展--2. 升级为多个工厂类 黑色人种的创建工厂实现* @author: Jacob* @create: 2020-08-17 16:06**/
public class BlackHumanFactory extends AbstractHumanFactory {@Overridepublic Human createHuman() {return new BlackHuman();}
}
/*** @program: DesignMode* @description: 工厂方法模式的扩展--2. 升级为多个工厂类 白色人种的创建类* @author: Jacob* @create: 2020-08-17 16:05**/
public class whiteHumanFactory extends AbstractHumanFactory {@Overridepublic Human createHuman() {return new WhiteHuman();}
}
/*** @program: DesignMode* @description: 工厂方法模式的扩展--2. 升级为多个工厂类 黄色人种的创建类* @author: Jacob* @create: 2020-08-17 16:02**/
public class YellowHumanFactory extends AbstractHumanFactory {@Overridepublic Human createHuman() {return new YellowHuman();}
}

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

/*** @program: DesignMode* @description:  工厂方法模式的扩展--2. 升级为多个工厂类 女娲类* @author: Jacob* @create: 2020-08-17 15:21**/
public class NvWa {public static void main(String[] args) {//女娲第一次造人,火候不足,于是白人产生了System.out.println("--造出的第一批人是白色人种--");Human whiteHuman = new whiteHumanFactory().createHuman();whiteHuman.getColor();whiteHuman.talk();//女娲第二次造人,火候过足,于是黑人产生了System.out.println("\n--造出的第二批人是黑色人种--");Human blackHuman = new BlackHumanFactory().createHuman();blackHuman.getColor();blackHuman.talk();//第三次造人,火候刚刚好,于是黄色人种产生了System.out.println("\n--造出的第三批人是黄色人种--");Human yellowHuman = new YellowHumanFactory().createHuman();yellowHuman.getColor();yellowHuman.talk();}
}

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

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

替代单例模式

之前讲的单例模式以及扩展出的多例模式,并且指出了单例和多例的一些缺点,我们是不是可以采用工厂方法模式实现单例模式的功能呢?单例模式的核心要求就是在内存中 只有一个对象,通过工厂方法模式也可以只在内存中生产一个对象。

image-20200817175919551

非常简单的类图,Singleton定义了一个private的无参构造函数,目的是不允许通过new的 方式创建一个对象。

/*** @program: DesignMode* @description: 工厂方法模式的扩展--3. 替代单例模式 单例类* @author: Jacob* @create: 2020-08-17 16:15**/
public class Singleton {//不允许通过new产生一个对象private Singleton() {}public void doSomething() {//业务处理}
}

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

/*** @program: DesignMode* @description: 工厂方法模式的扩展--3. 替代单例模式 单例工程* @author: Jacob* @create: 2020-08-17 16:21**/
public class SingletonFactory {private static Singleton singleton;static {try {Class cl = Class.forName(Singleton.class.getName());//获得无参构造Constructor constructor = cl.getDeclaredConstructor();//设置无参构造是可访问的constructor.setAccessible(true);//产生一个实例对象singleton = (Singleton) constructor.newInstance();} catch (Exception e) {//异常处理}}public static Singleton getSingleton() {return singleton;}
}

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

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

延迟初始化

何为延迟初始化(Lazy initialization)?一个对象被消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用。延迟初始化是工厂方法模式的一个扩展应用。

image-20200817180148980

ProductFactory负责产品类对象的创建工作,并且通过prMap变量产生一个缓存,对需要 再次被重用的对象保留,ProductConcreteProduct是一个示例代码。

/*** @program: DesignMode* @description: 工厂方法模式的扩展--4. 延迟初始化 抽象产品类* @author: Jacob* @create: 2020-08-17 15:32**/
public abstract class Product {//产品类的公共方法public void method1() {//业务逻辑处理System.out.println("Product.method1");}//抽象方法public abstract void method2();}
/*** @program: DesignMode* @description: 工厂方法模式的扩展--4. 延迟初始化 具体产品类 1* @author: Jacob* @create: 2020-08-17 15:33**/
public class Product1 extends Product {@Overridepublic void method1() {//业务逻辑处理System.out.println("ConcreteProduct1.method1()");}@Overridepublic void method2() {//业务逻辑处理System.out.println("ConcreteProduct1.method2()");}
}
/*** @program: DesignMode* @description: 工厂方法模式的扩展--4. 延迟初始化 具体产品类 1* @author: Jacob* @create: 2020-08-17 15:33**/
public class Product2 extends Product {@Overridepublic void method2() {//业务逻辑处理System.out.println("ConcreteProduct2.method2()");}
}
/*** @program: DesignMode* @description: 具体工厂类* @author: Jacob* @create: 2020-08-17 15:36**/
public class ProductFactory {private static final Map<String, Product> prMap = new HashMap<>();public static synchronized Product createProduct(String type) {Product product = null;if (prMap.containsKey(type)) {product = prMap.get(type);System.out.println("Map中已经有这个对象");} else {switch (type) {case "product1":product = new Product1();break;case "product2":product = new Product2();break;default:throw new RuntimeException("创建对象异常");}prMap.put(type, product);}return product;}
}
/*** @program: DesignMode* @description: 工厂方法模式的扩展--4. 延迟初始化 场景类* @author: Jacob* @create: 2020-08-17 15:38**/
public class Client {public static void main(String[] args) {Product product = ProductFactory.createProduct("product1");product.method1();product.method2();System.out.println("---------------------------------------------");product = ProductFactory.createProduct("product2");product.method1();product.method2();System.out.println("---------------------------------------------");product = ProductFactory.createProduct("product1");product.method1();product.method2();}}

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

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

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

最佳实践

工厂方法模式在项目中使用得非常频繁,以至于很多代码中都包含工厂方法模式。该模式几乎尽人皆知,但不是每个人都能用得好。熟能生巧,熟练掌握该模式,多思考工厂方法如何应用,而且工厂方法模式还可以与其他模式混合使用(例如模板方法模式、单例模式、原型模式等),变化出无穷的优秀设计,这也正是软件设计和开发的乐趣所在。

学习于:《设计模式之禅》 — 秦小波

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/273568.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式 之 单例模式

项目源码&#xff1a;https://gitee.com/Jacob-gitee/DesignMode 个人博客&#xff1a;https://jacob.org.cn 宗旨 Ensure a class has only one instance,and provide a global point of access to it.&#xff08;确保某一个类只有一个实例&#xff0c;而且自行实例化并向整个…

如何实现滑动scrollview上下隐藏

问题描述现在有一个需求&#xff0c;就是一个界面如下ABCA固定在顶部&#xff0c;C固定在底部其中B是一个scrollview(也可能是listview)&#xff0c;要实现&#xff0c;在向上滑动B的时候&#xff0c;A平滑的往上滑&#xff0c;同时C平滑的往下滑&#xff0c;直到消失&#xff…

设计模式 之 抽象工厂模式

项目源码&#xff1a;https://gitee.com/Jacob-gitee/DesignMode 个人博客 &#xff1a;https://jacob.org.cn 女娲的失误 工厂模式中讲了女娲造人的故事。人是造出来了&#xff0c;世界也热闹了&#xff0c;可是低头一看&#xff0c;都是清一色的类型&#xff0c;缺少关爱、仇…

strip 命令的使用方法

用途 通过除去绑定程序和符号调试程序使用的信息&#xff0c;降低扩展公共对象文件格式&#xff08;XCOFF&#xff09;的对象文件的大小。 语法 strip [ -V ] [ -r [ -l ] | -x [ -l ] | -t | -H | -e | -E ] [ -X {32 |64 |32_64 }] [ -- ] File ... 描…

设计模式 之 模板模式

项目源码&#xff1a;https://gitee.com/Jacob-gitee/DesignMode 个人博客 &#xff1a;http://jacob.org.cn 女娲的失误 工厂模式中讲了女娲造人的故事。人是造出来了&#xff0c;世界也热闹了&#xff0c;可是低头一看&#xff0c;都是清一色的类型&#xff0c;缺少关爱、仇…

使用Java高速实现进度条

基于有人问到如何做进度条&#xff0c;以下给个简单的做法&#xff1a; 主要是使用JProgressBar&#xff08;Swing内置javax.swing.JProgressBar&#xff09;和SwingWorker&#xff08;Swing内置javax.swing.SwingWorker&#xff09; 有人肯定会说&#xff0c;不是用线程做的吗…

Linux 安装JDK

个人博客 &#xff1a;https://www.siyuan.run CSDN&#xff1a;https://blog.csdn.net/siyuan 微信小程序&#xff1a;思远Y 安装时使用到的命令&#xff1a; cd&#xff1a;切换目录。 eg&#xff1a;cd / mkdir&#xff1a;创建目录。 eg&#xff1a;mkdir jacob 创建单极目…

Css导航

<div> <ul> <li><a></a></li> <li><a></a></li> <li><a></a></li> .. </ul> </div> <li>中也可包含 <ul> <a></a> <li><a></a>&…

关于js的function.来自百度知道的回答,学习了.

在js中&#xff0c;创建一个函数对象的语法是var myFunction new Function(arg1,…,agrN, body);其中&#xff0c;该函数对象的N个参数放在 函数主体参数body的前面&#xff0c;即函数主体参数必须放在参数列表的最后&#xff0c;也可以无参数new Function(body)。你添加第三个…

Ribbon 支持的9大负载均衡策略

个人博客 &#xff1a;https://www.siyuan.run CSDN&#xff1a;https://blog.csdn.net/siyuan 微信小程序&#xff1a;思远Y 线性轮询策略&#xff1a; RoundRibbonRule BaseLoadBalancer 负载均衡器默认采用线性负载轮询负载均衡策略。 工作流程&#xff1a; RoundRibbonRule…

fedora20开机启动配置:systemctl

老版fedora中使用chkconfig配置开机启动&#xff0c;fedora20中&#xff0c;使用chkconfig会出现各种问题。使用systemctl配置。 具体表格如下 转载于:https://www.cnblogs.com/hh6plus/p/5548083.html

Mysql 字符操作函数相关

常用的字符串函数&#xff1a; 函数说明CONCAT(s1,s2&#xff0c;...)返回一个或多个待拼接的内容&#xff0c;任意一个为NULL则返回值为NULL。CONCAT_WS(x,s1,s2,...)返回多个字符串拼接之后的字符串&#xff0c;每个字符串之间有一个x。SUBSTRING(s,n,len)、MID(s,n,len)两个…

“cvSnakeImage”: 找不到标识符

1>g:\project\opencv\helloopencv\helloopencv\helloopencv.cpp(74) : error C2065: “CV_VALUE”: 未声明的标识符1>g:\project\opencv\helloopencv\helloopencv\helloopencv.cpp(74) : error C3861: “cvSnakeImage”: 找不到标识符 增加头文件 #include <opencv2/l…

Shell 快速入门

个人博客 &#xff1a;https://www.siyuan.run CSDN&#xff1a;https://blog.csdn.net/siyuan 微信小程序&#xff1a;思远Y 概述 Shell 是一个用 C 语言编写的程序&#xff0c;它是用户使用 Linux 的桥梁。Shell 既是一种命令语言&#xff0c;又是一种程序设计语言。 Shell…

Andriod开发 --插件安装、环境配置、问题集锦

1.用Eclipse搭建Android开发环境和创建第一个Android项目&#xff08;Windows平台&#xff09; 链接阅读http://www.cnblogs.com/allenzheng/archive/2012/11/10/2762379.html 搭建环境中的不同之处&#xff1a; &#xff08;1&#xff09;我在安装过程中&#xff0c;在安装ADT…

《Java 高并发》01 高并发基本概念

基本概念 同步和异步 同步和异步通常是用来形容一次方法调用。 同步方法调用一旦开始&#xff0c;调用者必须等到方法返回才能继续执行后续操作。 异步方法调用更像一个消息传递&#xff0c;一旦开始&#xff0c;方法调用就会立即返回&#xff0c;调用者就可以继续后续的操…

Android之Http网络编程(四)

前面几篇博文简单的介绍了一些常见的Http的操作&#xff0c;这些操作几乎都是在新开的线程中进行的网络请求&#xff0c;并在日志中打印出获取到的网络数据。那么&#xff0c;问题来了&#xff01;&#xff08;呃~感觉下一句是蓝翔有木有&#xff1f;&#xff09;如何在把获取到…

《Java 高并发》02 多线程的特性

多线程的三大特性&#xff1a;原子性、可见性和有序性。 原子性 原子性是指一个操作或者多个操作&#xff0c;一旦开始就不会被其他线程干扰&#xff0c;即使是在多个线程一起执行的情况下也不会被干扰。或者不执行。 原子性主要是为了保证数据一致&#xff0c;线程安全问题…

U3D-FSM有限状态机的简单设计

http://coder.beitown.com/archives/592 在之前的文章里介绍了一个基础U3D状态机框架&#xff08;Unity3D游戏开发之状态流框架&#xff09;即大Switch的枚举状态控制。这种方法虽然容易理解&#xff0c;编程方法也相对简单&#xff0c;但是弊端是当状态变得复杂之后&#xff0…

《Java 高并发》04 线程的基本操作

新建线程 新建线程很简单。只要使用new 关键字创建一个线程对象&#xff0c;并且调用 start 方法启动线程。 Thread t new Thread(); t.start();注意&#xff1a;run 方法不是用来启动线程。如果调用 run 方法它只会作为普通方法来执行&#xff0c;而不会开启线程执行。 终止…