18 | 理论四:接口隔离原则有哪三种应用?原则中的“接口”该如何理解?

2019-12-13 王争
《设计模式之美》
课程介绍


讲述:冯永吉

时长:大小12.77M


上几节课中,我们学习了 SOLID 原则中的单一职责原则、开闭原则和里式替换原则,今天我们学习第四个原则,接口隔离原则。它对应 SOLID 中的英文字母“I”。对于这个原则,最关键就是理解其中“接口”的含义。那针对“接口”,不同的理解方式,对应在原则上也有不同的解读方式。除此之外,接口隔离原则跟我们之前讲到的单一职责原则还有点儿类似,所以今天我也会具体讲一下它们之间的区别和联系。
话不多说,现在就让我们正式开始今天的学习吧!

如何理解“接口隔离原则”?

接口隔离原则的英文翻译是“ Interface Segregation Principle”,缩写为 ISP。Robert Martin 在 SOLID 原则中是这样定义它的:“Clients should not be forced to depend upon interfaces that they do not use。”直译成中文的话就是:客户端不应该被强迫依赖它不需要的接口。其中的“客户端”,可以理解为接口的调用者或者使用者。
实际上,“接口”这个名词可以用在很多场合中。生活中我们...

展开全文
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。

精选留言

  • 辣么大
    2019-12-13
    Java.util.concurrent.atomic包下提供了机器底层级别实现的多线程环境下原子操作,相比自己实现类似的功能更加高效。
    AtomicInteger提供了
    intValue() 获取当前值
    incrementAndGet() 相当于++i
    getAndIncrement相当于i++
    从getAndIncrement实现“原子”操作的角度上来说,原子级别的给整数加一,返回未加一之前的值。它的职责是明确的,是符合单一职责的。

    从接口隔离原则上看,也是符合的,因为AtomicInteger封装了原子级别的整数操作。

    补充:
    多线程环境下如果需要计数的话不需旧的值时,推荐使用LongAdder或者LongAccumulator(CoreJava上说更加高效,但我对比了AtomicLong和LongAdder,没感觉效率上有提高,可能是例子写的不够准确。测试代码见 https://github.com/gdhucoder/Algorithms4/tree/master/designpattern/u18 希望和小伙伴们一起讨论)
    展开
    13
    116
  • 李小四
    2019-12-13
    设计模式_18
    纯理论分析,这么设计是不符合“接口隔离”原则的,毕竟,get是一个操作,increment是另一个操作。

    结合具体场景,Atomic类的设计目的是保证操作的原子性,专门看了一下AtomicInteger的源码,发现没有单独的 increment 方法,然后思考了一下线程同步时的问题,场景需要保证 get 与 increment 中间不插入其他操作,否则函数的正确性无法保证,从场景的角度,它又是符合原则的。
    展开
    7
    79
  • Geek_e9b8c4
    2019-12-13
    总结成思维导图了,链接 https://blog.csdn.net/dingshuo168/article/details/103531805
    
    29
  • 码到成功
    2019-12-13
    老师可以每次课对上一次课的思考题做下解答吗

    作者回复: 集中答疑一下吧 课都提前录好了

    1
    28
  • NoAsk
    2019-12-13
    单一职责原则针对的是模块、类、接口的设计。getAnd
    Increase()虽然集合了获取和增加两个功能,但是它作为对atomicInteger的值的常用方法,提供对其值的常规操作,是满足单一原则的。

    从单一原则的下面这个解释考虑,是不满足接口隔离原则的。“如果调用者只使用部分接口或接口的部分功能,那接口的设计就不够职责单一。”,用户可能调用获取或增加的其中一个方法,再或者先调用增加再调用获取increaseAndGet()方法。

    这是我个人理解,还望大家指正。
    展开
    1
    16
  • 小晏子
    2019-12-13
    思考题:
    先看是否符合单一职责原则,这个函数的功能是加1然后返回之前的值,做了两件事,是不符合单一职责原则的!
    但是却符合接口隔离原则,从调用者的角度来看的话,因为这个类是Atomic类,需要的所有操作都是原子的,所以为了满足调用者需要原子性的完成加一返回的操作,提供一个这样的接口是必要的,满足接口隔离原则。
    
    14
  • 方小白
    2019-12-13
    符合SRP 也符合ISP 。

    理由是这个方法完成的逻辑就是一个功能:新增和返回旧值。只不过是两步操作罢了。如果你想获取,就用get方法,自增就用increment 方法。都有提供哇。

    SRP:老师在文中说,实际上,要从中做出选择,我们不能脱离具体的应用场景。所以我认为是符合的。

    ISP: 可以参考老师说的这句话:而接口隔离原则相对于单一职责原则,一方面它更侧重于接口的设计,另一方面它的思考的角度不同。它提供了一种判断接口是否职责单一的标准:通过调用者如何使用接口来间接地判定。如果调用者只使用部分接口或接口的部分功能,那接口的设计就不够职责单一。
    我们调用这个方法肯定是要用它的整个功能,而不是其中的一个新增或自增功能。
    展开
    1
    14
  • 小海
    2019-12-14
    回答课后讨论题得结合具体的场景和运行环境。AtomicInteger的getAndIncrement()函数的职责很单一,就是"获取当前值并递增"这一步原子操作,有人说这是两步操作,这个函数是运行在多线程并发环境下,在这种环境下把获取当前值和递增拆分成两个函数会获得错误的结果,而该函数内部封装了两步操作使其成为一个原子操作,从这个角度任意一方都是另一方的附属品,两者必须同时完成而不能拆分,如果仅仅是为了获取当前值或者递增那完全可以使用该类的其它函数。从调用方的角度,必然是同时用到了获取当前值和递增两个功能,而不是部分功能,明白该函数设计的"单一职责",就知道它符合SRP和ISP,不要试图去拆分一个原子操作。
    
    12
  • M
    2020-02-08
    接口隔离原则:我只要我想要的,不想要的别给我
    2
    10
  • 星溯
    2021-04-01
    老师此题大有深意,我们可以从此思考题中方法的设计来深化对单一职责和接口隔离的理解:
    接口隔离,强调的是调用方,是否只使用了接口中的部分功能?若是,则违反接口隔离,应当细粒度拆分接口,从这个例子看,调用方诉求与方法名完全一致,通过方法内部封装两个操作,实现原子性,达成了调用方的最终目的,不多不少。
    单一职责,不强调是否为调用方,只要能某一角度观察出,一个模块/类/方法,负责了多于一件事情,就可判定其破坏了单一职责,基于此经典理论,不假以深层次思考的角度出发,从方法本身的命名(做两件事)就可断定,它一定是破坏了单一职责的,应该拆分为两个操作。
    但我们可以结合老师说的,判定职责是否单一,要懂得结合业务场景,业务需求,此方法,其实就是要通过JDK提供的CAS乐观自选锁(方法最终依赖硬件指令集原语,Compare And Swap)从“原语”这一词的含义看,其实也是同时、原子性地做了一件“完整”的事情,因此,考虑这一点,是可以判定它符合单一职责的。
    而这其实正是单一职责判定结果,往往见仁见智的原因:基于不同的角度,不同的立场,不同的业务理解,往往可以得到不同的判定结果,但不必纠结,判定过程中用到的思想才是精髓。
    展开
    1
    9
  • 黄林晴
    2019-12-13
    思考题:
    个人感觉,不符合单一职责,也不符合接口隔离,因为函数做了两件事,不应该把获取当前值和值加1放在一起,因为
    1.用户可能需要-1 *1等其他运算操作再返回原始值,这样就要n个方法每个方法中都有返回原始值的操作。
    2.用户可能只想运算操作,不想运算后暴露原始结果
    3.如果用户以后还想获取操作后的值,这个函数就不能同时返回两个值了
    希望大家指正
    展开
    1
    4
  • 江小田
    2021-03-11
    按语法定义貌似是违背了单一职责和接口隔离原则的。
    但是我们要考虑的是单一职责和接口隔离的意义是什么?是为了隔离开单元模块,方便使用者随机组合、灵活运用。

    但该方法的目的是为了正确获得更新与获得上一个值的准确定义。该方法通过cas无锁算法,实现了乐观锁,同时保证返回了准确值。

    假如系统不提供此方法,而是业务自行调用get方法获取自增前的值,然后加上一再设置新值。那这里从获取到设置的整段代码,就不是线程安全的了。违背了这段代码的初衷。

    反过来讲,调用方自己可以实现锁来保证线程安全。但是这个线程安全的职责就从 atomicInteger转移到了调用类,显然不是设计的初衷。

    所以结合业务场景,两段操作,实际是要求原子话的。也就复合单一职责和借口隔离原则了。
    展开
    
    3
  • 陈拾柒
    2019-12-19
    为什么觉得老师说的,对于接口的三种理解,第一种理解和第三种理解说的是同一件事情~

    作者回复: 不一样呢你再看看

    8
    3
  • 何妨
    2019-12-13
    单一职责是针对于模块、类在具体的需求业务场景下是否符合只做一件事情的原则。
    而接口隔离原则就细化到了接口方面,我是这样理解的,如果接口中的某些方法对于需要继承实现它的类来说是多余的,那么这个接口的设计就不符合接口隔离原则,可以考虑再拆分细化。
    对于课后思考题,他只对该数做了相关操作符合单一职责原则。但从接口、函数来看它实现了两个功能,获取整数及给该整数加一,是不符合接口隔离原则的。
    不知道我这样考虑是否正确,望指正
    展开
    1
    3
  • 这得从我捡到一个鼠标...
    2020-04-03
    文中第一种和第三种理解不是同一种吗?都是为了减少依赖不需要的接口函数。
    
    2
  • DullBird
    2019-12-14
    感觉是属于比较难判断是否单一职责的内容,顺便回头翻了一下单一职责的章节:在理解一下,单一职责主要还是要结合业务,getAndIncrement的方法实现了: 原子的获取并且新增的这一职责,如果拆开成get和Increment的话,就需要外层加锁处理原子的获取并新增操作,对于业务不太合适。
    从接口隔离的原则看,调用这个方法的类,本身就是依赖这个接口,所以并没有违反。
    想到一个问题:
    如果一个类中,有n个查询的业务接口,根据姓名查,根据年纪查,根据地址查(假设不是参数控制,而是拆成3个接口)。那么不同调用方依赖这个类的时候,有可能是根据姓名查,有可能根据年纪查,如果都拆开了。那么接口是不是粒度太细了
    展开
    
    2
  • Chen
    2019-12-13
    getAndIncrement()符合接口隔离原则,这是不是一个大而全的函数,而是一个细粒度的函数,跟count++的功能类似。
    
    2
  • xzy
    2021-01-04
    接口隔离原则中的”接口“在这里指的是函数,AtomicInteger是一个提供原子操作的Integer类,通过线程安全的方式操作加减,getAndIncrement()相当于线程安全的i++操作,调用者需要的是函数的需要加的同时,返回旧值,换句话说:用的是函数的全部功能,符合接口隔离原则。

    getAndIncrement()执行的是i++这个单一操作,因此也符合单一职责原则。
    
    1
  • 郑大钱
    2020-09-01
    如果调用者只使用部分接口或接口的部分功能,那接口的设计就不够职责单一。
    getAndIncrement通过调用CAS实现原子自增,保证线程安全。自增本身必须包含两个操作,取值和加一,我觉得这里的返回的取值,只是一个附产品。
    在了解getAndIncrement方法的过程中,了解到CAS操作的乐观锁和自旋,还要CAS操作带来的ABA问题,堪比小说,很有意思!
    1
    1
  • skying
    2020-06-29
    我理解是符合接口隔离原则。
    因为需要提供 原子性的服务能力,即 更新数据且返回更新后的数据,要保证原子性,必须在一个方法调用中返回。
    
    1