初步理解IOC和DI和AOP模式
初步理解IOC和DI和AOP模式
控制反转(IOC)
控制反转(IOC,Inversion of Control)是一种转主动为被动关系的一种编程模式,有点类似于工厂模式,举个栗子,
下面这个这不是IOC模式
Interface interface{ // 啥也不写 } class A implements interface{ String name; } class B implements interface{ Strng name; } class C{ interface AorB; public C(){ AorB = new A(); } }
在上面在这个例子,类C依赖于类A或类B。
这种方法有很多缺点,比如,如果类A的定义改了,比如说
第一点:增加了一个构造方法来动态设定name属性,那么那类A的定义,类C的实现,全部都要改。
第二点:如果要更改类C的实现,比如临时更改类C中的依赖的接口AorB的实现为类B,这个时候就要重新更改代码,将类C改为:
class C{ interface AorB; public C(){ AorB = new B(); } }
然后重新编译,重新运行,系统才能重新运作,特别麻烦。
要知道,在大型应用开发中,一个类依赖的可能不仅仅是一两个类,可能是一两百个,或者是几千个类,这个时候如果其中一个类要更改,则这几千个类的源码都要重新修改重新编译,
知乎上有个大神用了个比喻来描述这种情况:
需求是造一辆汽车。
从最基础的轮子开始造起,此时底盘是依赖于轮子的,车身依赖于底盘,整个汽车又依赖于车身,而底盘的具体形状是依赖于轮子的大小的。
试想一下,当你造的差不多的时候,客户跟你说,他觉得轮子大一点显得气派,要你把轮子造的大一圈。
而你的底盘是依赖于轮子的,所以你的底盘也要重新设计,车身是依赖于底盘的,所以你的车身也要重新设计,换句话说,你因为轮子的变动,不得不把整个汽车重新设计一遍。
很痛苦。
假定你要在轮子类的实现里面增加一个构造函数来动态的修改的他的大小,那么你就要在依赖于他的底盘类中也增加一个构造函数,然后,又要在车身类中增加一个构造函数。
形如下面的修改(红色是修改部分):
// 轮子 class Tire(){ private int size; public Tire(int size){ this.size = size; } } // 底盘类 class Bottom { private Tire tire; // 依赖于轮子 public Bottom(int size) { tire = new Tire(size); } } // 车身类 class Framework { private Bottom bottom; public Framework(int size) { bottom = new Bottom(size); } } // 汽车类
可以看到,这基本就是牵一发而动全身的趋势。
运用IOC模式可以避免这种情况的发生,IOC模式让我们不主动创建类,创建类的任务交给IOC容器来做,换句话说,类中的属性,也完全不需要我们手动进行赋值,IOC容器会自动将这些属性用xml规定好的内容赋上(也就是依赖注入)。同时,创建的对象的生命周期也完全由IOC容器进行管理,而不用我们进行销毁啊,关闭啊之类的行动(只管用就行了= =)。
下面使用IOC容器(Spring)重做轮子,底盘,车身类
先编写xml
beans.xml
<?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=”tire” class=”car.Tire”> <property name=”size” value=4 /> </bean> <!-- 底盘类 --> <bean id=”bottom” class=”car.Bottom”> <property name=”tire”> <!-- 依赖轮子类 --> <ref bean=”tire” /> </property> </bean> <!-- 车身类 --> <bean id=”framework” class=”car.Framework”> <property name=”bottom”> <!-- 依赖轮子类 --> <ref bean=”bottom” /> </property> </bean> <!-- 汽车类 --> </beans>
下面编写bean,轮子类,底盘类,车身类
class Tire{ private int size; // 大小 size : set方法 } class Bottom{ private Tire tire; tire : set方法 } class Framework { private Bottom bottom; bottom:set方法 }
要用这汽车类的时候,像下面这样编写。
// 编写测试类 class Test { public static void main(String[] args){ // 得到IOC容器 BeanFactory beanfactory = new XmlBeanFactory(new ClassPathResource(“beans.xml”)); // 直接从容器里面拿车,而不需要自己手动new创建 Car car = (Car)beanfactory.getBean(“car”); } }
依赖注入(DI)
我觉得依赖注入算是实现控制反转的一个方法,抽象一点来说,IOC是一种设计模式,而DI就是实现这种设计模式的一种方法,常见的依赖注入的方法有下面这三种。
- 使用set方法注入属性
- 使用构造器注入属性
- 接口类型(这种我没看懂)
面向切面编程(AOP)
AOP我用的还不熟,现阶段感觉就是python里面的装饰器啊。。。。一堆听不懂的名词。。。。
认真的讲,AOP解决的问题是一个切面的问题,用书上的栗子来说明AOP的好处。
目前有一个任务,要你计算一个班上的学生的成绩的平均数,学生成绩放在一个数据库里
一般实现这个程序只需要三个步骤:
- 拿到班上所有同学的成绩
- 成绩相加/同学的总数
- 得到平均数
但实际上编程序的时候远远不止这三步,因为实际情况下,你要打开数据库,然后这个打开数据库的途中可以出现很多异常(数据库服务器没开,表名错了,连错数据库等等),这些我们都要一一解决,最后,得到平均数,关闭数据库的时候,也会有很多情况发生(各种SQL语句错误),这个时候,代码里面就会多了很多跟我们的业务逻辑不相关的内容(打开,关闭数据库,处理数据库异常),看起来不好看,同时代码耦合,改起来麻烦。
面向切面编程(AOP)解决了这个问题,他可以在链接数据库那个地方创建一个连接点,然后连接点处可以编写代码,专门解决这些数据库链接的问题,嗯,这听起来有点像在前面写个函数解决问题的。。。。但实际上不是这样的,用py的装饰器举个例子:
现在有一个函数,用循环从1加到10000.
def add():
result = 0
for i in range(10000):
result += i
return result
现在有一个需求,就是计算这个函数的运行时间。
一般来说,就是在函数里面写个什么time.now(),然后在最后也来个time.now(),然后相减,得到运行时间,使用AOP模式不需要更改原本函数的一丝一毫的代码,下面是AOP模式(装饰器模式,Spring里面是代理模式实现AOP模式)
def getTime(func):
def addfunction(*arg,*kargs):
t1 = time.now()
result = func(*arg,*kargs)
t2 = time.now()
print(‘运行时间是:’+(t2-t2))
return result
return addfunction
没有对原本函数进行修改,达到了获得函数运行时间的效果,书上说这就是解决了横切关注点和调用者和被调用者之间的解耦问题。