写点什么

IOC 思想开窍之路

作者:留乘船
  • 2022 年 5 月 30 日
  • 本文字数:3712 字

    阅读完需:约 12 分钟

IOC思想开窍之路

大家好,最近小编我深入的对 java 进行学习。从类,接口,对象到现在接触 spring 技术。对于 spring 的理解可谓是一波三折,对于 spring 的入门思想 IOC 我也是昨天才开了窍。说到 IOC(控制反转)我从字面上理解就是你控制了某个事物然后那个事物又反转过来控制了你。那这种思想的目的是什么?为什么要这样做?我在网上查过各种资料,对于 ioc 理解大多是晦涩难懂的,像 IOC 容器对象托管之类的。找到最多的一句话就是:使用 ioc 技术在写 java 程序可以不用去 new 对象而是自动创建对象,然后就是那个本来一个 new 关键字就可以创建对象的变成了一大串什么 ApplicationContext 类以及写配置文件的方式创建了一个对象,老实说一开始我很不理解这些操作。直到昨天,我开窍了!当我理解 IOC 后可谓是兴奋了一个下午。下面我将结合生活中最常见的例子和朴素的语言带你理解 IOC 妙处!

下面我通过代码来讲述 IOC,注意这里只是涉及 IOC,前期没有用到一丝 spring 的代码(不会 spring 也没关系)。由于我是在餐馆吃饭的时候开了窍,就以开餐馆作为一个例子吧。说到开餐馆,赚钱的方式我想就是做出客户想要的菜然后将菜上给客户吧。


基于这个做菜我们来设计一个自动做菜的程序吧。打开 IDEA:


​ 这里我们建立两个目录,一个专门负责做菜的 Dao 目录(类似于后厨),一个负责接待客户的 Service 目录。在 Dao 层先建立一个 MenuBar 接口(菜单接口),然后编写一个方法就叫出菜。


package Dao;//建立一个菜单接口public interface MenuBar { //提供一个出菜的方法    public void outdish();}
复制代码


然后在 Dao 层处建立几个菜类,比如我这建立两个菜类(dish1 和 dish2)并且两个类都继承 MenuBar 接口


dish1:


package Dao;
public class dish1 implements MenuBar{ //出菜 public void outdish() { System.out.println("剁椒鱼头这到菜做好了"); }}
复制代码


dish2:


package Dao;//创建第二道菜继承菜单接口public class dish2 implements MenuBar{    //出菜    public void outdish() {        System.out.println("酸辣土豆丝这道菜做好了");    }}
复制代码


这是菜单接口(MenuBar)里就有两个菜类了如果要上到新菜就再编写一个类继承这个菜单接口(MenuBar).


接下来我们在建立一层 Service 层并建立一个 DishService 接口并建立一个方法负责将做好的菜送到客户那里就叫上菜吧。


package Service;//编写一个服务层接口,类似于服务员public interface DishService {    //上菜    public void putdishss();}
复制代码


在 Service 层建立一个接口实现类 DishServiceImpl 用于具体的上菜操作


package Service;import Dao.MenuBar;import Dao.dish1;
//编写服务层实现类public class DishServiceImpl implements DishService{ //引入MenuBar接口并以他的接口实现类创建一个menu对象 MenuBar menu = new dish1(); //继承上菜方法 public void putdishss() { //进行上菜操作,从后厨拿到菜 menu.outdish(); System.out.println("菜做好了,请享用");
}}
复制代码


对于上菜的操作服务员肯定是要从后厨拿到菜,所以我在这个是类中将 Dao 层的菜类引入,然后用 MenuBar 接口实现类创建的对象调用 outdish()实现拿到这道菜。这样就可以在服务层 DishService 接口实现类中调用上菜方法拿到这道菜了.


最后我们编写一个测试类在控制台上输出这道菜:


import Service.DishService;import Service.DishServiceImpl;//编写一个测试类进行上菜操作(面向用户的)public class DishTest {    public static void main(String[] args) {        //使用DishService接口实现类创建一个对象        DishService dser = new DishServiceImpl();        //使用该对象调用上菜的方法        dser.putdishss();    }}
复制代码


然后我们运行一下

我们通过这种层层调用的方法实现了上菜剁椒鱼头这道菜,大工告成。但是看完以上这种方式写代码,有没有看出什么问题。


​ 答案就是代码之间写死了不易改动。以上这种方法看懂的可以发现虽然可以上菜但是只能上一道菜那就是剁椒鱼头,当然不是所有的客户像我一样喜欢剁椒鱼头。比如如果客户说:“我不要剁椒鱼头我要酸辣土豆丝”。算了,改代码怎么改呢?当然是在服务层去改:


接下来我们修改 DishServiceImpl 类:


package Service;import Dao.MenuBar;import Dao.dish1;import Dao.dish2;
//编写服务层实现类public class DishServiceImpl implements DishService{ //用MenuBar的接口实现类ddish2()去创建对象 MenuBar menu = new dish2(); //MenuBar menu = new dish1(); //继承上菜方法 public void putdishss() { //进行上菜操作 menu.outdish(); System.out.println("菜做好了,请享用");
}}
复制代码


其他代码不变再运行一下:

修改一下 DishServiceImpl 类,因为 dish1 类提供的是剁椒鱼头这道菜,而提供酸辣土豆丝这道菜的方法在 dish2 类中。就用 dish2 创建一个对象。如图改好了!结合实际,客户需求可是经常改变的,点菜之后再换菜的这种例子在现实生活中屡见不鲜。这种方式对于这种只有一两个类的代码还好,顶多就是换一个单词的事。但是对于一个大型项目动不动就是几十个上百个类,难道你还要一个一个去改吗?很显然,这种设计不好!耦合性太强,那么我们就在改一下吧(注意,重要的部分来了)。


既然耦合性太强我们就尝试另一中方法,把接口的优势好好利用起来改动 DishServiceImpl 类;


package Service;import Dao.MenuBar;//编写服务层实现类public class DishServiceImpl implements DishService{    //引入菜单接口  private MenuBar menu;//添加一个set方法    public void setMenu(MenuBar menu) {        this.menu = menu;    }    //继承上菜方法    public void putdishss() {        //进行上菜操作       menu.outdish();       System.out.println("菜做好了,请享用");
}}
复制代码


然后再将测试类改一下:


import Dao.dish1;import Service.DishService;import Service.DishServiceImpl;//编写一个测试类进行上菜操作(面向用户的)public class DishTest {    public static void main(String[] args) {        //使用DishService接口实现类创建一个对象        DishService dser = new DishServiceImpl();        //经过强转的接口类对象调动set方法进行注入       ((DishServiceImpl)dser).setMenu(new dish1());        //使用该对象调用上菜的方法        dser.putdishss();    }}
复制代码


大家看好这个时候我们如果要换一道菜,比如改成酸辣土豆丝。就只需要在这个测试类里改动一行代码,


  ((DishServiceImpl)dser).setMenu(new dish2());
复制代码


在运行一下:

这种方式与上一种方式相比有什么不同?相信各位都懂了吧,上一种方法改变的是服务层里的代码,而这一次只是修改测试类里面的方法测试类与服务层里面的类我想不用我说大家就应该知道了吧:服务层里面的代码属于源码级别的,而测试类是我们随便写并且不作为源码进行封装的。而测试类的改变是随着用户的改变而改变的,这里就体现了我们的 IOC 思想,控制反转。由用户去根据需求去控制程序(源码可以不用动)。


回到开头,我们经常在网上搜到 ioc 容器,对象托管的 spring 技术。那这个 spring 技术到底是干嘛的呢?


答案就在这个测试类:


import Dao.dish1;import Service.DishService;import Service.DishServiceImpl;//编写一个测试类进行上菜操作(面向用户的)public class DishTest {    public static void main(String[] args) {        //使用DishService接口实现类创建一个对象        DishService dser = new DishServiceImpl();//这些new操作可以让spring容器去做        //经过强转的接口类对象调动set方法进行注入       ((DishServiceImpl)dser).setMenu(new dish1());//这些new操作可以让spring容器去做        //使用该对象调用上菜的方法        dser.putdishss();    }}
复制代码


​ 使用 spring 技术可以去除这些人为 new 对象的操作,虽然上述的升级改变,是在测试类改变。但是本质上还是在 new 的操作上改变。虽然不动源码但是还是动了代码。我们在这里仔细想想,我们究竟为什么而改变?是怎么样的思想去获得另一道菜"酸辣土豆丝"。说白了就是需要不同的对象,有了 dish1 的对象我们就可以调用出菜的方法成功输出“剁椒鱼头”,同理,有了 dish2 的对象就可以调用出菜的方法做出"酸辣土豆丝"。所以我们需要获得这些对象去调用相应的方法去迎合客户的需求。而创建对象的活如果使用 spring 技术就可以不用人为的创建对象了,spring 提供的 IOC 容器自动帮我们创建这些对象。


​ 这就是 IOC(控制反转)技术的奇妙之处!!不用去修改源码而让用户根据不同的需求拓展程序(这就是控制反转中的反转吧)。对于 spring 框架的两大技术 IOC 容器,和 AOP。使用过后你会发现两者的共同之处:在不改变源码的情况下拓展程序迎合客户的需求!这就是 spring 自从诞生以来一直活跃在 java 市场的原因吧。


​ 希望这边博文可以帮助你理解 IOC 思想,真的很有趣。先别走,关注我。下期教你如何使用 spring 技术去修改上述代码做到用户改变需求不用修改任何代码(包括测试类)!!!

发布于: 刚刚阅读数: 2
用户头像

留乘船

关注

还未添加个人签名 2022.05.23 加入

hello ,大家好!我是一名程序初学者。喜欢编程,希望在这个平台留下学习的脚印!一起加油呗!

评论

发布
暂无评论
IOC思想开窍之路_Java_留乘船_InfoQ写作社区