工厂模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
工厂模式可以将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到依赖关系的解耦,提高扩展和可维护性;
简单来说,对象不再通过new 进行创建,而是工厂来创建对象,这样做的好处是可以屏蔽创建对象的细节(例如需要大量传参),对外提供一个创建对象的统一方法,实现了创建者和调用者的分离;
工厂模式可以分为两类:
- 简单工厂模式(也叫静态工厂模式)
- 工厂方法模式
【抽象工厂模式,归属于另一种设计模式;】
简单工厂模式
简单工厂模式,工厂类提供创建对象的静态方法,因此也叫静态工厂模式;
代码实现
有一个Car(车)的接口和WuLing(五菱),Tesla(特斯拉)两个实现类;
- Car的接口类
//接口类
public interface Car {
void name();
}
- WuLing 实现类
public class WuLing implements Car{
@Override
public void drive () {
System.out.println("五菱宏光");
}
}
- Tesla 实现类
public class Tesla implements Car {
@Override
public void drive () {
System.out.println("特斯拉");
}
}
- 客户端
public class Client {
public static void main(String[] args) {
Car car1 = new WuLing();
Car car2 = new Tesla();
car1.drive ();
car2.drive ();
}
}
没有用工厂模式之前,我们创建对象需要通过new 来创建,如果创建对象的构造器比较复杂,需要传入很多数,创建对象的操作就会变得复杂,所以有了工厂模式。工厂模式可以屏蔽创建对象的细节,不会对客户端暴露创建对象的逻辑,解耦等优点;
简单工厂的代码实现
- Car的工厂类:
public class CarFactory {
//根据条件创建对应的对象
public static Car getCar(String car){
if(car.equals("五菱")){
return new WuLing();
} else if (car.equals("特斯拉")) {
return new Tesla();
}else {
return null;
}
}
}
- 客户端
public class Client {
public static void main(String[] args) {
//通过工厂方法创建对象
Car car1 = CarFactory.getCar("五菱");
Car car2 = CarFactory.getCar("特斯拉");
car1.drive ();
car2.drive ();
}
}
简单工厂模式的实现就是这样,那么现在如果新增一个车的类,CarFactory 工厂类就需要修改代码;
如果新增一个车的类
- 新增一个大众的车
public class Dazhong implements Car {
@Override
public void drive () {
System.out.println("大众");
}
}
- 修改后的工厂方法
public class CarFactory {
public static Car getCar(String car){
if(car.equals("五菱")){
return new WuLing();
} else if (car.equals("特斯拉")) {
return new Tesla();
}else if (car.equals("大众")){
return new Dazhong();
}else {
return null;
}
}
}
所以简单工厂模式是违背开闭原则(OCP)的,新增一个车,需要修改原来的代码;但在实际开发中,很多时候都用到了简单工厂模式,因为它简单,容易理解,所以很实用;
那有没有不违背OCP原则的方法,工厂方法模式可以做到,但要付出一定代价,会使类变多;
工厂方法模式
工厂方法模式需要定义一个工厂类的接口,为每一个车类创建一个实现工厂类接口的工厂类;
以上面的例子为例,还是有Car接口,WuLing和Tesla实现类;
工厂方法模式的代码实现
- car的接口和工厂接口
public interface Car {
void name();
}
public interface CarFactory {
Car getCar();
}
- WuLing 类和工厂类
//WuLing 对象
public class WuLing implements Car {
@Override
public void name() {
System.out.println("五菱宏光");
}
}
//WuLing工厂类
public class WuLingFactory implements CarFactory{
@Override
public Car getCar() {
return new WuLing();
}
}
- Tesla 类和工厂类
//Tesla 对象
public class Tesla implements Car {
@Override
public void name() {
System.out.println("特斯拉");
}
}
//Tesla工厂类
public class TeslaFactory implements CarFactory{
@Override
public Car getCar() {
return new Tesla();
}
}
- 客户端,通过不同的工厂方法创建对应的类
//简单工厂模式
public class Client {
public static void main(String[] args) {
Car car1 = new WuLingFactory().getCar();
Car car2 = new TeslaFactory().getCar();
car1.name();
car2.name();
}
}
工厂方法模式为每一个车类创建对应的工厂类,如果新增一个车的类,原来的代码不需要改动,但需要新增对应车工厂;
新增一个车的类
//Dazhong 对象
public class Dazhong implements Car{
@Override
public void name() {
System.out.println("大众");
}
}
//Dazhong工厂类
public class DazhongFactory implements CarFactory {
@Override
public Car getCar() {
return new Dazhong();
}
}
工厂方法模式遵守开闭原则(OCP原则),但每新增一个对象,都需要新增对应的工厂类方法,可能造成类爆炸;
工厂模式和工厂方法模式的优缺点
- 简单工厂模式:虽然某种程度上不符合OCP原则,但简单,容易理解,实际使用最多;
- 工厂方法模式:遵守OCP原则,不修改已有类的前提下,通过增加新的工厂类实现扩展;可能造成类爆炸;
工厂模式在 Calendar 应用的源码分析
JDK 中的 Calendar 日历类中,就使用了简单工厂模式
代码分析
- 日期实例
public class CalendarDemo {
public static void main(String[] args) {
// getInstance 是 Calendar 静态方法
Calendar cal = Calendar.getInstance();
// 注意月份下标从 0 开始,所以取月份要+1
System.out.println("年:" + cal.get(Calendar.YEAR));
System.out.println("月:" + (cal.get(Calendar.MONTH) + 1));
System.out.println("日:" + cal.get(Calendar.DAY_OF_MONTH));
System.out.println("时:" + cal.get(Calendar.HOUR_OF_DAY));
System.out.println("分:" + cal.get(Calendar.MINUTE));
System.out.println("秒:" + cal.get(Calendar.SECOND));
}
}
- getInstance 实例方法
public static Calendar getInstance() {
return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}
- createCalendar 方法,根据 TimeZone zone, locale 创建对应的实例对象
private static Calendar createCalendar(TimeZone zone, Locale aLocale){
CalendarProvider provider =
LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
.getCalendarProvider();
if (provider != null) {
try {
return provider.getInstance(zone, aLocale);
} catch (IllegalArgumentException iae) {
// fall back to the default instantiation
}
}
Calendar cal = null;
//根据不同的条件创建对应的实例
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) {
// If no known calendar type is explicitly specified,
// perform the traditional way to create a Calendar:
// create a BuddhistCalendar for th_TH locale,
// a JapaneseImperialCalendar for ja_JP_JP locale, or
// a GregorianCalendar for any other locales.
// NOTE: The language, country and variant strings are interned.
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;
}
工厂模式的应用场景
除了上面的Calendar 外,工厂模式还有一些常见的应用场景;
应用场景:
- JDK中Calendar的getlnstance方法
- JDBC中的Connection对象的获取
- Spring中IOC容器创建管理bean对象
- 反射中Class对象的newlnstance方法