• 工厂模式-理解Spring的Bean工厂(马士兵经典例子)


    工厂模式-理解Spring的Bean工厂

    接面向对象里面 “老张开车去东北”的场景。链接名称

    封装“老张开车去东北”里面的交通工具,封装交通工具Car

    只给司机一辆车(单例、多例)

    顺带讲解单例

    要求只能有一辆车,别人不能new Car,只有Car自己能控制newCar的逻辑。私有化构造方法,别人就不能new了。

    /**
     * 交通工具Car
     * 
     */
    public class Car {
    	
    	//private static Car car = new Car();
    	private Car(){}
    	
    	public static Car getInstance(){
    		return new Car();
    	}
    	
    	public void run(){
    		System.out.println("冒着烟奔跑中...");
    	}
    
    }
    
    

    工厂就是自主生产自己的产品,不再依赖于new。比如你想new我家的一个抽屉,你想拿钱就拿钱, 肯定不行。

    但是我要给你提供一个方法:getChouTi(); 我就能在get方法里面做各种各样的限制了。

    比如返回Car的getInstance方法,可以做逻辑判断。

    /**
     * 交通工具Car
     */
    public class Car {
    
        private Car(){}
    
        public static Car getInstance(){
            if(有驾照){
                return new Car();
            }
            return null;
        }
        public void run(){
            System.out.println("冒着烟奔跑中...");
        }
    }
    
    

    再回到上面的要求,只有一辆车,这么做:自己new一个Car,调用getInstance时候,返回这个Car 。

    public class Car {
    	
    	private static Car car = new Car();
    	private Car(){}
    	
    	public static Car getInstance(){
    		return car;
    	}
    	
    	public void run(){
    		System.out.println("冒着烟奔跑中...");
    	}
    
    }
    
    

    测试

    getInstance两次看是不是一辆车:

    public static void main(String[] args) {
            Car car1 = Car.getInstance();
            Car car2 = Car.getInstance();
            System.err.println(car1 == car2);
        }
    

    打印:true

    打印true,说明是一辆车

    这个模式叫 单例,又有人叫这个getInstance方法叫静态工厂方法。

    任何方法,里面控制了产生对象的逻辑,都可以叫工厂方法。

    多例

    如果Car类里面返回的不是一个Car,里面有一个List装了一堆的Car,getInstance的时候随机返回一个,这个又有人起了个名字 叫---多例

    public class Car {
    	
    	//private static Car car = new Car();
    	private static List<Car> cars = new ArrayList<>();
    	
    	static{
    		//静态初始化cars
    		cars.add(new Car());
    		cars.add(new Car());
    	}
    	
    	private Car(){}
    	
    	public static Car getInstance(){
    		//return car;
    		//随机返回一个Car,这里就不随机了
    		return cars.get(1);
    	}
    	
    	public void run(){
    		System.out.println("冒着烟奔跑中...");
    	}
    
    }
    
    

    JDBC连接池,里面装的Connection,就是多例。

    任意定制交通工具的类型和生产过程

    自然就想起了多态,抽取一个接口:Moveable,然后让Car实现Moveable接口:

    public interface Moveable {
    	void run();
    }
    

    Car的实现:

    public class Car implements Moveable{
    	@Override
    	public void run() {
    		System.out.println("冒着烟奔跑中...");
    	}
    }
    

    飞机的实现:

    public class Plane implements Moveable{
    
    	@Override
    	public void run() {
    		System.out.println("扇着翅膀飞呀飞...");
    	}
    }
    

    测试类:

    调用的时候,父类引用指向子类对象,多态,我new谁,就调用的是谁,很随意就换了交通工具:

    Moveable m = new Car();
    m.run();
    m = new Plane();
    m.run();
    打印结果:
    冒着烟奔跑中...
    扇着翅膀飞呀飞...
    
    

    还有其他任何交通工具 比如交通工具是哈利波特的扫帚,就直接实现Moveable接口,你就可以直接new Broom(); 了 。

    现在存在的问题就是,可以任意的new 交通工具,构造方法是公开的。现在想对任意交通工具的生产过程也能够定制的话。有了上面单例的思路,现在第一个想到的还是,在交通工具类里面写一个静态的方法控制new 的过程。这里比如飞机,把产生飞机的过程单独一个类拿出来,比如叫飞机工厂PlaneFactory:

    //飞机工厂类
    public class PlaneFactory {
    	public Plane createPlane(){
    		//单例、多例、条件检查自己控制
    		return new Plane();
    	}
    }
    

    测试 :

    //飞机工厂
    		PlaneFactory factory = new PlaneFactory();
    		Moveable m = factory.createPlane();
    		m.run();
    
    打印结果:
    扇着翅膀飞呀飞...
    

    如果现在想有一个Car工厂,那么很简单,就是这样:

    //Car工厂类
    public class CarFactory {
    	public Car createPlane(){
    		//单例、多例、条件检查自己控制
    		return new Car();
    	}
    }
    

    测试代码:

    以前是开着飞机,现在想换成开Car,需要把飞机工厂换成了Car工厂,调用他的createCar方法。这样太别扭了。

    //飞机工厂
    		//PlaneFactory factory = new PlaneFactory();
    		//换成Car工厂,整个工厂方法都得换
    		CarFactory factory = new CarFactory();
    		Moveable m = factory.createCar();
    		m.run();
    

    有没有什么办法,从飞机换成Car的时候,只换工厂的实现就行呢?自然就想起了多态,有多态就得有父类、子类。所以工厂类需要有一个父类。Factory本来是产生交通工具的,抽象出一个产生交通工具的工厂:

    //交通工具工厂
    public abstract class VehicleFactory {
    
    	//具体生成什么交通工具由子类决定,这里是抽象的。
    	public abstract Moveable create();
    }
    

    这时候让CarFactory和PlaneFactory去继承VehicleFactory :

    //Car工厂类
    public class CarFactory extends VehicleFactory{
    	
    	@Override
    	public Moveable create() {
    		//单例、多例、条件检查自己控制
    		return new Car();
    	}
    }
    //飞机工厂类
    public class PlaneFactory extends VehicleFactory {
    	
    	@Override
    	public Moveable create() {
    		//单例、多例、条件检查自己控制
    		return new Plane();
    	}
    }
    

    换了工厂的实现,就可以换交通工具了,比如你加了一个哈利波特的魔法扫帚,需要加一个Broom类实现Moveable接口,和一个BroomFactory工厂类继承VehicleFactory就可以了。

    //扫帚
    public class Broom implements Moveable{
    
    	@Override
    	public void run() {
    		System.out.println("扫帚摇着尾巴呼呼呼...");
    	}
    }
    
    //扫帚工厂类
    public class BroomFactory extends VehicleFactory {
    	
    	@Override
    	public Moveable create() {
    		//单例、多例、条件检查自己控制
    		return new Broom();
    	}
    }
    
    

    此时测试代码就成了这样子:

    //机车工厂,new飞机工厂实例
    		VehicleFactory factory = new PlaneFactory();
    		Moveable m = factory.create();
    		m.run();
    		//换成Car工厂
    		factory = new CarFactory();
    		m = factory.create();
    		m.run();
    		//换成扫帚工厂
    		factory = new BroomFactory();
    		m = factory.create();
    		m.run();
    打印结果;
    扇着翅膀飞呀飞...
    冒着烟奔跑中...
    扫帚摇着尾巴呼呼呼...
    
    

    在某一个维度上有了可扩展了,不仅可以控制产生交通工具的类型,还可以控制产生交通工具的生产过程。需要改的只有一个地方:要是需要换交通工具,站在客户角度,需要改的只有交通工具的工厂,其他地方都不用动。如果用了配置文件的话,代码都不用该,只改配置文件即可。后面再说。

    关于抽象类和接口的选择:

    比如上面的交通工具工厂,是一个抽象类,也可以设计成接口,么问题。

    假如这个概念在我们脑子是确确实实存在的,就用抽象类,

    假如这个概念只是某些方面的特性:比如会飞的,会跑的,就用接口

    假如两个概念模糊的时候,不知道选择哪个的时候,就用接口,原因是,从实现了这个接口后,还能从其它的抽象类继承,更灵活。

    抽象工厂

    看一下JDK里面,先看getInstance方法,有一大堆,不同类里面有同样的方法getInstance。

    这些大多数都是静态的工厂方法,是不是单例不一定,得看具体的实现。

    各种各样的Factory也很多。像下面的加密的key,就不适合new,用一个工厂去实现它,产生的时候可以实现各种各种算法,检测各种资质,new的话构造方法只能是写死的,用工厂的话,实现一个子类的时候还可以控制生产过程,更灵活。

    总而言之,getInstance和Factory,JDK里面很常用。下面开始说抽象工厂。

    回到最原始的状态,我们有一辆车Car。

    控制一系列的产品(车、武器、食品补给)

    现在让这个人,开着车,拿着AK47,吃着苹果。 意思就是,这是一些列的产品,要控制这一些列的产品的生产。

    比如你要装修,海尔整体厨房,有微波炉,油烟机,洗衣机,电磁炉。一系列的产品。

    public class Car{
    	
    	public void run() {
    		System.out.println("冒着烟奔跑中...");
    	}
    }
    
    public class AK47 {
    
    	public void shoot(){
    		System.out.print("哒哒哒....");
    	}
    }
    public class Apple {
    	public void getName(){
    		System.out.println("Apple..."); 
    	}
    }
    
    

    测试类:

    Car car = new Car();
    		car.run();
    		AK47 ak = new AK47();
    		ak.shoot();
    		Apple apple = new Apple();
    		apple.getName();
    打印:
    冒着烟奔跑中...
    哒哒哒....
    Apple...
    

    产生这一系列的产品,需要有一个默认的工厂:

    //默认的工厂
    public class DefaultFactory {
    
    	public Car createCar(){
    		return new Car();
    	}
    	public AK47 createAK47(){
    		return new AK47();
    	}
    	public Apple createApple(){
    		return new Apple();
    	}
    }
    
    

    此时的测试程序,只要new出来一个默认工厂,就可以生产这一系列的产品了:

    DefaultFactory factory = new DefaultFactory();
    		Car car = factory.createCar();
    		car.run();
    		AK47 ak = factory.createAK47();
    		ak.shoot();
    		Apple apple = factory.createApple();
    		apple.getName();
    打印:
    冒着烟奔跑中...
    哒哒哒....
    Apple...
    
    

    当需要吧这一系列产品全换掉的话,把这个工厂换掉就可以了:

    这里新建一个工厂,魔法工厂:

    //哈利波特的魔法工厂
    public class MagicFactory {
    
    	//交通工具:扫把
    	public Broom createBroom(){
    		return new Broom();
    	}
    	
    	//武器:魔法棒
    	public MagicStick createMagicStick(){
    		return new MagicStick();
    	}
    	//食物:毒蘑菇
    	public MushRoom createMushRoom(){
    		return new MushRoom();
    	}
    }
    
    //扫帚
    public class Broom{
    
    	public void run() {
    		System.out.println("扫帚摇着尾巴呼呼呼...");
    	}
    }
    
    
    //武器:魔法棒
    public class MagicStick {
    }
    
    //食物:毒蘑菇
    public class MushRoom {
    
    }
    
    

    但是此时,站在客户的角度,想把工厂从DefaultFactory换到MagicFactory:

    有了之前的经验,这里自然就联想到,工厂创建的不能是具体的类,要是一个接口/或者是一个抽象类,比如你的Car,工厂创建的不能是Car,要是Car的父类,这样在换工厂的时候,下面的代码才可以不用改动。所以,我们要建一个抽象工厂,然后让DefaultFactory、MagicFactory都去继承/实现这个工厂。而且工厂的返回值,都是抽象类或者接口。

    //抽象工厂
    public abstract class AbstractFactory {
    	//生产 交通工具
    	public abstract Vehicle createVehicle();
    	//生产 武器
    	public abstract Weapon createWeapon();
    	//生产食物
    	public abstract Food createFood();
    }
    
    
    //交通工具
    public abstract class Vehicle {
    	//实现由子类决定
    	public abstract void run();
    }
    
    //食物
    public abstract class Food {
    	public abstract void printName();
    }
    
    
    //武器
    public abstract class Weapon {
    	//
    	public abstract void shoot();
    }
    

    产品类:都继承产品的抽象类

    public class Car extends Vehicle{
    	@Override
    	public void run() {
    		System.out.println("冒着烟奔跑中...");
    	}
    }
    //扫帚
    public class Broom extends Vehicle{
    
    	@Override
    	public void run() {
    		System.out.println("扫帚摇着尾巴呼呼呼...");
    	}
    }
    //食物:毒蘑菇
    public class MushRoom extends Food {
    
    	@Override
    	public void printName() {
    		System.out.println("mushroom");
    	}
    
    }
    
    public class Apple extends Food {
    	@Override
    	public void printName() {
    		System.out.println("apple");
    	}
    }
    
    public class AK47 extends Weapon{
    
    	public void shoot(){
    		System.out.println("哒哒哒....");
    	}
    }
    
    //武器:魔法棒
    public class MagicStick extends Weapon {
    	@Override
    	public void shoot() {
    		System.out.println("fire hu hu hu ...");
    	}
    
    }
    
    

    魔法工厂继承抽象工厂:

    //哈利波特的魔法工厂
    public class MagicFactory extends AbstractFactory {
    
    	//交通工具:扫把
    	public Vehicle createVehicle(){
    		return new Broom();
    	}
    	
    	//武器:魔法棒
    	public Weapon createWeapon(){
    		return new MagicStick();
    	}
    	//食物:毒蘑菇
    	public Food createFood(){
    		return new MushRoom();
    	}
    }
    
    

    DefaultFactory继承抽象工厂:

    //默认的工厂
    public class DefaultFactory extends AbstractFactory{
    
    	@Override
    	public Food createFood() {
    		return new Apple();
    	}
    
    	@Override
    	public Vehicle createVehicle() {
    		return new Car();
    	}
    
    	@Override
    	public Weapon createWeapon() {
    		return new AK47();
    	}
    }
    
    

    最终形成的类结构是这样的:

    测试程序:

    //换一个工厂,只需要改动这一处,就可以了,换一个工厂,就把生产的系列产品都换了
    		AbstractFactory factory =  new MagicFactory(); //new DefaultFactory();
    		//换一个工厂
    		Vehicle vehicle = factory.createVehicle();
    		vehicle.run();
    		Weapon weapon = factory.createWeapon();
    		weapon.shoot();
    		Food food = factory.createFood();
    		food.printName();
    	
    DefaultFactory打印:
    冒着烟奔跑中...
    哒哒哒....
    apple
    

    **把工厂换为****MagicFactory

    AbstractFactory factory = new DefaultFactory();

    打印:

    扫帚摇着尾巴呼呼呼...

    fire hu hu hu ...

    mushroom

    抽象工厂:生产一系列的产品,如果你想换掉一系列的产品,或者你想在这一系列的产品上进行扩展,以及想对这一些列产品的生成过程进行控制,用抽象工厂。

    抽象工厂和普通工厂的优缺点:

    普通工厂:

    可以在产品的维度上进行扩展,可以产生新的产品,可以产生新的产品的工厂。也就是说可以在产品的维度进行扩展。

    在普通工厂想要产生产品系列,就会特别麻烦。产生一个产品,就会出现一个产品的Factory。就会出现”工厂泛滥”。

    抽象工厂:

    能换工厂,生产新的产品系列,但是不能产生新的产品品种,新添加一个产品,就要在抽象工厂里面加 createXXX(); 方法,所有子类都要实现这个方法。要改动的地方太多。

    有没有一种工厂,结合普通工厂和抽象工厂的有点呢?

    既可以随意添加产品品种,又很方便的添加产品系列? 没有。

    Spring提供了一种方案,Spring的Bean工厂。

    Spring说,你就不要这么Moveable m = new Car(); 就行new Car了,你给配置到配置文件里。

    测试java读取properties配置文件反射生成对象

    spring.properties:
    
    VehicleType=com.lhy.springfactory.Car
    

    测试代码:

    public static void main(String[] args) throws Exception{
    	Properties props = new Properties();
    		props.load(Test.class.getClassLoader().getResourceAsStream("com/lhy/springfactory/spring.properties"));
    
    		String vehicleTypeName = props.getProperty("VehicleType");
    		System.out.println(vehicleTypeName);
    		//反射生成对象
    		Object o = Class.forName(vehicleTypeName).newInstance();
    		Moveable m = (Moveable)o;
    		m.run();
    	}
    打印结果:
    com.lhy.springfactory.Car
    冒着烟奔跑中...
    
    

    把配置文件换成或者Trian:

    VehicleType=com.lhy.springfactory.Train
    

    执行测试代码,打印:

    com.lhy.springfactory.Train

    小火车呜呜呜...

    可以看到,只是改了配置文件,就可以动态控制生成的类了。代码都不用动。Spring就是这样的思路。

    最简单的使用Spring:

    引入必须的jar包,

    Spring要求的配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    
      <bean id="v" class="com.bjsxt.spring.factory.Train">
      </bean>
      
      <!--  //v=com.bjsxt.spring.factory.Car  -->
    
    
    </beans>
    
    

    测试程序:

    package com.bjsxt.spring.factory;
    
    import java.io.IOException;
    
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class Test {
    
    	/**
    	 * @param args
    	 * @throws IOException 
    	 */
    	public static void main(String[] args) throws Exception {
    		BeanFactory f = new ClassPathXmlApplicationContext("applicationContext.xml");
    		Object o = f.getBean("v");
    		Moveable m = (Moveable)o;
    		m.run();
    	}
    
    }
    配置文件配置的火车类,打印:
    小火车呜呜呜...
    换成Car,打印:
    冒着烟奔跑中...
    
    

    下面来模拟Spring的Bean工厂

    Spring的BeanFactory ,就是一个容器,是用一个map实现的,就是从配置文件读取 <bean id="v" class="com.bjsxt.spring.factory.Train"/> 这样的配置,遍历解析这样的xml配置,以id为key,class后的类全限定名用反射生成的对象为value,放到这个map中去。当用的时候,直接map.get(id); 获取这个Bean对象。

    ClassPathXmlApplicationContext是BeanFactory的一种实现。这里模拟这种实现。

    ​ 这里模拟Spring的Bean工厂:

    public interface BeanFactory {
    
    	Object getBean(String id);
    }
    
    

    ClassPathXmlApplicationContext:

    public class ClassPathXmlApplicationContext implements BeanFactory{
    	//存放一个个Bean对象的容器,
    	private Map<String, Object> container = new HashMap<String, Object>();
    	
    	// 构造方法找到配置文件,读取xml配置文件
    	public ClassPathXmlApplicationContext(String fileName) throws Exception{
    		SAXBuilder sb = new SAXBuilder();
    		Document doc = sb.build(this.getClass().getClassLoader()
    				.getResourceAsStream(fileName));
    		Element root = doc.getRootElement();
    		List list = XPath.selectNodes(root, "/beans/bean");
    		System.out.println(list.size());
    
    		for (int i = 0; i < list.size(); i++) {
    			Element bean = (Element) list.get(i);
    			String id = bean.getAttributeValue("id");
    			String clazz = bean.getAttributeValue("class");
    			Object o = Class.forName(clazz).newInstance();
    			container.put(id, o);
    			System.out.println(id + " " + clazz);
    		}
    	}
    
    	//读取配置文件,读取id为传进来的id的Bean,实例化
    	@Override
    	public Object getBean(String id) {
    		return container.get(id);
    	}
    
    	
    }
    
    

    测试程序:

    public static void main(String[] args) throws Exception{
    		
    		BeanFactory f = new ClassPathXmlApplicationContext("com/lhy/springfactory/applicationContext.xml");
    		Object o = f.getBean("v");
    		Moveable m = (Moveable)o;
    		m.run();
    		
    		Train trian = (Train)f.getBean("trian");
    		trian.run();
    		
    	}
    
    

    applicationContext.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans>
      <bean id="v" class="com.lhy.springfactory.Car"/>
    	<bean id="trian" class="com.lhy.springfactory.Train"/>
      <!--  //v=com.bjsxt.spring.factory.Car  -->
    </beans>
    
    

    打印:

    2

    v com.lhy.springfactory.Car

    trian com.lhy.springfactory.Train

    冒着烟奔跑中...

    小火车呜呜呜...

    这样,就把类配置在了配置文件里,更灵活了!

  • 相关阅读:
    C#获取Excel Sheet名称,对特殊字符、重名进行了处理
    10个你必须知道的jQueryMobile代码片段
    HTML 5 学习之应用程序缓存
    JS取地址栏参数的两种方法
    关于AJAX+HTML5+ASHX进行全静态页面的数据交互
    重病后的重生
    非常值得学习的java 绘图板源代码
    C#开发者通用性代码审查清单
    【week3】四人小组项目—东师论坛
    【week2】结对编程-四则运算 及感想
  • 原文地址:https://www.cnblogs.com/lihaoyang/p/13720163.html
Copyright © 2020-2023  润新知