工厂模式讲解, 引入Spring IOC
程序员文章站
2022-04-14 18:12:55
[TOC] 引入 假设有一个司机, 需要到某个城市, 于是我们给他一辆汽车 如果我们希望给到这个司机的始终是一辆车, 应该怎么做? (单例) 首先我们不能让司机自己通过 产生一辆汽车, 而是应该通过调用 类中的某个方法对外提供车. 简单工厂 下面考虑, 如果我们不希望只有汽车这种交通工 ......
引入
- 假设有一个司机, 需要到某个城市, 于是我们给他一辆汽车
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
放入容器*用户获取的. - 思路比较简单:
- 创建
beanfactory
工厂接口, 添加方法getbean()
. - 创建
beanfactory
的实现类classpathxmlapplicationcontext
. 将在该实现类中展示ioc的具体实现. -
classpathxmlapplicationcontext
需要一个container
容器存放创建的bean对象, 这里使用hashmap
实现. - 在
classpathxmlapplicationcontext
的构造方法中读取spring
的配置文件, 这里使用到了dom4j
. 读取配置文件后根据bean
的class
属性使用反射创建出bean
对象. 然后把id
和bean
对象分别作为key
和value
添加到容器中. - 当工厂被调用
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>