zoukankan      html  css  js  c++  java
  • 工厂模式讲解, 引入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>
    
  • 相关阅读:
    《时间的朋友》2017跨年演讲:观后感
    如何实现目标?
    不患贫而患不均
    科学地定目标(附我的目标)
    书单:我的“基本书”
    你只有非常努力,才能看起来毫不费力
    买书?买知识?
    论知识与技能
    【读书笔记】《代码不朽》
    【工作实践】如何创新?
  • 原文地址:https://www.cnblogs.com/tanshaoshenghao/p/10770659.html
Copyright © 2011-2022 走看看