绪论
shiro是一个简单易用,功能强大的Java安全框架,学习其源码设计思想对我们的编码水平的提高大有裨益。现在,就从源码角度带大家学习一下shiro里面的工厂方法模式。 这里的前提是读者有过使用shiro的基础,没有也行,关键理解思想就可以了。
从一个简单例子说起
首先,我们先从一个简单的例子说起。我们在使用ini文件来作为用户角色权限配置的时候,我们获取SecurityManager的方法是:
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:demo01_getstarted.ini");
SecurityManager securityManager = factory.getInstance();
复制代码
上面两行代码看似简单,其实框架底层就使用了工厂方法模式。
关于上面的例子就不多讲了。这里是侧重分析工厂方法设计模式。
工厂方法模式
在工厂方法模式中,我们对模式角色划分为四种:
1、抽象产品(产品接口):比如上面shiro实例中的SecurityManager
2、具体产品:比如实现了SecurityManager的类
3、抽象工厂(工厂接口):比如上面shiro实例中的Factory、AbstractFactory
4、具体工厂:比如上面shiro实例中的IniSecurityManagerFactory
我们先来看看shiro里面工厂方法模式实现的源码:
一、顶级工厂抽象接口Factory,所有抽象工厂类都继承此接口
public interface Factory<T> {
T getInstance();
}
复制代码
二、抽象工厂类,这个抽象工厂类负责获取工厂实例,具体创建过程由其子类来实现
public abstract class AbstractFactory<T> implements Factory<T> {
private boolean singleton;
private T singletonInstance;
public AbstractFactory() {
this.singleton = true;
}
public boolean isSingleton() {
return singleton;
}
public void setSingleton(boolean singleton) {
this.singleton = singleton;
}
// 获取工厂实例,可以以单例形式获取,也可以每一次获取都创建一个实例
public T getInstance() {
T instance;
if (isSingleton()) {
if (this.singletonInstance == null) {
this.singletonInstance = createInstance();
}
instance = this.singletonInstance;
} else {
instance = createInstance();
}
if (instance == null) {
String msg = "Factory 'createInstance' implementation returned a null object.";
throw new IllegalStateException(msg);
}
return instance;
}
protected abstract T createInstance();
}
复制代码
上面两个工厂类抽象了最基本的工厂接口--创建工厂、获取工厂。如果我们需要对工厂类进行扩展的话,只需要继承AbstractFactory来实现即可,非常方便。现在看一下AbstractFactory的一个子类。
IniFactorySupport是一个特定的抽象工厂类,是根据ini文件来创建工厂实例的工厂抽象类。我们不需要细究IniFactorySupport代码干了什么。只需要明白,它是对根据ini文件创建工厂做了一些逻辑处理就好了。
我们可以看到,继承AbstractFactory,我们可以随便扩展定制我们工厂类的行为。
public abstract class IniFactorySupport<T> extends AbstractFactory<T> {
public static final String DEFAULT_INI_RESOURCE_PATH = "classpath:shiro.ini";
private static transient final Logger log = LoggerFactory.getLogger(IniFactorySupport.class);
private Ini ini;
private Map<String, ?> defaultBeans;
protected IniFactorySupport() {
}
protected IniFactorySupport(Ini ini) {
this.ini = ini;
}
public Ini getIni() {
return ini;
}
public void setIni(Ini ini) {
this.ini = ini;
}
protected Map<String, ?> getDefaults() {
return defaultBeans;
}
public void setDefaults(Map<String, ?> defaultBeans) {
this.defaultBeans = defaultBeans;
}
public static Ini loadDefaultClassPathIni() {
Ini ini = null;
if (ResourceUtils.resourceExists(DEFAULT_INI_RESOURCE_PATH)) {
log.debug("Found shiro.ini at the root of the classpath.");
ini = new Ini();
ini.loadFromPath(DEFAULT_INI_RESOURCE_PATH);
if (CollectionUtils.isEmpty(ini)) {
log.warn("shiro.ini found at the root of the classpath, but it did not contain any data.");
}
}
return ini;
}
protected Ini resolveIni() {
Ini ini = getIni();
if (CollectionUtils.isEmpty(ini)) {
log.debug("Null or empty Ini instance. Falling back to the default {} file.", DEFAULT_INI_RESOURCE_PATH);
ini = loadDefaultClassPathIni();
}
return ini;
}
public T createInstance() {
Ini ini = resolveIni();
T instance;
if (CollectionUtils.isEmpty(ini)) {
log.debug("No populated Ini available. Creating a default instance.");
instance = createDefaultInstance();
if (instance == null) {
String msg = getClass().getName() + " implementation did not return a default instance in " +
"the event of a null/empty Ini configuration. This is required to support the " +
"Factory interface. Please check your implementation.";
throw new IllegalStateException(msg);
}
} else {
log.debug("Creating instance from Ini [" + ini + "]");
instance = createInstance(ini);
if (instance == null) {
String msg = getClass().getName() + " implementation did not return a constructed instance from " +
"the createInstance(Ini) method implementation.";
throw new IllegalStateException(msg);
}
}
return instance;
}
protected abstract T createInstance(Ini ini);
protected abstract T createDefaultInstance();
}
复制代码
通过看类关系图,IniSecurityManagerFactory继承IniFactorySupport,在IniFactorySupport基础上面进一步封装创建工厂过程。
IniSecurityManagerFactory的源码就不贴出来了,明白设计思想就可以了。
通过源码分析,我们可以看到,首先抽象出最基本的工厂接口,具体的工厂类由其子类去实现。一个具体工厂类对应这一类产品。当需要新增产品类的时候,我们只需要新加工厂类,并且新增对应的产品类即可,不需要修改原有工厂类代码,符合了设计模式中的开闭原则、单一职责原则。
demo实现
一、创建工厂接口:IFactory IFactory.java
/**
* 泛型代表的是产品类型
*
* @author wunanliang
* @date 2018/1/8
* @since 1.0.0
*/
public interface IFactory<T> {
/**
* 获取产品
*
* @return 产品实例
*/
T getInstance();
}
复制代码
进一步抽象工厂接口 AbstractFactory.java
/**
* @author wunanliang
* @date 2018/1/8
* @since 1.0.0
*/
public abstract class AbstractFactory<T> implements IFactory<T> {
/**
* 创建产品,具体创建过程由其子类实现
*
* @return 创建的产品实例
*/
protected abstract T createInstance();
@Override
public T getInstance() {
return createInstance();
}
}
复制代码
二、创建产品接口 IProduct.java
/**
* @author wunanliang
* @date 2018/1/8
* @since 1.0.0
*/
public interface IProduct {
}
复制代码
进一步分类产品,创建一类产品接口 Car.java
/**
* @author wunanliang
* @date 2018/1/8
* @since 1.0.0
*/
public abstract class Car implements IProduct {
/**
* 创建汽车产品
*
* @return 创建的汽车产品
*/
protected abstract Car createCar();
/**
* 驾驶汽车
*/
public abstract void drive();
}
复制代码
具体产品类 Taxi.java
/**
* @author wunanliang
* @date 2018/1/8
* @since 1.0.0
*/
public class Taxi extends Car {
private Taxi taxi;
@Override
protected Car createCar() {
this.taxi = new Taxi();
return this.taxi;
}
@Override
public void drive() {
System.out.println("我是接送客的车");
}
}
复制代码
三、创建具体产品的工厂类 TaxtFactory.java
/**
* @author wunanliang
* @date 2018/1/8
* @since 1.0.0
*/
public class TaxiFactory extends AbstractFactory<Car> {
@Override
protected Car createInstance() {
return new Taxi();
}
}
复制代码
四、客户端代码 Client.java
/**
* @author wunanliang
* @date 2018/1/8
* @since 1.0.0
*/
public class Clent {
public static void main(String[] args) {
IFactory<Car> factory = new TaxiFactory();
Car taxi = factory.getInstance();
taxi.drive();
}
}
复制代码
通过例子,我们知道,在工厂方法模式中,有一个顶级的产品接口,对产品作出做基本的抽象,然后产品下面还有不同产品的分类,在同一类产品中又有不同的具体产品,比如car类产品下面又会有多种汽车产品。每一个具体的产品都有对应一个具体的工厂类。 如果想再新加一个新的产品,不论是car类产品,还是非car类产品,我们都可以通过新加工厂类和产品类来实现,比如新增一个船类产品
Ship.java
/**
* @author wunanliang
* @date 2018/1/8
* @since 1.0.0
*/
public abstract class Ship implements IProduct {
/**
* 造船
*
* @return
*/
protected abstract IProduct createShip();
public abstract void doSomething();
}
复制代码
创建渔船 FishShip.java
/**
* 渔船
*
* @author wunanliang
* @date 2018/1/8
* @since 1.0.0
*/
public class FishShip extends Ship {
private FishShip ship;
@Override
public IProduct createShip() {
this.ship = new FishShip();
return this.ship;
}
@Override
public void doSomething() {
System.out.println("我在打鱼呀");
}
}
复制代码
创建渔船工厂类 FishShipFactory.java
/**
* @author wunanliang
* @date 2018/1/8
* @since 1.0.0
*/
public class FishShipFactory extends AbstractFactory<Ship> {
@Override
protected Ship createInstance() {
return new FishShip();
}
复制代码
添加一个产品,我们就得添加产品类和工厂类。对于系统的扩展来说,工厂方法模式有优势,但是会增加系统的复杂度以及类的数量。
结束语
对于设计模式,大家重点是理解这样设计的原理与优缺点,不要机械的背诵条条框框。实际我们在开发真实系统时,会糅合多种设计模式在一起。只有我们对设计模式有本质性的认识和掌握,才是真正掌握了设计模式。