当前位置: 首页 > 后端技术 > Java

讨论:从面向抽象编程到IOC控制反转

时间:2023-04-01 14:42:58 Java

1。WHAT-Exampleofabstract-orientedprogramming1.1面向对象一般来说,面向对象编程对我们来说比较常见,就是把一个实际的项目分成多个类(对象),给这些对象赋予属性和方法来实现编程。比如我们要写一个校园管理平台,分别管理老师和学生。很直观的,我们可以对老师和同学进行分类。教师有教学功能(方法)和性别年龄(属性),而学生有阅读功能(方法)也有性别和年龄(属性)。1.2面向对象进阶到面向抽象在我看来,面向对象其实就是:归纳和总结。我们程序员对产品经理提出的直观需求进行汇总,我们可以得到汇总的类别。如果我们可以进一步概括(或抽象)一个归纳类,我们就有了一个抽象类。在同一个例子中,除了教师类和学生类之外,我们还可以泛化出一个“人”类。这个“人”类具有一个人应有的属性(如性别、年龄、身高、体重)和能力(如吃饭、睡觉等)。).(P.S:当然,总结一个抽象类只是抽象的一部分,不是全部。)2.WHY-为什么我们要为抽象编程2.1代码编写在代码编写层面,相信有编程经验的人experiencedwillorExperiencedmoreorless:把通用的部分提取出来,这样就不用重复写代码了。比如组件和包,我们不需要写极其重复的代码。2.2代码维护在代码编写层面,更多的是面向对象的思想在起作用。对于一个项目,我们正常的思维往往是自上而下的,所以我们先总结一个抽象类,然后再得到一个具体类。这是正常的思维逻辑。这样写出来的代码层次分明,但还是远远不够。一个大型项目,难的往往不是它的开发,而是它的维护。在大型项目中,即使我们将类总结为抽象类的形式,类与类之间仍然存在非常强的依赖关系。举个栗子:我们的A类被抽象成了IA类,但是我们还是要实例化A类,而实例化A类的启动类B仍然引用A,启动类B和A之间的依赖关系依然存在。(java中的多态性)2.2.1开闭原则(OCP)首先,为了实现更好的代码维护,必须遵循开闭原则:允许扩展,禁止修改。见上图,上图中的四个齿轮就是我们的四个类,它们相互依赖,带动程序的运行。但是有一天,产品经理突然提出了一个新的需求。这个需求可以通过直接修改A类来实现,但是一旦A类发生变化,我们的B类和D类也必须跟着变化,然后连C类也可能需要。随它而动,这样的变化是灾难性的。可见遵循开闭原则并不是一件容易的事,在设计代码框架时必须想清楚这一点。那么如何才能做到开闭原则呢?2.2.2对于抽象编程,我们需要明白变化(产品经理)是存在的,是不可磨灭的。不可能只扩充代码,根本不修改。因此,遵循OCP原则,我们应该做的是:1.将代码变更隔离到一个地方。2.减少类之间的相关性,让每个类都可以独立改变,不影响其他类的使用。面向抽象的编程将这些变化抽象化,减少了类之间的关联。它包括三个方面:1、用变量来抽象“值”的变化;2、使用Interface接口抽象“类”的变化;3.使用容器封装类的实例化,然后使用反射机制抽象类的实例化。2.2.3工厂模式抽象编程的第一个方面其实在日常编程中是很常用的,比如写配置文件,这里就不赘述了。第二点,如前所述,就是用一个抽象类来实现多个不同的具体类。具体在java中,以多态的形式引用类。fufu=newZi1()//newZi2()最重要的是第三点:用容器来封装类的实例化。在类已经被抽象出来的前提下,类的实例化成为类与类之间相互依赖的关键。正是因为一个类在另一个类中被直接实例化,所以这两个类是耦合的。如上图,还是四个档位,不过这次我在四个档位之间加了一个大档位。以后不再是小齿轮互相推动,而是大齿轮带动四个小齿轮转动。显然,小齿轮之间的相关性已大大降低。无论是改装小齿轮内部还是更换小齿轮,风险都比原来的小很多。回到代码上,这个大齿轮其实就是一个容器,这个容器就是用来实现类之间的关联的。要实现这个容器,我们可以使用工厂模式+反射机制。使用具体代码实现工厂模式+反射机制:假设我们需要做一个简单的英雄联盟选择英雄,释放R技能的demo。简单工厂模式://主控类publicclassMain{publicstaticvoidmain(String[]args)throwsException{Stringname=Main.getPlayerInput();英雄hero=HeroFactory.getHero(name);英雄.r();}privatestaticStringgetPlayerInput(){System.out.println("EnteraHero'sName");扫描仪扫描仪=新扫描仪(System.in);字符串名称=scanner.nextLine();返回名称;}}//工厂类publicclassHeroFactory{publicstaticHerogetHero(Stringname)throwsException{Herohero;开关(名称){案例“戴安娜”:英雄=新戴安娜();休息;case"Irelia":hero=newIrelia();休息;案例“卡米尔”:英雄=新卡米尔();休息;默认值:抛出新的异常();返回英雄;factory来生成它,已经封装好了,但是我们每次添加英雄,我们都需要改变这个工厂,这个工厂显然不够资格作为我们的“大齿轮”,它连接了各种英雄类,直接修改它仍然影响链接的风险。(我觉得这个例子可能不太有说服力,因为各个英雄职业之间的关联性不是很大,看来改装厂问题不大,只能直接从齿轮上解释了。我不不知道你有没有更新。很好的例子--.)。工厂模式+反射机制:publicstaticHerogetHero(Stringname)throwsException{StringclassStr="reflect.hero."+姓名;类cla=Class.forName(classStr);对象obj=cla.newInstance();return(Hero)obj;}简单工厂模式,每次都需要改动是因为实例化过程还没有抽象出来,如果加上java的反射机制就不一样了,类的实例化完全依赖于hero用户输入的名称(或配置文件中的英雄名称)。无论我们添加任何新英雄还是调整任何英雄,工厂都不需要改变。显然,这个工厂基本实现了我们图中的“大齿轮”。同时我们也可以看出,这么大的齿轮还是很不错的。控制代码运行的类与实现业务逻辑的类分离---控制代码:MAIN+FACTORY;业务代码:各英雄类DIANA+IRELIA+CAMILE。因此,我们可以换一种方式来理解OCP原则:尽量不要修改负责控制的代码,只修改业务逻辑的代码。3.IOC控制反转假设有一个A类需要引用B类,那么一般来说,我们会在A类中实例化B类;而在引入一个容器的时候,预先将A类和B类引入到容器中,然后容器将B类注入到A类中,实现了IOC控制反转。从这个概念上看,工厂模式+反射机制,这种模式好像实现了IOC控制反转(将hero类注入到控制类中)。但实际上,生成类的工厂并不像上面的例子那么简单。可能需要根据某些特定的条件来判断注入哪个类,还需要考虑如何实例化该类:是否有参数,参数可能会发生变化。换句话说,我们的工厂不可能是静态的。既然工厂里还有这么繁琐的业务逻辑改动,我们自然不能直接把工厂放到控制代码里(也就是直接引用HEROFACTORY)。同时,工厂模式+反射虽然隔离了类的实例化,但依然没有偏离正向思维:控制类(MAIN)需要英雄类(HERO)在工厂中生成我们想要的英雄.我们希望的是:容器一开始就把英雄类注入到控制类中,也就是IOC控制反转。因此,我们需要Springboot框架帮我们扫描类生成bean,然后将bean注入到对应的控制类中。这里只是想展开一下,下次再记录下自己对Springboot框架的理解。我自己的疑问:1.工厂模式+反射已经解决了控件类不变的问题,那么Spring框架相对于它有什么优势呢?因为Spring框架还需要先把类生成bean添加到IOC容器中,多了这么一个步骤,所以感觉比工厂模式+反射要麻烦一些。