介绍
在设计原则中有这样一句话“我们应该针对接口编程,而不是针对实现编程”。但是我们还是一直使用new关键字来创建一个对象,这不就是在针对实现编程吗?
在技术上,new没有错,毕竟这是java的基础部分。真正的犯人是我们的老朋友“改变”。如果有一个不像是会改变的类,那么在代码中直接实例化也无大碍。想想看,我们平常还不是在程序中不假思索地实例化字符串对象吗?就没有违反这个原则?当然有!可以这么做吗?可以!为什么?因为字符串不可能改变。
针对接口编程,可以隔离掉以后系统发生的一大堆改变。为什么呢?如果代码是针对接口编写,那么通过多态,它可以与任何新类实现该接口。但是当代码使用大量的具体类时,等于是自找麻烦。
如果我们希望能够调用一个简单的方法,传递一个参数过去,就可以返回一个相应的具体对象。这个时候就可以使用简单工厂模式。
简单工厂其实不是一个设计模式,反而像是一种编程习惯。但是由于经常被使用,有些开发人员的确是把这个编程习惯称为“工厂模式”。
概念
简单工厂模式,可以根据传递的参数不同,返回不同类的实例。简单工厂模式定义了一个类,这个类专门用于创建其它类的实例,这些创建的类都有一个共同的父类。
简单工厂模式结构图:
- Factory:工厂角色。专门用于创建实例类的工厂,提供一个方法,该方法根据传递参数的不同返回不同类的具体实例。
- Product:抽象产品角色。是所有具体产品的父类。
- ConcreateProduct:具体的产品角色。
模式实现
一年一度的春节到了,小马哥打算去附近车厂买一辆车,这个车厂生产三种类型的车,分别是宝马、奔驰和奥迪。下面我们使用简单工厂来模拟实现:
抽象车类(Product):
public abstract class BaseCar {
public static final String BMW = "Bmw";
public static final String BENZ = "Benz";
public static final String AUDI = "Audi";
/**
* 启动
*/
public abstract void start();
}
宝马、奔驰、奥迪(ConcreteProduct)这三辆车都实现了车类:
public class BmwCar extends BaseCar {
@Override
public void start() {
System.out.println("宝马启动了");
}
}
public class BenzCar extends BaseCar {
@Override
public void start() {
System.out.println("奔驰启动了");
}
}
public class AudiCar extends BaseCar {
@Override
public void start() {
System.out.println("奥迪启动了");
}
}
车厂类(Factory),根据传入的参数来生产对应的车:
/**
* 增加车库类来管理车
*/
public class CarFactory {
public BaseCar getCar(String carType){
BaseCar car;
switch (carType) {
case BaseCar.BMW:
car = new BmwCar();
break;
case BaseCar.BENZ:
car = new BenzCar();
break;
case AUDI:
car = new AudiCar();
break;
default:
car = null;
}
return car;
}
}
买一辆奔驰回家:
public class Test {
public static void main(String[] args) {
BaseCar car = new CarFactory().getCar(BaseCar.BENZ);
car.start();
}
}
继续来看一下UML类图:
优点
- 简单工厂模式实现了对责任的分割,提供了专门的工厂类用于创建对象。
- 客户端无需知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可。
缺点
- 由于工厂类集中了所有产品创建的逻辑,一旦不能正常工作,整个系统都要收到影响。
- 系统扩展困难,一旦添加新产品不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
适用场景
- 工厂类负责创建的对象比较少。
- 客户端只知道传入工厂类的参数,对于如何创建对象不关心。
总结
简单工厂模式最大的优点在于工厂类中包含了必要的逻辑判断,根据应用层的选择条件动态实例化相关的类,对于应用层来说,去除了具体的产品依赖。
简单工厂模式是用一个工厂来生产不同的产品(工厂方法模式是用不同的工厂来生产不同的产品,一个工厂用来生产一种产品),通过应用层传入的参数来判断应该生产哪一种产品,如果要增加新的产品,就需要修改工厂类的判断逻辑(增加‘Case’的分条件),不符合开闭原则(对修改关闭,对扩展开放)。如果产品类经常更改,可以使用工厂方法模式。
源码分析(JDK 13)
Calendar 类的getInstance()方法
public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {
//...(省略无关的代码,下同)
public static Calendar getInstance() {
Locale aLocale = Locale.getDefault(Locale.Category.FORMAT);
//根据语言和国家的不同来返回不同的Calendar对象
return createCalendar(defaultTimeZone(aLocale), aLocale);
}
//根据传入的参数返回相应的具体Calendar实例
private static Calendar createCalendar(TimeZone zone,
Locale aLocale){
//...
Calendar cal = null;
//...
if (cal == null) {
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
}
UML类图: