设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解并且保证代码可靠性。
本文主要介绍简单工厂模式及典型应用,内容如下:
工厂模式是最常用的一类创建型设计模式,包括 抽象工厂模式,工厂方法模式和简单工厂模式 这三种,简单工厂模式是其中最简单的一种
简单工厂模式(Simple Factory Pattern):定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。
因为在简单工厂模式中用于创建实例的方法是静态(static)方法,因此简单工厂模式又被称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式,但不属于GOF23种设计模式
Factory(工厂角色):工厂角色即工厂类,它是简单工厂模式的核心,负责实现创建所有产品实例的内部逻辑;工厂类可以被外界直接调用,创建所需的产品对象;在工厂类中提供了静态的工厂方法factoryMethod(),它的返回类型为抽象产品类型Product
Product(抽象产品角色):它是工厂类所创建的所有对象的父类,封装了各种产品对象的公有方法,它的引入将提高系统的灵活性,使得在工厂类中只需定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象。
ConcreteProduct(具体产品角色):它是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。每一个具体产品角色都继承了抽象产品角色,需要实现在抽象产品中声明的抽象方法
在简单工厂模式中,客户端通过工厂类来创建一个产品类的实例,而无须直接使用new关键字来创建对象,它是工厂模式家族中最简单的一员
抽象产品类 Video,定义了抽象方法 produce()
public abstract class Video {
public abstract void produce();
}具体产品类 JavaVideo 和 PythonVideo,都继承了抽象产品类 Video
public class JavaVideo extends Video {
@Override
public void produce() {
System.out.println("录制Java课程视频");
}
}
public class PythonVideo extends Video {
@Override
public void produce() {
System.out.println("录制Python课程视频");
}
}工厂类实现的两种方法:使用if-else判断和使用反射来创建对象
public class VideoFactory {
/**
* 使用if else 判断类型,type 为 Java 则返回 JavaVideo, type为Python则返回 PythonVideo
*/
public Video getVideo(String type) {
if ("java".equalsIgnoreCase(type)) {
return new JavaVideo();
} else if ("python".equalsIgnoreCase(type)) {
return new PythonVideo();
}
return null;
}
/**
* 使用反射来创建对象
*/
public Video getVideo(Class c) {
Video video = null;
try {
video = (Video) Class.forName(c.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return video;
}
}使用一个客户端来调用工厂类
public class Test {
public static void main(String[] args) {
VideoFactory videoFactory = new VideoFactory();
Video video1 = videoFactory.getVideo("python");
if (video1 == null) {
return;
}
video1.produce();
Video video2 = videoFactory.getVideo(JavaVideo.class);
if (video2 == null) {
return;
}
video2.produce();
}
}输出
录制Python课程视频
录制Java课程视频示例.简单工厂模式类图
Test 类通过传递参数给 VideoFactory.getVideo() 来获取对象,创建对象的逻辑交给了工厂类 VideoFactory 来完成
简单工厂模式的主要优点如下:
简单工厂模式的主要缺点如下:
适用场景:
Calendar 抽象类,该类的子类有 BuddhistCalendar、JapaneseImperialCalendar、GregorianCalendar、RollingCalendar等
getInstance方法,根据参数获取一个Calendar子类对象,该方法实际将参数传给 createCalendar 方法,createCalendar 在根据参数通过 provider 或 switch 或者 if-else 创建相应的子类对象
以下为 Java8 中的 Calendar 类代码,Java7 中的实现为 if-else 方式
public static Calendar getInstance(TimeZone zone, Locale aLocale) {
return createCalendar(zone, aLocale);
}
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) {
}
}
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 (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的继承关系
可以看到抽象产品角色和工厂角色都由 Calendar 担任,具体产品角色由 Calendar 的子类担任
一般JDBC获取MySQL连接的写法如下:
//加载MySql驱动
Class.forName("com.mysql.jdbc.Driver");
DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "123456");首先通过反射加载驱动类 com.mysql.jdbc.Driver 类,然后再通过 DriverManager 获取连接
看看 com.mysql.jdbc.Driver 的代码,该类主要的内容是静态代码块,其会随着类的加载一块执行
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}静态代码块:new 一个 Driver 类并注册到 DriverManager 驱动管理类中
public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException {
/* Register the driver if it has not already been added to our list */
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
throw new NullPointerException();
}
println("registerDriver: " + driver);
}其中的 registeredDrivers 是一个 CopyOnWriteArrayList 对象
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全且读操作无锁的ArrayList,写操作则通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可以称这种容器为"写时复制器",Java并发包中类似的容器还有CopyOnWriteSet 一篇CopyOnWriteArrayList的文章:https://www.cnblogs.com/chengxiao/p/6881974.html
再通过 DriverManager.getConnection 获取连接对象的主要代码如下:通过for循环从已注册的驱动中(registeredDrivers)获取驱动,尝试连接,成功则返回连接
private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException {
// ...省略...
println("DriverManager.getConnection(\"" + url + "\")");
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
// ...省略...
}Connection 接口及子类实现关系
工厂角色为 DriverManager 类,抽象产品角色为 Connection,具体产品角色则很多
查看 LoggerFactory 类的 getLogger 方法,可看到调用了 iLoggerFactory.getLogger(),其中 iLoggerFactory 是一个接口
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
public static Logger getLogger(Class clazz) {
return getLogger(clazz.getName());
}iLoggerFactory 接口只有一个 getLogger 方法
public interface ILoggerFactory {
Logger getLogger(String var1);
}查看其子类依赖关系
iLoggerFactory接口子类的依赖关系
再看一个子类 LoggerContext 对 ILoggerFactory 的实现
image
可看到这是通过 if-else 方式的简单工厂模式
Logger 接口及子类实现关系
工厂角色为 iLoggerFactory 接口的子类如 LoggerContext,抽象产品角色为 Logger,具体产品角色为 Logger 的子类,主要是 NOPLogger 和 Logger 类
下一篇介绍工厂方法及典型应用
参考: 刘伟:设计模式Java版 慕课网java设计模式精讲 Debug 方式+内存分析