引入

  • 假设有一个司机, 需要到某个城市, 于是我们给他一辆汽车
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>

工厂模式讲解, 引入Spring IOC的更多相关文章

  1. Java工厂模式解耦 —— 理解Spring IOC

    Java工厂模式解耦 -- 理解Spring IOC 最近看到一个很好的思想来理解Spring IOC,故记录下来. 资源获取方式 主动式:(要什么资源都自己创建) 被动式:(资源的获取不是我们创建, ...

  2. 工厂模式如何返回Spring的Bean

    工厂返回的可以是一个具体的对象,比如造一辆车,可以返回一个自行车对象,或者汽车对象. 但是在Spring 中需要工厂返回一个具体的Service,这就是一个抽象工厂了 一种方法是反射,个人觉得这种方式 ...

  3. Spring IOC 方式结合TESTGN测试用例,测试简单java的命令模式

    java命令模式: 可以命令("请求")封装成一个对象,一个命令对象通过在特定的接收着上绑定一组动作来封装一个请求.命令对象直接把执行动作和接收者包进对象中,只对外暴露出执行方法的 ...

  4. 深入理解Spring IOC容器

    本文将从纯xml模式.xml和注解结合.纯注解的方式讲解Spring IOC容器的配置和相关应用. 纯XML模式 实例化Bean的三种方式: 使用无参构造函数 默认情况下,会使用反射调用无参构造函数来 ...

  5. 深入理解Spring IOC容器及扩展

    本文将从纯xml模式.xml和注解结合.纯注解的方式讲解Spring IOC容器的配置和相关应用. 纯XML模式 实例化Bean的三种方式: 使用无参构造函数 默认情况下,会使用反射调用无参构造函数来 ...

  6. Spring:源码解读Spring IOC原理

    Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. I ...

  7. Spring IOC原理解读 面试必读

    Spring源码解析:Bean实例的创建与初始化 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. IoC容器 ...

  8. Spring源码解读Spring IOC原理

    一.什么是Ioc/DI? IoC 容器:最主要是完成了完成对象的创建和依赖的管理注入等等. 先从我们自己设计这样一个视角来考虑: 所谓控制反转,就是把原先我们代码里面需要实现的对象创建.依赖的代码,反 ...

  9. Spring IOC设计原理解析:本文乃学习整理参考而来

    Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. I ...

随机推荐

  1. 深度学习之 TensorFlow(四):卷积神经网络

    基础概念: 卷积神经网络(CNN):属于人工神经网络的一种,它的权值共享的网络结构显著降低了模型的复杂度,减少了权值的数量.卷积神经网络不像传统的识别算法一样,需要对数据进行特征提取和数据重建,可以直 ...

  2. oracle的事务级别

    ooracle的事务级别是不提交的,如果在sql语句中插入数据,如果不提交(commit).在程序里面试读不出来数据的.长时间不用oracle竟然忘了这些东西,特此记下.方便以后查看

  3. PAT1055:The World's Richest

    1055. The World's Richest (25) 时间限制 400 ms 内存限制 128000 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue ...

  4. git - 简明指南

    助你入门 git 的简明指南,木有高深内容 ;) 作者:罗杰·杜德勒 感谢:@tfnico, @fhd 和 Namics如有纰漏,请在 github 提报问题 安装 下载 git OSX 版 下载 g ...

  5. 许式伟:我与Go语言的这十年[转]

    2017-12-18 许式伟 Go中国 2007 年 9 月 20 日,关于设计一门全新语言的讨论正式开始,这门全新的语言,就是后来的 Go.时至今日,Go 语言已经发布到 1.9 版本,走过了整整十 ...

  6. Day1 《机器学习》第一章学习笔记

    <机器学习>这本书算是很好的一本了解机器学习知识的一本入门书籍吧,是南京大学周志华老师所著的鸿篇大作,很早就听闻周老师大名了,算是国内机器学习领域少数的大牛了吧,刚好研究生做这个方向相关的 ...

  7. 什么是设计思维Design Thinking——风靡全球的创造力培养方法

    “把学习带到现实中,让孩子用自己的力量创造改变,可以直接提升他们的幸福感和竞争力.” 这是“全球孩童创意行动”的发起人——Kiran Sethi在TED演讲时说的一句话,这个行动旨在引导中小学生主动寻 ...

  8. Jmeter运行后出现乱码

    1.响应结果出现乱码一般是编码的问题,汉子乱码在编码处编码写成utf-8 2.如果还不行,对jmeter的文件进行修改.具体修改方法参考https://blog.csdn.net/liu5781821 ...

  9. 你不知道的JavaScript--Item17 循环与prototype最后的几点小tips

    1.优先使用数组而不是Object类型来表示有顺序的集合 ECMAScript标准并没有规定对JavaScript的Object类型中的属性的存储顺序. 但是在使用for..in循环对Object中的 ...

  10. Sublime Text 3 Build 3065 License key 注册码 秘钥

    -– BEGIN LICENSE -– Andrew Weber Single User License EA7E-855605 813A03DD 5E4AD9E6 6C0EEB94 BC99798F ...