从shiro源码角度学习工厂方法设计模式

shiro是一个简单易用,功能强大的Java安全框架,学习其源码设计思想对我们的编码水平的提高大有裨益。现在,就从源码角度带大家学习一下shiro里面的工厂方法模式。 这里的前提是读者有过使用shiro的基础,没有也行,关键理解思想就可以了。

从一个简单例子说起

首先,我们先从一个简单的例子说起。我们在使用ini文件来作为用户角色权限配置的时候,我们获取SecurityManager的方法是:

 Factory 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 getInstance();
}

二、抽象工厂类,这个抽象工厂类负责获取工厂实例,具体创建过程由其子类来实现

public abstract class AbstractFactory implements Factory {

    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 extends AbstractFactory {

    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 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 getDefaults() {
        return defaultBeans;
    }

    public void setDefaults(Map 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 {

    /**
     * 获取产品
     *
     * @return 产品实例
     */
    T getInstance();

}

进一步抽象工厂接口 AbstractFactory.java

/**
 * @author wunanliang
 * @date 2018/1/8
 * @since 1.0.0
 */
public abstract class AbstractFactory implements IFactory {

   /**
        * 创建产品,具体创建过程由其子类实现
        *
        * @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 {

    @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 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 {
    @Override
    protected Ship createInstance() {
        return new FishShip();
    }

添加一个产品,我们就得添加产品类和工厂类。对于系统的扩展来说,工厂方法模式有优势,但是会增加系统的复杂度以及类的数量。

结束语

对于设计模式,大家重点是理解这样设计的原理与优缺点,不要机械的背诵条条框框。实际我们在开发真实系统时,会糅合多种设计模式在一起。只有我们对设计模式有本质性的认识和掌握,才是真正掌握了设计模式。

稀土掘金责编内容来自:稀土掘金 (源链) | 更多关于

阅读提示:酷辣虫无法对本内容的真实性提供任何保证,请自行验证并承担相关的风险与后果!
本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 综合编程 » 从shiro源码角度学习工厂方法设计模式

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录