内容简介:从shiro源码角度学习工厂方法设计模式
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(); }
添加一个产品,我们就得添加产品类和工厂类。对于系统的扩展来说,工厂方法模式有优势,但是会增加系统的复杂度以及类的数量。
结束语
对于设计模式,大家重点是理解这样设计的原理与优缺点,不要机械的背诵条条框框。实际我们在开发真实系统时,会糅合多种设计模式在一起。只有我们对设计模式有本质性的认识和掌握,才是真正掌握了设计模式。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Mybatis源码解读-设计模式总结
- OKHTTP3源码和设计模式(下篇)
- JDK源码——利用模板方法看设计模式
- 从shiro源码角度学习建造者设计模式
- 从源码角度理解Java设计模式——装饰者模式 原 荐
- 设计模式之发布订阅模式(5) Spring Events源码解析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
You Can Program in C++
Francis Glassborow / John Wiley & Sons / 2006-7 / 406.80元
An interactive and fun way to learn C++, one of the most popular high-level programming languages for graphic applications This unique, hands-on approach to learning C++ makes t......一起来看看 《You Can Program in C++》 这本书的介绍吧!