zoukankan      html  css  js  c++  java
  • 解耦技巧——依赖注入!

    加油.png加油.png

    在之前的一篇博客 Java Builder 模式,你搞懂了么? 中,我们提到了在 oop 编码设计中 只要能拿到类的实例,即对象,就能命令对象做一系列事情了。在 java/android 程序中,每个功能模块都由 n 个 类所组成,而每个类都封装了各自的功能,我们实例化每个类的实例,分别命令他们去完成指定的功能,但是,程序设计中,类之间往往不是单独去完成任务的,他们存在相互依赖的关系,就比如 A 类依赖 B 类,B 类又依赖 C 类,A 类 又同时依赖 C 类等等。这样构成了一个错综复杂的关系网。先来个简单代码直观的感受~

    还是以 制造 car 为例子。CarCreateTechnology 制造汽车的技术封装接口。

    1public interface CarCreateTechnology {
    2    String createCar();
    3}

    CarAutoCreate 制造汽车的技术封装接口的实现类,具体逻辑由他实现。

    1public class  CarAutoCreate implements CarCreateTechnology{
    2    @Override
    3    public String createCar() {
    4        return "自动生产的 car ~";
    5    }
    6}

    CarFactory 汽车工厂类:

     1public class CarFactory {
    2    private CarCreateTechnology mCreateTechnology;
    3
    4    public CarFactory() {
    5        mCreateTechnology = new CarAutoCreate();
    6    }
    7    public String createCar(){
    8        return mCreateTechnology.createCar();
    9    }
    10}

    最终 实例化 CarFactory 调用 createCar() 方法生产出 car
    String car = new CarFactory().createCar();哈哈,这样我们的 car 就生产好啦,貌似很简单嘛。

    嘟嘟~车开着正起劲的时候,有位客户说了,不喜欢自动生产的汽车,想请个专业人员整一辆定制 car。这时候有人说这还不简单,噼里啪啦的敲敲敲,代码修改如下:

    Creater:定制 car 的制作者:

     1public class Creater {
    2    private String name;//汽车制造者名字
    3    private String carType;//d定制 car 的类型
    4
    5    public Creater(String name, String carType) {
    6        this.name = name;
    7        this.carType = carType;
    8    }
    9    public String createCar(){
    10        return this.carType;
    11    }
    12}

    CarAutoCreate 制造汽车的技术封装接口的实现类:

     1public class CarAutoCreate implements CarCreateTechnology {
    2    private Creater mCreater;//定制 car 的制作者
    3
    4    public CarAutoCreate(Creater creater) {
    5        mCreater = creater;
    6    }
    7
    8    @Override
    9    public String createCar() {
    10        return mCreater.createCar();
    11    }
    12}

    CarFactory 汽车工厂类:

     1public class CarFactory {
    2    private CarCreateTechnology mCreateTechnology;
    3
    4    public CarFactory(Creater creater) {
    5        mCreateTechnology = new CarAutoCreate(creater);
    6    }
    7    public String createCar(){
    8        return mCreateTechnology.createCar();
    9    }
    10}

    最后 调用 String car = new CarFactory(new Creater("张少林", "宝马")).createCar();我们的定制 car "宝马" 就被制造出来啦。这样修改起来貌似挺简单的,但是存在以下一些问题

    分析问题

    • CarFactory 类 依赖 CarAutoCreate 类。需要在 CarFactory 中持有 CarAutoCreate 实例。而我们在 CarFactory 的构造中强行初始化了 CarAutoCreate,这就使得 CarFactory 与 CarAutoCreate 紧紧耦合在一起。这就是传说中的硬初始化(hard init)
    • 一旦 CarAutoCreate 的构造发生了变化,比如上面我们增加了 Creater .这使得 CarFactory 也要被迫做出修改。
    • 这种耦合度在程序相对简单的情况下,修改也就修改嘛,ide 打代码挺快的,但是一旦程序变得越来越复杂,依赖 CarAutoCreate 的地方非常多的情况下,修改起来就不辣么容易了,我们需要付出相当多的劳动力去修改代码,浪费时间不说,还很有可能因为修改失误而造成难以查找的错误,加大我们的工作量。

    知道了问题所在,我们来想办法解决,毕竟不喜欢偷懒的程序员不是好程序员,我们需要写出一手优雅的代码。

    解决问题——依赖注入

    java 中常见的依赖注入有三种:

    一、作为构造函数的参数注入

    我们修改一下 CarFactory 的构造,将 CarCreateTechnology 作为参数传入,在构造中初始化全局 CarCreateTechnology 。

     1public class CarFactory {
    2    private CarCreateTechnology mCreateTechnology;
    3
    4    public CarFactory(CarCreateTechnology createTechnology) {
    5        mCreateTechnology = createTechnology;
    6    }
    7
    8    public String createCar(){
    9        return mCreateTechnology.createCar();
    10    }
    11}

    调用:String car = new CarFactory(new CarAutoCreate(new Creater("张少林", "宝马"))).createCar();结果是一样的,定制 car 一样制造成功。但是不同的是:我们没有在 CarFactory 构造中直接构造所依赖的类(CarAutoCreate),而是通过传入形参,由最终调用方去初始化依赖类,这样就解决了之前的硬初始化问题,实现了解耦,提高了程序的可拓展性、可维护性。 眼毒的同学肯定发现了,我们的 CarAutoCreate 就是通过构造方法依赖注入所需的依赖(Creater)。

    二、提供 Setter 设置值注入

    CarFactory 添加 setxxx 注入全局变量。

     1public class CarFactory {
    2    private CarCreateTechnology mCreateTechnology;
    3
    4    public void setCreateTechnology(CarCreateTechnology createTechnology) {
    5        mCreateTechnology = createTechnology;
    6    }
    7
    8    public String createCar(){
    9        return mCreateTechnology.createCar();
    10    }
    11}

    调用:

    1CarFactory carFactory = new CarFactory(); 
    2 carFactory.setCreateTechnology(new CarAutoCreate(new Creater("张少林","宝马")));
    3 String car = carFactory.createCar();

    这种方法也是提供方法设置属性进去,同样也没有直接在 CarFactory 中直接实例化依赖类,而是最终调用的时候初始化设置进去,同样达到解耦目的。

    三、接口注入

    接口注入跟 Setter 注入类似,只是用接口包装了 Sertter 方法。这种方式易于管理。

    1public interface InjectTechnology {
    2    void injectCreate(CarCreateTechnology carCreateTechnology);
    3}

    CarFactory 实现接口。复写方法。在方法中注入。、

     1public class CarFactory implements InjectTechnology{
    2    private CarCreateTechnology mCreateTechnology;
    3
    4    public String createCar(){
    5        return mCreateTechnology.createCar();
    6    }
    7
    8    @Override
    9    public void injectCreate(CarCreateTechnology carCreateTechnology) {
    10        this.mCreateTechnology = carCreateTechnology;
    11    }
    12}

    "硬编码" 和 "软编码",图片来自网络

    编码方式区别.png编码方式区别.png

    最后,应该有的思考

    我们在 oop 编码设计中,应该尽可能的让依赖类在最上层初始化,在细节中杜绝硬编码。编写解耦,有可拓展性、可维护性的代码。

    更多原创文章会在公众号第一时间推送,欢迎扫码关注 张少林同学

    张少林同学.jpg张少林同学.jpg
  • 相关阅读:
    RPC 调用简述
    bootstrap tab切换无效的一种情况
    html块元素和内联元素介绍
    重装系统后,Linux虚拟机无法联网
    datatables.js表头挤在一列的解决办法
           ‌‍ 6种空白空格的区别
    pagination.js通过ajax请求获取数据
    谷歌浏览器调制,控制面板各命令总结
    点击表格弹窗获取另外一套数据之后,原表格相关数据的调用
    datatable columns与columnDefs
  • 原文地址:https://www.cnblogs.com/zhangshaolin/p/10172730.html
Copyright © 2011-2022 走看看