什么是简单工厂模式
简单工厂模式属于类的创建型模式,又叫做静态工厂方法模式。通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
模式中包含的角色及其职责
工厂(Creator)角色
简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。
抽象(Product)角色
简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
具体产品(Concrete Product)角色
简单工厂模式所创建的具体实例对象
上面的定义可能不容易理解,我们举例子来一步一步的说明简单工厂模式是如何实现的。
1、原始实现
定义两个实体类,一个是苹果类,一个是香蕉类,然后在 Main 方法中通过 new 生成他们对应的实例,通过 实例.方法名称 去调用他们对应的方法。
public class Apple {
public void get(){
System.out.println("摘苹果");
}
}
public class Banana {
public void get(){
System.out.println("摘香蕉");
}
}
public class Main {
public static void main(String[] args) {
Apple apple = new Apple();
Banana banana = new Banana();
apple.get();
banana.get();
}
}
2、改进实现一
这是最基本的写法,我们观察到苹果和香蕉都有相同的 get 方法,因此我们决定新建一个 Fruit 类作为父类,让 Apple 和 Banana 去分别实现父类中的方法,于是就有了下面的写法。
public interface Fruit {
void get();
}
public class Main {
public static void main(String[] args) {
Fruit apple = new Apple();
Fruit banana = new Banana();
apple.get();
banana.get();
}
}
public class Apple implements Fruit {
public void get(){
System.out.println("摘苹果");
}
}
public class Banana implements Fruit{
public void get(){
System.out.println("摘香蕉");
}
}
上面的代码很明显用到了多态,在 Main Class 中的这样两行代码
Fruit apple = new Apple();
Fruit banana = new Banana();
很明显,父类的引用指向了子类的对象。基于上面的写法,我们还能不能进一步进行修改呢?答案是 yes 。下面我们在对上面的代码进行进一步的修改。
3、改进实现二
我们新建一个工厂类,这个工厂类主要负责产生对应的实例
public class FruitFactory {
public static Fruit getApple(){
return new Apple();
}
public static Fruit getBanana(){
return new Banana();
}
}
public class Main {
public static void main(String[] args) {
//通过工厂来产生对应的实例
Fruit apple = FruitFactory.getApple();
Fruit banana = FruitFactory.getBanana();
apple.get();
banana.get();
}
}
其他的类我就不贴出来了,和上面的一样,这里我们主要是对 FruitFactory 进行改进。
4、改进实现三
根据上面的情况,我们看看 FruitFactory 还有没有什么需要改进的地方呢? 应该是有的,是吧。我们接下来对工厂进行进一步的改进。
提供一个 get 方法,通过传入的 type 来决定生产什么样的对象。
public class FruitFactory {
public static Fruit get(String type) throws IllegalAccessException, InstantiationException {
if(type.equalsIgnoreCase("apple")){
return Apple.class.newInstance();
}else if(type.equalsIgnoreCase("banana")){
return Banana.class.newInstance();
}else{
throw new RuntimeException("找不到对应的实例生成");
}
}
}
public class Main {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Fruit apple = FruitFactory.get("apple");
Fruit banana = FruitFactory.get("banana");
apple.get();
banana.get();
}
}
上面的这种写法比较灵活,但是缺点也很明显,比如如果我现在想要新增一个梨子对象,我们必须要修改 FruitFactory 中的 if else 语句才能通过工厂产生梨子对象,这显然是不合乎要求的。我们可以对 FruitFactory 在进行更好的优化。
public class FruitFactory {
pubilc static Fruit get(String type) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class fruit = Class.forName(type);
return (Fruit) fruit.newInstance();
}
}
public class Main {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Fruit apple = FruitFactory.get("com.selfassu.factory.Apple");
Fruit banana = FruitFactory.get("com.selfassu.factory.Banana");
apple.get();
banana.get();
}
}
这里我们通过 Class.forName(完整类名) 去动态生产不同的对象,这里的原理就是反射。我们的简单工厂模式到这里就结束了,可以观察一下每一个 FruitFactory 中的 get 方法,三次均是不同的,我们对 get 方法经过了 三次不同的改造,从适用性来看,最后一种是最好的。
假如我们现在要添加一个新的类型 – 梨子
首先我们新建一个 Pear 类,它实现了 Fruit 接口
按照第一个工厂
我们必须在工厂中添加一个 getPear() 方法,然后返回一个 Fruit 对象。按照第二个工厂
我们需要在 FruitFactory 中的 get() 方法中添加 一个 if else 语句。按照第三个工厂
我们不需要对工厂做任何的改动,只需要通过调用 FruitFactory 的 get() 方法传入 Pear 类的全路经即可生成对应的对象。这样很好的实现了对扩展开放,对修改闭合的原则。也就是我们常说的开闭原则。
综合上面所描述的,可扩展性和适用性一目了然。当然第三种也有它自己的缺陷,比如 get() 方法传入的类型必须是类的全路径,而第二种就不需要传入全路径名。
简单工厂模式的优缺点
在这个模式中,工厂类是整个模式的关键所在。它包含必要的判断逻辑,能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。用户在使用时可以直接根据工厂类去创建所需的实例,而无需了解这些对象是如何创建以及如何组织的。有利于整个软件体系结构的优化。
不难发现,简单工厂模式的缺点也正体现在其工厂类上,由于工厂类集中了所有实例的创建逻辑,所以“高内聚”方面做的并不好。另外,当系统中的具体产品类不断增多时,可能会出现要求工厂类也要做相应的修改,扩展性并不很好。