IOC的优劣势
容器革新
在java&php的开发过程中存在一个定律:使用对象之前,必须先创建,但是在Laravel&Spring框架中我们往往通过IOC容器直接获取,而无需事先创建它们,这种技术变革,就如同我们无需考虑对象的销毁一样,因为Java&Php的垃圾回收机制会帮我们进行处理。
优点
- 自动依赖注入
- 实现组件解耦
- 提升程序的灵活性
缺点
- 对象生成,步骤复杂
- 使用反射生成对象,损耗运行效率
IOC的优势描述的太过于模糊和抽象,我们通过实现一个具体的需求,来进行了解
母亲通过故事书讲故事,哄孩子睡觉
原始实现
<?php
class Book{
public function content() : string{
return "重生之我在FS写代码....";
}
}
class Mother {
private $book;
public function __construct(Book $book) {
$this->book = $book;
}
public function story() : string{
return $this->book->content();
}
}
class Son {
private $mother;
public function __construct(Mother $mother)
{
$this->mother = $mother;
}
public function sleep() {
echo "听母亲讲" . $this->mother->storyTelling() . "慢慢入睡";
}
}
$book = new Book();
$mother = new Mother($book);
$son = new Son($mother);
$son->sleep();
IOC实现
<?php
class Book{
public function content() : string{
return "重生之我在FS写代码....";
}
}
class Mother {
private $book;
public function __construct(Book $book) {
$this->book = $book;
}
public function story() : string{
return $this->book->content();
}
}
class Son {
private $mother;
public function __construct(Mother $mother)
{
$this->mother = $mother;
}
public function sleep() {
echo "听母亲讲" . $this->mother->story() . "慢慢入睡";
}
}
Ioc::getInstance(Book::class);
Ioc::getInstance(Mother::class);
Ioc::make(Son::class,'sleep');
通过对比的方式,可以发现,在IOC实现中,我们不用在意类之间的依赖关系,只需将精力放置于业务上,容器会自动帮我们完成依赖注入
其余的两组优点我们可以通过下面的一组代码进行展示
需求:通过orderId 查询 order详情
//interface
<?php
namespace App\repository\interfaces;
interface OrderRepositoryInterface {
function details(int $orderId) : string;
}
//repository
<?php
namespace App\repository;
use App\repository\interfaces\OrderRepositoryInterface;
class DatabaseOrderRepository implements OrderRepositoryInterface {
function details(int $orderId): string
{
return "从数据库中获取订单数据";
}
}
//service
<?php
namespace App\Service;
use App\repository\interfaces\OrderRepositoryInterface;
class OrderService {
private $orderRepository;
public function __construct(OrderRepositoryInterface $repository) {
$this->orderRepository = $repository;
}
public function order(int $orderId): string
{
return $this->orderRepository->details($orderId);
}
}
//provider
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\repository\RedisOrderRepository;
use App\repository\DatabaseOrderRepository;
use App\repository\interfaces\OrderRepositoryInterface;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
// $this->app->bind(OrderRepositoryInterface::class,DatabaseOrderRepository::class);
$this->app->bind(OrderRepositoryInterface::class,RedisOrderRepository::class);
}
public function boot()
{
}
}
上面的代码我们可以知道:OderService 与 OrderRepository组件彼此相互独立,实现了组件的解耦。同样当需求发生变更后,我们可以更换细节,来实现需求,提升程序的灵活性。
当然,IOC也不是全是优点,它依然也存在缺点。其中比较大的一个就是,IOC采用反射来创建对象,会导致性能有一定的损耗,但是相对于优点,这个缺点可以忽略不计,因为0.00几秒的损耗,不会对我们的程序有过多的影响。
IOC简介
IOC是Inversion of Control 的缩写,即控制反转
- 上层模块不应该依赖下层模块,它们共同依赖一个抽象
- 抽象不能依赖具体的实现,具体实现依赖于抽象
又称为依赖倒置原则(DIP),这是设计模式的六大原则之一
这些概念讲的太过于深奥,我们依然采用需求加代码来进行分析
需求一:母亲给孩子讲故事,只要给她一本书,她就可以按照书的内容给孩子讲故事
原始实现
public class LearnApplication {
public static void main(String[] args) {
Mother mother = new Mother();
mother.story(new Book());
}
}
class Book {
public String getContent() {
return "重生之在FS写代码.....";
}
}
class Mother {
public void story(Book book) {
System.out.println("母亲开始讲故事了....");
System.out.println(book.getContent());
}
}
需求二:母亲给孩子讲故事,只要给她一份报纸,她就可以按照报纸的内容给孩子讲故事
当需求变更成报纸后,我们必须修改Mother类中的依赖项,才能让Mother拥有读报纸的能力。假如以后我们需求变更成杂志,网页等等,还需要不断的修改Mother类。显然这不是一个好的设计。原因就是:Mother与Book之间的耦合太高,必须降低它们之前耦合度才行。
DIP实现
public class MotherTwoApplication {
public static void main(String[] args) {
Mother mother = new Mother();
mother.story(new Book());
}
}
interface IReadr{
String getContent();
}
class Book implements IReadr{
@Override
public String getContent() {
return "书中:重生之在FS写代码.....";
}
}
class News implements IReadr {
@Override
public String getContent() {
return "报纸中:重生之在FS写代码.....";
}
}
class Mother {
public void story(IReadr readr) {
System.out.println("母亲开始讲故事了....");
System.out.println(readr.getContent());
}
}
这样修改后,无论以后怎样扩展Client类,都不需要再修改Mother类了。这只是一个简单的例子,实际情况中,代表高层模块的Mother类将负责完成主要的业务逻辑,一旦需要对它进行修改,引入错误的风险极大。所以遵循依赖倒置原则可以降低类之间的耦合性,提高系统的稳定性,降低修改程序造成的风险。
采用依赖倒置原则给多人并行开发带来了极大的便利,比如上例中,原本Mother类与Book类直接耦合时,Mother类必须等Book类编码完成后才可以进行编码,因为Mother类依赖于Book类。修改后的程序则可以同时开工,互不影响,因为Mother与Book类一点关系也没有。参与协作开发的人越多、项目越庞大,采用依赖导致原则的意义就越重大。
IOC的意义
Ioc不是什么技术,而是一种设计思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合,更优良的程序
传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了 IoC 容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
其实 IoC 对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在 IoC/DI 思想中,应用程序就变成被动的了,被动的等待 IoC 容器来创建并注入它所需要的资源了。
IoC 很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由 IoC 容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
IOC控制对象创建
- laravel
class GoodController extends Controller
{
public function good(int $goodId) {
return app()->get(GoodService::class)->good($goodId);
}
}
- springboot
@GetMapping("/order/{orderId}")
public String order(@PathVariable("orderId") Integer orderId) {
OrderService orderService = SpringUtil.getBean(OrderService.class);
return orderService.order(orderId);
}
IOC和DI
其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以 2004 年大师级人物 Martin Fowler 又给出了一个新的名字:“依赖注入”,相对 IoC 而言,“依赖注入”明确描述了“被注入对象依赖 IoC 容器配置依赖对象”
深入理解IOC
如何理解IOC呢?理解IOC的关键是要明确“谁控制了谁,控制了什么,为何是反转(有反转就应该有正转)那些方面反转了。
-
谁控制了谁?
在以前,对象的创建和销毁都是由用户控制的,用了ioc之后,对象的创建和销毁就都交给容器来控制了,用户就不用管这些,只关注业务需求就好了。
-
什么是反转?
既然叫反转,肯定就有正转,正转其实就是对象去找实例,而反转就反过来了嘛,让实例来找对象;怎么找呢?当然是通过容器了。
-
谁依赖谁?
在spring项目中,将对象理解为Bean,也可以叫bean对象,这个bean和容器之间有个依赖关系,bean对象的创建是依赖容器的,就好像孩子依赖父母一样,孩子不能自己生出自己,需要父母的合作才能出生,这里的孩子就是bean,父母就是容器;
-
谁注入谁?
通过容器注入了bean对象,而且这个过程是自动化的,也就是说容器会自动找到和bean对象匹配的类型实例注入到对象中
模拟Spring IOC实现
模拟一个Spring容器
不管是使用SpringBoot,还是SpringMVC,还是直接使用Spring,都需要创建一个容器,也就是ApplicatikonContext对象
参考:DIP详讲
标签:容器,Mother,public,mother,IOC,class From: https://www.cnblogs.com/ywjcqq/p/16800864.html