工廠模式講解, 引入Spring IOC

引入

  • 假設有一個司機, 須要到某個城市, 因而咱們給他一輛汽車
public class Demo {
    public static void main(String[] args) {
        Car car = new Car();
        car.run();
    }
}

public class Car {
    public void run(){
        System.out.println("汽車正在向前跑...");
    }
}
複製代碼
  • 若是咱們但願給到這個司機的始終是一輛車, 應該怎麼作? (單例)
  • 首先咱們不能讓司機本身經過new產生一輛汽車, 而是應該經過調用Car類中的某個方法對外提供車.
public class Car {
    private static Car car = new Car();//用於提供給外界, 始終是同一輛車

    private Car(){};//私有構造方法, 在類以外不能經過new得到本類對象了, 保證了單例

    public Car getInstance(){
        return car;
    }

    public void run(){
        System.out.println("汽車正在向前跑...");
    }
}

public static void main(String[] args) {
    Car car = Car.getInstance();
    car.run();
}
複製代碼

 

簡單工廠

  • 下面考慮, 若是咱們不但願只有汽車這種交通工具, 咱們但願能夠定製交通工具, 並定製生產交通工具的流程, 應該怎麼作?
  • 一旦產生由汽車到交通工具這樣的概念, 就應該想到多態. 咱們能夠定義一個Moveable接口, 在接口中聲明run()方法, 全部的交通工具類都實現該接口.
  • 對於定製生產流程, 咱們能夠經過一個工廠進行生產對應的交通工具.
public interface Moveable {
    void run();
}

public class Car implements Moveable{

    public Car(){};//私有構造方法, 在類以外不能經過new得到本類對象了, 保證了單例

    public void run(){
        System.out.println("汽車正在向前跑...");
    }
}

public abstract class VehicleFactory {
    public abstract Moveable create();
}

public class CarFactory extends VehicleFactory {
    @Override
    public Moveable create() {
        return new Car();
    }
}

//Test
public static void main(String[] args) {
    VehicleFactory factory = new CarFactory();
    Moveable m = factory.create();
    m.run();
}
複製代碼

 

抽象工廠

  • 下面把簡單工廠的畫面從腦海中清空, 講述另外一種工廠實現.
  • 咱們假設開頭的司機不是一個普通的司機, 他除了須要一種交通工具以到達某個城市外, 他還須要一把AK47, 而且還須要一個蘋果以備路上不時之需.
  • 因此咱們須要給他一個工廠來製造這一系列產品.
  • 爲了提升可擴展性, 咱們還但願不一樣的工廠能夠製做不一樣系列的產品, 好比上面說的A工廠製造的是汽車, AK47, 蘋果; 而B工廠製造的是飛機, 火箭炮, 旺仔小饅頭.
//test
public static void main(String[] args) {
    AbstractFactory factory = new Factory1();
    Vehiche v = factory.createVehiche();
    Weapon w = factory.createWeapon();
    Food f = factory.createFood();

    v.run();
    w.fire();
    f.eat();
}

public abstract class Vehiche {//交通工具的抽象類
    public abstract void run();
}

public abstract class Weapon {//武器的抽象類
    public abstract void fire();
}

public abstract class Food {//食物的抽象類
    public abstract void eat();
}

public class Car extends Vehiche{一種具體的交通工具
    @Override
    public void run() {
        System.out.println("小汽車啓動...");
    }
}

public class AK47 extends Weapon {//一種具體的武器
    @Override
    public void fire() {
        System.out.println("噠噠噠...");
    }
}

public class Apple extends Food{//一種具體的食物
    @Override
    public void eat() {
        System.out.println("大口吃蘋果...");
    }
}

//抽象工廠
public abstract class AbstractFactory {
    public abstract Vehiche createVehiche();
    public abstract Weapon createWeapon();
    public abstract Food createFood();
}

//抽象工廠的實現1
public class Factory1 extends AbstractFactory {
    @Override
    public Vehiche createVehiche() {
        return new Car();
    }

    @Override
    public Weapon createWeapon() {
        return new AK47();
    }

    @Override
    public Food createFood() {
        return new Apple();
    }
}
複製代碼

 

  • 總結一下, 抽象工廠和簡單工廠各有什麼優劣?
  • 抽象工廠可以生產一系列產品, 也能方便地替換掉一系列產品, 可是若是想要在產品系列中添加多一個品種將會很是麻煩. 好比說在上面的系列產品中添加一個盔甲抽象類, 那麼抽象工廠以及對應的實現都要修改源碼了.
  • 而簡單工廠可以靈活的生產但一個品種的產品, 可是若是生產的品種較多, 會出現工廠氾濫的問題.
  • 二者優劣互補, 那麼有沒有能夠兼容二者優勢的工廠實現呢? 下面看spring的工廠實現, 它給出了一種解決方案.

 

Spring的bean工廠

  • 咱們再次考慮最原始的狀況, 有一個Moveable接口, 裏面有run方法, Car小汽車類實現了該接口.
public static void main(String[] args) {
    Moveable m = new Car();
    m.run();
}

public interface Moveable {
    void run();
}

public class Car implements Moveable{
    @Override
    public void run() {
        System.out.println("小汽車往前跑...");
    }
}
複製代碼
  • 在Spring的bean工廠中, 新對象不是經過new關鍵字獲取的, 而是經過配置文件獲取的.
  • 具體的過程是: 先讀取配置文件得到該類的class對象, 而後經過class對象建立具體的實例對象.
public static void main(String[] args) throws Exception {
    //獲取配置文件
    Properties props = new Properties();
    props.load(Test.class.getClassLoader().getResourceAsStream("spring.properties"));
    //獲取配置文件中配置的類
    String vehicheTypeName = props.getProperty("vehicheTypeName");
    //反射生成對應的對象
    Moveable m = (Moveable) Class.forName(vehicheTypeName).newInstance();
    m.run();
}

//spring.properties
vehicheTypeName=designPattern.factory.springFactory.Car
複製代碼

 

  • 上面是對spring中bean工廠使用的模擬, 下面咱們使用真實的spring來生成Car對象, 對比一下.
public static void main(String[] args) throws Exception {
    BeanFactory bf = new ClassPathXmlApplicationContext("applicationContext.xml");
    Vehiche v = (Vehiche)bf.getBean("v");
    v.run();
}

//配置文件
<bean id="v" class="designPattern.factory.Car">
</bean>
複製代碼
  • 通過對比咱們發現咱們本身寫的簡單工廠和spring的bean工廠在使用上沒有什麼區別, 確實spring使用起來就是這麼簡單, 下面咱們模擬一下spring的bean工廠實現.

 

模擬Spring工廠實現

模擬IOC

  • 都說spring是個bean容器, 如下的代碼將展現它是如何生成bean, 並把bean放入容器中供用戶獲取的.
  • 思路比較簡單:
  1. 建立BeanFactory工廠接口, 添加方法getBean().
  2. 建立BeanFactory的實現類ClassPathXmlApplicationContext. 將在該實現類中展現IOC的具體實現.
  3. ClassPathXmlApplicationContext須要一個container容器存放建立的bean對象, 這裏使用HashMap實現.
  4. ClassPathXmlApplicationContext的構造方法中讀取spring的配置文件, 這裏使用到了dom4j. 讀取配置文件後根據beanclass屬性使用反射建立出bean對象. 而後把idbean對象分別做爲keyvalue添加到容器中.
  5. 當工廠被調用getBean()方法時, 從容器中找到對應的bean並返回.
public static void main(String[] args) throws Exception {
    BeanFactory bf = new ClassPathXmlApplicationContext("applicationContext.xml");
    Vehiche v = (Vehiche) bf.getBean("v");
    v.run();
}

//BeanFactory的實現類
public class ClassPathXmlApplicationContext implements BeanFactory {

    private Map<String, Object> container = new HashMap<>();//用於存放bean對象的容器
    
    //在構造方法中讀取xml配置文件, 把bean對象都建立好並放入容器中
    public ClassPathXmlApplicationContext(String propAddr) throws Exception {
        SAXReader reader = new SAXReader();
        File file = new File(this.getClass().getClassLoader().getResource(propAddr).toURI());
        Document document = reader.read(file);
        Element root = document.getRootElement();
        List<Element> childElements = root.elements();

        for (Element child : childElements) {
            Object bean = Class.forName(child.attributeValue("class")).newInstance();
            container.put(child.attributeValue("id"), bean);
        }
    }

    @Override
    public Object getBean(String beanId) {
        return container.containsKey(beanId) ? container.get(beanId) : null;
    }
}

//極簡BeanFactory
public interface BeanFactory {
    Object getBean(String beanId);
}

//xml中配置的bean
<bean id="v" class="designPattern.factory.Car">
</bean>
複製代碼
相關文章
相關標籤/搜索