Java设计模式2:简单工厂模式

时间:2021-01-05 12:30:26

简单工厂模式

简单工厂模式是类的创建模式,又叫做静态工厂方法模式。简单工厂模式由一个工厂对象决定生产出哪一种产品类的实例。

为什么要使用简单工厂模式

原因很简单:解耦。

A对象如果要调用B对象,最简单的做法就是直接new一个B出来。这么做有一个问题,假如C类和B类实现了同一个接口/继承自同一个类,系统需要把B类修改成C类,程序不得不重写A类代码。如果程序中有100个地方new了B对象,那么就要修改100处。

这就是典型的代码耦合度太高导致的"牵一发动全身"。所以,有一个办法就是写一个工厂IFactory,A与IFactory耦合,修改一下,让所有的类都实现C接口并且IFactory生产出C的实例就可以了。

简单工厂模式示例

以水果为例:

public interface Fruit
{
void grow();// 生长
void harveset(); // 收货
void plant();// 种植
}

有两个子类苹果和葡萄:

public class Apple implements Fruit
{
public void grow()
{
System.out.println("Apple.grow()");
} public void harveset()
{
System.out.println("Apple.harveset()");
} public void plant()
{
System.out.println("Apple.plant()");
}
}
public class Grape implements Fruit
{
public void grow()
{
System.out.println("Grape.grow()");
} public void harveset()
{
System.out.println("Grape.harveset()");
} public void plant()
{
System.out.println("Grape.plant()");
}
}

有一个园丁,专门负责生产出各种水果:

public class Gardener
{
public static Fruit getFruit(String fruit)
{
if ("apple".equalsIgnoreCase(fruit))
{
return new Apple();
}
else if ("grape".equalsIgnoreCase(fruit))
{
return new Grape();
}
else
{
return null;
}
}
}

想要什么水果就问园丁拿就好了:

public static void main(String[] args)
{
Fruit fruit0 = Gardener.getFruit("APPLE");
fruit0.grow();
Fruit fruit1 = Gardener.getFruit("GRAPE");
fruit1.harveset();
}

程序这么写优点就出来了:

1、用户不自己去生产产品,只需要负责去拿自己需要的东西就好了,这样用户-->产品之间的耦合度就降低了

2、代码模块职责更明确了,有专门消费的模块、有专门生产的模块

改进

上面的代码虽然实现了用户-->产品之间的分离,但还是有一个问题,工厂并不知道有多少种产品,所以每一次新增产品的时候,都需要新增else if分支,这样是不是不便呢?所以我们又想了一个办法,就是反射,园丁可以这么修改:

public class Gardener
{
public static Fruit getFruit(String fruitPath) throws Exception
{
Class<?> c = Class.forName(fruitPath);
return (Fruit)c.newInstance();
}
}

调用的地方可以写成:

public static void main(String[] args) throws Exception
{
Fruit fruit0 = Gardener.getFruit("com.xrq.simplefactory.Apple");
fruit0.grow();
Fruit fruit1 = Gardener.getFruit("com.xrq.simplefactory.Grape");
fruit1.harveset();
}

当然,这么写其实也有一点点问题,假如有一天我的项目想进行一个重构,重整类路径,包路径,比方说生产Apple的地方有100处,岂不是要修改100处?当然不用,有以下三种方法推荐:

1、写一个接口FruitPath,里面定义常量:

public interface FruitPath
{
public final static String apple = "com.xrq.simplefactory.Apple";
public final static String grape = "com.xrq.simplefactory.Grape";
}

2、写一个Fruit.properties文件,里面定义水果和类路径的对应关系:

Apple=com.xrq.simplefactory.Apple
Grape=com.xrq.simplefactory.Grape

3、写一个Fruit.xml文件,里面定义水果和类路径的对应关系:

<apple>com.xrq.simplefactory.Apple</apple>
<grape>com.xrq.simplefactory.Grape</grape>

第一种方式不说了,第二种方式.properties可以用Java自带的Properties类来解析,第三种方式.xml可以用DOM4J来解析。这样, 假设我以后要修改水果的路径,修改一个文件就可以了。

从设计模式的角度讲,这么修改也有很大的优点。现在不管我新增还是删除水果,园丁(类工厂)都不用变了,只需要告诉工厂我需要哪种水果就够了,工厂自然会给调用者返回。这种写法,也是Spring的基础。

最后说一点,希望大家明白,简单工厂模式或者说工厂模式的关注点并不在于在工厂中是如何生产出来需要的类的,而在于将创建产品与消费产品分离。前面使用过if...else if...else、反射,除了这些方法,还可以有别的方法可以创建产品,比如传入一个具体产品的标识,根据这个标识去数据库里面查询。

简单工厂模式在Java中的应用及解读

以后每一篇文章尽可能地介绍设计模式在Java中的应用,因为我认为不是每种设计模式开发者都有机会可以用到,但是能在原有代码中敏锐地看出这是一种什么设计模式,至少能说明对这种设计模式是理解了。这里讲一下JDK中的简单工厂模式。

JDK中的简单工厂模式有很多应用,比较典型的比如线程池,具体可以参见Java多线程18:线程池。我们使用线程池的时候,可以使用ThreadPoolExecutor,根据自己的喜好传入corePoolSize、maximumPoolSize、keepAliveTimem、unit、workQueue、threadFactory、handler这几个参数,new出一个指定的ThreadPoolExecutor出来。

JDK给开发者提供了Executors这个类,可以让用户产生ThreadPoolExecutor和使用ThreadPoolExecutor分离开,比如可以让Executors提供一个单线程的线程池Executors.newSingleThreadExecutor()、让Executors提供一个*线程池Executors.newCachedThreadPool()等,这样,开发者可以不用关心线程池是如何去实现的,直接使用Executors方法提供给开发者的ThreadPoolExecutor就可以了。

工厂模式的优缺点

优点:

1、简单优化了软件体系结构,明确了各自功能模块的职责和权利

2、通过工厂类,外界不需要直接创建具体产品对象,只需要负责消费,不需要关心内部如何创建对象

缺点:

1、改进前的简单工厂模式全部创建逻辑都集中在一个工厂类中,能创建的类只能是考虑到的,如果需要添加新的类,就必须改变工厂类了

2、改进前的简单工厂模式随着具体产品的不断增多,可能会出现共产类根据不同条件创建不同实例的需求,这种对条件的判断和对具体产品类型的判断交错在一起,很难避免功能模块的蔓延,对系统的维护和扩展不利

3、改进后的简单工厂模式主要是使用反射效率会低一些