《大话设计模式》之接口类型模式介绍

        在系统设计时,常常遇到这样一个问题:类Client 的实例 instanceClient 希望使用另一个对象instanceX 提供的服务service,但在设计时,我们并不能确定对象instanceX究竟属于哪个类。遇到这种情况的时候,我们应该怎么办呢?

        常见的解决办法是:将对象 instanceX 提供的服务service 抽象为一个接口 ServiceProvider, 然后让对象instanceClient 通过持有接口ServiceProvider 的实例来使用服务service 。这种接口间接获得服务的解决方案就是接口模式。

        接口模式还可以有一些变化的形式:不止用一个接口抽象一个对象提供的服务,还可用一组就扣抽象一群对象的交互。

        接口模式包括:适配器模式,外观模式,合成模式以及桥接模式。

        在Java语言中,abstract class 和 interface 是支持抽象类定义的两种机制,那抽象类和接口有什么区别呢?

        abstract class 和 interface 之间在对于抽象定义的支持方面具有很大的相似性,甚至可以相互替换,因此很多开发者在进行抽象类定义是对于 abstract class 和 interface 的选择显得比较随意。其实,两者之间还是有很大的区别的,对于它们的选址甚至反映出对于问题领域本质的理解、对于设计意图的理解是否正确、合理。

        抽象类往往是用来表征我们在问题领域进行分离、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。比如进行一个图形编辑软件的开发,就会发现问题领域存在着圆、三角形这样一些具体概念,他们是不同的,但是它们又都属于形状这样一个概念,形状这个概念在问题领域是不存在的,它就是一个抽象概念。正是因为抽象的概念在问题领域没有对应的具体概念,所以用以表征抽象概念的抽象类是不能够实例化的,在面向对象领域,抽象类主要用来进行类型隐藏。

使用abstract class 的方式定义 Demo 抽象类的方式如下:

abstract class Demo {
    abstract void method1 ();
    abstract void method2 ();
}

 使用 interface 的方式定义Demo抽象类的方式如下:

interface Daemo {
    void method1 ();
    void method2 ();
}

         在abstract class 方式中,Demo 可以有自己的数据成员,也可以有非数据abstract 的成员方法,而在interface 方式的实现中,Demo 只能够有静态的不能被修改的数据成员(也就是必须static final 的,不过在interface 中一般不定义数据成员),所有的成员方法都是abstract的。从某种意义上说,interface 是一种特殊形式的abstract class 。 从编程的角度来看,abstract class 和 interface 都可以用来实现 ‘design by contract’(契约式模式)的思想,但是在具体的使用上还是有一些区别的。

        abstract class 在Java 语言中表示的是一种继承关系,一个类只能使用一次继承关系(因为Java 不支持多继承)。但是,一个类却可以实现多个interface。在abstract class 的定义中,可以赋予方法的默认行为;但在interface 的定义中,方法却不能拥有默认行为,为了绕过这个限制,必须使用委托,但是这会增加一些复杂性,有时会造成很大的麻烦。

        假设在我们的问题领域中有一个关于Door的抽象概念,该Door具有执行两个动作open 和 close ,如果要求Door还要具有报警功能,那我们该如何设计这一例子的类结构呢?

使用abstract class 方式定义Door :

abstract class Door {
    abstract open ();
    abstract close();
}

使用interface 方式定义Door :

interface Door {
    void open ();
    void close();
}

如果我们直接在Door定义中增加一个方法

在Door的定义中增加一个alarm方法

abstract class Door{
    abstract void open ();
    abstract void close();
    abstract void alarm();
}

或者

interface Door{
    void open ();
    void close();
    void alarm();
}

      根据面向对象设计中的一个核心原则ISP,在Door的定义中把Door概念本身固有的行为方法和另一个概念‘报警器’的行为方法混在一起。这样引起的一个问题是哪些仅仅依赖于Door这个概念的模块会因为‘报警器’这个概念的改变而改变,所以以上的方式是不可行的。既然open、close和alarm 属于两个不同的概念,根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有:

- 两个概念使用abstract class 方式定义

- 两个概念使用interface 方式定义

- 一个概念使用abstract class 方式定义,另一个使用interface 方式定义。

显然,由于Java 语言不支持多重继承,所以两个概念都使用abstract class 方式定义是不可行的。后面两个方式都是可行的。但是对于它们的选择,却反映出对于问题领域中的概念本质的理解,对于设计意图的放映是否正确、合理。

如果两个概念都使用interface 方式来定义,那么反映出两个问题:

- 我们可能没有理解清楚问题领域,AlarmDoor在概念本质上到底是Door还是报警器?

- 如果我们 对于问题领域的理解没问题,比如:我们通过对于问题领域的分析发现,AlarmDoor在概念本质上和Door是一致的,那么我们在实现时就没有能够正确的揭示我们的设计意图,因为两个该闹闹在定义上,均使用interface定义,反映不出上述含义。我们对于问题领域的理解是AlarmDoor 在本质上是Door,同时它有具有报警的功能。继承关系的本质是‘is-a’关系,所以Door我们应该使用abstract class 方式来定义,AlarmDoor 又具有报警器功能,说明它又能够完成报警概念中定义的行为,所以报警概念可以通过interface定义。

abstract class Door {
    abstract void open ();
    abstract void close();
}
interface Alarm{
    void alarm();
}
class AlarmDoor extends Door implements Alarm{
    void open (){...}
    void close(){...}
    void alarm(){...}
}

这种实现方式基本上能够明确放映出我们对于问题领域的理解,正确揭示我们的设计意图。abstract class表示的是‘is-a’关系,interface表示的是‘is-like’关系,在选择时,可以作为一个依据。

        如果我们认为AlarmDoor在概念本质上是‘报警器’,同时又有Door的功能,那么上述的定义方式就要反过来。

        综上所述:

- abstract class 在Java 语言中表示的都是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个interface。

- 在abstract class 中可以有自己的数据成员,也可以有非abstract的成员方法,而在interface中,只能够有静态的不能被修改的(static final)数据成员(不过interface 中一般不定义数据成员),所有的成员方法都是abstract 的。

- abstract class 和 interface 所反映出的设计理念不同,其中 abstract class 表示的是 ‘is-a’关系,interface表示的是‘like-a’的关系。

- 实现抽象类和接口的类必须实现其中的所有的方法,抽象类中可以有非抽象方法,接口中则不能实现方法

- interface中定义的变量默认是public static final型。且必须给其初值,所以实现类中不能重新定义,也不能改变其值。

- abstract class 中的变量默认是friendly型,其值可以在子类中重新定义,也可以重新赋值。

- 接口中的方法默认都是public, abstract 类型。

转载于:https://my.oschina.net/u/2600440/blog/1543373

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值