從shiro源碼角度學習工廠方法設計模式

緒論

shiro是一個簡單易用,功能強大的Java安全框架,學習其源碼設計思想對咱們的編碼水平的提升大有裨益。如今,就從源碼角度帶你們學習一下shiro裏面的工廠方法模式。 這裏的前提是讀者有過使用shiro的基礎,沒有也行,關鍵理解思想就能夠了。html

從一個簡單例子提及

首先,咱們先從一個簡單的例子提及。咱們在使用ini文件來做爲用戶角色權限配置的時候,咱們獲取SecurityManager的方法是:java

 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:demo01_getstarted.ini");
 
 SecurityManager securityManager = factory.getInstance();
複製代碼

上面兩行代碼看似簡單,其實框架底層就使用了工廠方法模式。
關於上面的例子就很少講了。這裏是側重分析工廠方法設計模式。設計模式

工廠方法模式

在工廠方法模式中,咱們對模式角色劃分爲四種:
一、抽象產品(產品接口):好比上面shiro實例中的SecurityManager
二、具體產品:好比實現了SecurityManager的類
三、抽象工廠(工廠接口):好比上面shiro實例中的Factory、AbstractFactory
四、具體工廠:好比上面shiro實例中的IniSecurityManagerFactory
安全

咱們先來看看shiro裏面工廠方法模式實現的源碼:
angular2

1、頂級工廠抽象接口Factory,全部抽象工廠類都繼承此接口
框架

public interface Factory<T> {
    T getInstance();
}
複製代碼

2、抽象工廠類,這個抽象工廠類負責獲取工廠實例,具體建立過程由其子類來實現
ide

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實現

1、建立工廠接口:IFactory IFactory.javaui

/**
 * 泛型表明的是產品類型
 *
 * @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();
       }
}


複製代碼

2、建立產品接口 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("我是接送客的車");
    }
}
複製代碼

3、建立具體產品的工廠類 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();
    }
}
複製代碼

4、客戶端代碼 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();
    }
複製代碼

添加一個產品,咱們就得添加產品類和工廠類。對於系統的擴展來講,工廠方法模式有優點,可是會增長系統的複雜度以及類的數量。

結束語

對於設計模式,你們重點是理解這樣設計的原理與優缺點,不要機械的背誦條條框框。實際咱們在開發真實系統時,會糅合多種設計模式在一塊兒。只有咱們對設計模式有本質性的認識和掌握,纔是真正掌握了設計模式。

相關文章
相關標籤/搜索