简单工厂模式
- 定义:提供一个创建对象实例的功能, 而无须关心其具体实现,被创建的类型可以是接口、抽象类,也可以是具体类。
简单工厂模式的例子:
public interface Fruit {
public void grow();
}
public class Banana {
@Override
public void grow() {
System.out.println("我是香蕉,我在生长");
}
}
public class Apple {
@Override
public void grow() {
System.out.println("我是苹果,我在生长");
}
}
public class FruitFactory {
public static Fruit product(String name) {
if ("apple".equals(name)) {
return new Apple();
}
if ("banana".equals(name)) {
return new Banana();
}
return null;
}
}
public class Client {
public static void main(String[] args) {
Fruit fruit1 = FruitFactory.product("apple");
fruit1.grow();
Fruit fruit2 = FruitFactory.product("banana");
fruit2.grow();
}
}
输出结果:
我是苹果,我在生长
我是香蕉,我在生长
如果不使用简单工厂模式那么客户端代码会变成这样:
public class Client {
public static void main(String[] args) {
Fruit fruit1 = new Apple();
fruit1.grow();
Fruit fruit2 = new Banana();
fruit2.grow();
}
}
在阐述简单工厂模式之前,我们先来回顾一下接口,接口是用来干什么的呢?通常用接口来定义实现类的外观,约定了实现类应该要实现的功能。使用接口的好处:只要接口不变,内部实现的变化就不会影响到外部应用,从而使系统变得更加灵活,具有更好的扩展性和可维护性。而以上代码是直接在客户端new 的实现类,这样大大降低的接口的好处,扩展性和可维护性都变差了,如果系统又增加了一种水果怎么办,需要改代码吗?答案是肯定不是,如果你说那还不是要在FruitFactory中在加一段逻辑,那还不是要改代码呀,不要着急,这里会有其他办法解决的,比如使用配置文件解析,反射等知识点。
静态工厂模式
通常把简单工厂类实现成一个工具类,直接使用静态方法就可以了,也就是说简单工厂的方法都是静态的,所有也被成为静态工厂。
public class FruitFactory {
private FruitFactory() {}
public static Fruit product(String name) {
if ("apple".equals(name)) {
return new Apple();
}
if ("banana".equals(name)) {
return new Banana();
}
return null;
}
}
静态工厂类的构造方法为private,因为工厂类只有一个就可以了,所有声明成私有的,这样更加严格。
静态工厂(简单工厂)中方法的写法:主要实现的功能是“选择合适的实现类”来创建实例对象。那如何选择?
- 通过配置文件
public static Fruit product() {
Properties p = new Properties();
InputStream in = null;
try {
in = FruitFactory.class.getResourceAsStream("Fruit.properties");
p.load(in);
} catch (IOEception e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Fruit fruit = null;
try {
fruit = (Fruit)Class.forName(p.getProperty("fruitClass")).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return fruit;
}
//Fruit.properties文件的内容如下:frutiClass=com.yhc.fruitImpl.Apple
- 通过Client或者说是形参
//不同的参数返回不同的对象,而参数正是来源于客户端
if ("apple".equals(name)) {
return new Apple();
}
if ("banana".equals(name)) {
return new Banana();
}
- 通过系统自身,比如运行期间的某个值
FruitFactory类中定义一个静态的私有的变量
private static int count = 0;
静态方法中这样写:
if (count < 5) {
count++;
return new Apple();
}
if (count >= 5) {
return new Banana();
}
注意:第2中方式有一点需要了解的,如果是从客户端调用工厂的时候,传入选择的参数,这就说明客户端必须知道每个参数的含义,也需要知道每个参数对应的功能处理,这就要求必须在一定程度上,向客户端暴露一定的内部实现细节。第一种方式比起第二种方式要好,第一种方式的“选择”体现在配置文件中,系统需要那种对象,我就修改它的全路径类名,而且其还具有可扩展性,如果我再增加一种水果,就只编写类和修改配置文件,其他代码完全不用修改,这就是可插拔性。
简单工厂的优缺点:帮助封装,解耦,可能增加客户端的复杂度,不方便扩展自工厂
简单工厂的本质是:选择实现
何时使用简单工厂:
1.如果想要完全封装隔离具体实现,让外部只能通过接口来操作封装体,那么可以选用简单工厂,让客户端通过工厂来获取相应的接口,而无需关心具体实现。
2.如果想要把对外创建对象的职责集中管理和控制,可以选用简单工厂,一个简单工厂可以创建很多的、不相关的对象,可以把对外创建对象的职责集中到一个简单工厂来,从而实现集中管理和控制。