本文共 1638 字,大约阅读时间需要 5 分钟。
关于抽象,是面向对象的特性,之前实际写代码的时候,可能注意得不是很好,对于这个点的理解也不是很到位。
但是因为公司项目涉及到了组件化,不同组件之间的通信其实是依赖抽象接口来规范的,或者说是约束的。
而一开始,我在定义抽象接口的时候,就处理得不是很好,偶尔会把具体业务层的实现带到接口定义之中,虽然觉得这样有点不对劲,但是却无法参透。
后来,在 code review 的时候,我的导师针对我的问题跟我聊了聊,在受到点拨之后,我感觉我对此的认知有了些许进步。
说得简单的,就是:
抽象层不应该对外暴露具体的实现参数,即不是面向实现编程,而要面向抽象编程。
因为对于抽象层来说,并不应该关心具体的实现,只需要做到 “定义”,至于业务层具体怎么实现,那是业务的事,在外人看来,只需要根据抽象层对外暴露的对应的接口方法能够实现目的即可。
而且对于抽象层来说,要做到替换业务层的实现,对外是无感知的,但是如果抽象层参杂了业务层的东西,那还怎么做到替换业务层的时候对抽象层不产生影响。
比如我对外定义一个抽象接口 IPageService
,里面有个抽象方法 toUserInfoPage()
,用于表示跳转到应用的用户信息页。
但是,在具体的实现中,假设有两种情况,一种是用户信息页实际上在首页的一个 tab 项,假设是第 X 个 tab,那跳转到个人信息页此时就对应跳转到首页第 X 个 tab 的逻辑;第二种则是用户信息页是一个二级页面,那此时的逻辑则是跳转到对应的 Activity。
此时,如果抽象层没有定义好,针对第一种情况,则可能会将形参 Int index
也带入到 IPageService#toUserInfoPage()
方法中,从而变成,fun toUserInfoPage(index: Int)
,用来在实现类 IPageImpl
中通过参数 index
来指定跳转到首页第 X 个 tab(即个人信息页)。
interface IPageService { fun toUserInfoPage(index: Int)}class PageFirstImpl: IPageService { fun toUserInfoPage(index: Int) { // use index jump to user info tab }}
这种情况下,其实就是对外暴露了业务层的实现,而且调用的时候必须要指定 index
为 X 才能达到目的。
val pageService = PageFirstImpl()// should introduce X, and should know index be XpageService.toUserInfoPage(X)
之后,如果要该成第二种实现,此时参数 index
就相当于摆设了,不会有丝毫用处。
class PageSecondImpl: IPageService { fun toUserInfoPage(index: Int) { // start UserInfoActivity, index is useless }}
而正确的实现应该是像下面那样,在具体的实现类中直接指定 X 即可,因为外部调用时根本是不需要知道的,只要能够实现跳转到用户信息页即可。
interface IPageService { fun toUserInfoPage()}class PageFirstImpl: IPageService { fun toUserInfoPage() { val index = X // jump to user info tab via local variable ‘index’ }}
再补充一点,即不要本末倒置地先去想到业务层应该怎么实现,再根据实现去定义抽象层的接口方法,这个时候就会被业务层的参数给影响到。
转载地址:http://prerj.baihongyu.com/