简单工厂
简单工厂虽然不是一个标准的设计模式,反而更像是一种编程习惯,太常用了,所以我们也有必要介绍一下它。
简单工厂又叫静态工厂方法模式,它是通过使用静态方法接收不同的参数类型返回不同的实例对象。
目的
在工厂类中提供一个封装的静态工厂方法,用于隐藏对象初始化细节,使客户端可以专注于使用,而不用关心类的初始化过程。
解释
假设我们有一个需要连接到 SQL Server 的 Web 应用,但现在我们需要切换到连接 Oracle。为了不修改现有代码的情况下做到这一点,
我们需要实现简单工厂模式。在这种模式下,可以通过调用一个静态方法来创建与给定数据库的连接。
维基百科
工厂类是一个用于创建其他对象的对象 – 从形式上看,工厂方法是一个用于返回不同原型或类型的函数或方法。
动机
常规的创建对象的方法使用关键字new,new带来的问题就是实现依赖,不能应对具体实例化类型的变化;那么我们如何解决这个问题。
我们知道设计模式的最最核心的理念就是封装变化点,哪里变化,封装哪里。
new对象带来的问题就是创建对象依赖具体的实例化对象,因此变化点就在对象的创建,我们就要使用简单工厂封装对象的创建。
引例
本例主要引入new关键字带来的问题,示例代码如下:
public interface Api {
public void test(String str);
}
public class Impl implements Api {
@Override
public void test(String str) {
System.out.println("print::::::"+str);
}
}
public class Client {
public static void main(String[] args) {
// 客户端在调用时候,不但知道了接口,还知道了实现,接口的思想就是"封装隔离";
// 而实现类Impl应该是被接口Api封装并同客户端隔离开的,也就是说客户端根本就
// 不应该知道具体的实现类是Impl
Api api = new Impl();
api.test("new has some problem");
}
}
上述代码问题如下:
客户端在调用时候,不但知道了接口,还知道了实现。
接口的思想就是"封装隔离"而实现类Impl应该是被接口Api封装并同客户端隔离开的,也就是说客户端根本就
不应该知道具体的实现类是Impl。
那么此问题如何解决?当然使用简单工厂了
定义
提供一个创建对象实例的功能,而无须关心具体实现。被创建的实例的可以是接口、抽象类、也可以是具体的类
简单工厂重构上述例子
在这里,我只列举出来变化的类,代码如下:
/**
* 类描述: 简单工厂
*/
public class Factory {
// 静态工厂方法
public static Api createApi() {
return new Impl();
}
}
public class Client {
public static void main(String[] args) {
Api api = Factory.createApi();
api.test("new has some problem");
}
}
上述客户端代码,不再依赖具体的实例化对象,依赖一个简单工厂,解决了new对象带来的耦合问题;
那么你可能会问,你不就是把new Impl()的代码放入到了Factory中,那么Factory不就又与具体的
实例耦合在一起了吗?针对这个问题,首先我们要明白简单工厂所处的位置,简单工厂的位置位于封装体内
也就是简单工厂是跟接口和具体类的实现在一起的,算是封装体内部的一个类,所以简单工厂知道具体的实例化类没有关系的
虽然说简单工厂的方法大多是用来创建接口的,但是仔细分析就会发现,真正能实现功能的是具体的实现类,这些类是已经做好的
并不是真的要靠简单工厂来创造出来的,简单工厂的方法无外乎就是:实现了选择一个合适的实现类来使用
说明:我上述的引例,api的接口实现类就写了一个,因此在客户端无需选择,如果有多个实现类的话,客户端传递选择的条件。
本质
简单工厂的本质是:选择实现
首先我们来看以下示意例子
public interface Car {
void print();
}
/**
* 类描述: 奔驰
*
*/
public class Ben implements Car {
@Override
public void print() {
System.out.println("this is Ben");
}
}
/**
* 类描述: 奥迪
*
*/
public class Audi implements Car {
@Override
public void print() {
System.out.println("this is Audi");
}
}
public enum CarTypeEnum {
BEN(Ben::new),
AUDI(Audi::new);
private final Supplier<Car> constructor;
CarTypeEnum(Supplier<Car> constructor) {
this.constructor = constructor;
}
public Supplier<Car> getConstructor() {
return this.constructor;
}
}
public class CarFactory {
public static Car getCar(CarTypeEnum typeEnum) {
return typeEnum.getConstructor().get();
}
}
public class CarTest {
public static void main(String[] args) {
Car ben = CarFactory.getCar(CarTypeEnum.BEN);
Car audi = CarFactory.getCar(CarTypeEnum.AUDI);
ben.print();
audi.print();
}
}
上述代码Car ben = CarFactory.getCar(CarTypeEnum.BEN);通过枚举类选择车型,验证了简单工厂的本质,选择实现
适用场景
在你只关心对象的创建,但不关心如何创建、管理它的时候,请使用简单工厂模式。
优点
可以把对象创建代码集中在一个地方,避免在代码库存散布 “new” 关键字。
可以让代码更加低耦合。它的一些主要优点包括更好的可测试性、更好的可读性、组件可替换性、可拓展性、更好的隔离性。
缺点
会使代码变得比原来的更加复杂一些。
案例
java.util.Calendar#getInstance()
java.util.ResourceBundle#getBundle()
java.text.NumberFormat#getInstance()
java.nio.charset.Charset#forName()
java.net.URLStreamHandlerFactory#createURLStreamHandler(String)
java.util.EnumSet#of()
javax.xml.bind.JAXBContext#createMarshaller()