6 I( X7 D! {' m; J! l7 V下面从三个方面进行比较:% X5 p0 i1 x. u5 S
! j% G5 ~* S- V3 S; a ]6 P
一、从语法定义层面看abstract class和interface ! l$ d, s; E1 [6 ]* V+ G3 P 0 D f' `# @5 S S" p/ J使用abstract class的方式定义Deal抽象类的方式如下: . n4 W4 r! \0 k * u7 u9 d( L! ^5 K" wabstract class FxDeal {8 g+ z' D. D# S& H; `. k
7 k+ f6 u4 |" i* S9 i4 P! w2 a private long dealsId; //交易流水号 ! Z( _; P# G* l' G5 k; Y4 L P5 d1 q( k+ d
private long blockNumber;//套流水号 2 O' m) x9 N! m* I 6 H9 d7 Q1 T- V+ |private String appls;//产品类别9 g: B. I( U" f. s5 O
4 I" _9 J1 h# P; | V
private String inputChannel;//录入渠道5 k0 B* X- @3 L0 |0 w% u9 Y
: n1 N' l1 D) x" {% I z
private String typeOfDeal;//交易类型, p9 M( b' P6 k I
- f$ q" h3 s/ [4 uprivate String flagOfDeal;// 特殊交易标识 A; |& |2 {6 z, ?2 P" f 0 D; b% s5 Z6 O- P" S' C3 w- Eprivate String bankId;//业务发生行. Z' o( v* N p8 U8 k
, a: O, J+ X# c ^7 T; ^7 ^: r- g
private String customerId; //客户号 0 Y: H. Y b( n1 S5 P" B8 \( L$ x- t- g( A# C p1 W+ G! E
private String customerName;//客户名称" j W% S# F( h9 m' I. ]5 A
2 d% t* Z! n# V, n6 n* }private int customerType;//客户类型& |/ a/ u% F+ a* {& j; u1 _
& a1 R# m( d; R# a* [9 L
private double amount1;" O$ j# R. ^3 }4 B L, h( D
+ ~$ ]' w2 U% T/ X$ B, S; i$ K9 C6 M) }abstract void method2(); 1 g5 N& t3 [, W e7 O) v" B J! f
6 O* Q- H1 @' q) g0 M l …) ~- t/ l% Z$ p. {4 D$ F: @
; E2 W% j0 b z. Z, _' `6 v r p}: r2 \6 O/ [; j/ q
, C8 E& b; \" p* B) F$ P! C4 j
使用interface的方式定义Deal抽象类的方式如下:$ N% a2 f0 _; s
interface FxDeal {" j; ~7 b: `; ^
void method1();: d8 O8 o2 h1 ^& ^# r' ]( T# Z
void method2(); . c/ [, c) f6 Y+ F& b f# c- p6 t1 ] … . c! o8 ^! W( _/ J; @( f }& L& i% I( S6 L. _* z. [1 |
7 f0 N3 J' c, d5 a, X! M( K: {" m在abstract class方式中,Deal可以有自己的数据成员,也可以有非abstarct的成员方法,而在interface方式的实现中,Deal只能够有静态的不能被修改的数据成员(也就是必须是static final的,不过在interface中一般不定义数据成员),所有的成员方法都是abstract的。从某种意义上说,interface是一种特殊形式的abstract class。 " I" n( z1 \( n* b' @- W7 q- `8 q# Q! x3 {/ I ]7 a) [$ g
二、从编程层面看abstract class和interface+ V9 g2 W( l* P, F2 Q
$ @$ }$ W& M' o; j1 z# P6 U首先,abstract class在Java语言中表示的是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个interface。: q1 l3 c2 K3 B& b. W- }& U2 a4 q
: U! ]& ]0 y% j7 v- A
其次,在abstract class的定义中,我们可以赋予方法的默认行为。但是在interface的定义中,方法却不能拥有默认行为,为了绕过这个限制,必须使用委托,但是这会增加一些复杂性,有时会造成很大的麻烦。 / J5 m! l0 Y7 i, `7 R( P& h6 N : y& [$ i: @4 a$ r t7 N Y在抽象类中不定义默认行为存在一个比较严重的问题,那就是可能会造成维护上的麻烦。因为如果后来想修改类(一般通过abstract class或者interface来表示)以适应新的情况(比如,添加新的方法或者给已用的方法中添加新的参数)时,就会非常的麻烦,可能要花费很多的时间(对于派生类很多的情况,尤为如此)。但是如果它是通过abstract class来实现的,那么可能就只需要修改定义在abstract class中的默认行为就可以了。9 i2 D1 O& P+ n
0 q# T* d9 A* y9 p
另一个问题是:如果不是采用抽象类中的默认行为,就会导致同样的方法实现出现在该抽象类(或接口)的每一个派生类(或实现类)中,违反了“one rule,one place”原则,造成代码重复,同样不利于以后的维护。因此,我认为在abstract class和interface间进行选择时还是需要谨慎的,搞不好的话,是会给程序埋下一些隐患的。 / H/ \- p* f8 M2 N+ E& l& f+ k" S9 k: E7 k) a- G( w, g
三、从设计理念层面看abstract class和interface) K, D% V7 J3 Q/ L7 t& N$ z
+ q+ \8 D, R9 r6 V5 X5 e abstract class在Java语言中体现的是一种继承关系,父类和派生类之间必须存在“is a”关系,即父类和派生类在概念本质上应该是相同的。对于interface 来说则不然,并不要求interface的实现者和interface定义在概念本质上是一致的,仅仅是实现了interface定义的契约而已。为了使论述便于理解,下面将通过一个简单的实例进行说明。 1 e# v0 u5 U- R* I0 }3 B 6 t, ?" q' w3 g" E7 \+ J3 H 考虑这样一个例子,假设在我们的问题领域中有一个关于Door的抽象概念,该Door具有执行两个动作open和close,此时我们可以通过abstract class或者interface来定义一个表示该抽象概念的类型,定义方式分别如下所示: 4 ^5 R! a L* ]1 f' F- `. O+ Q! H! C8 L3 a* ]/ p
使用abstract class方式定义Door: ; e y4 i. ~5 W: X" B' ~2 ^3 i4 X1 ^
abstract class Door {1 F) v+ ^$ }# i. ~" r
5 i. }) j: K. e1 t3 k8 A
abstract void open();7 s# V/ Z: v% g! z6 _
& B0 K! V* Z, I* X3 f3 r
abstract void close(); . F& E6 Z7 G2 d* w7 Q' G6 i. K4 l( ^& I* N7 d% a4 J( w/ z- V
} ( Q8 R# b4 \2 f8 e7 F3 Z8 c# L2 b. _. Q( N) Z, v- P3 E
使用interface方式定义Door: c- h3 I5 a# G; n
9 G% ^ M& U% W( m% ?1 s interface Door {: P2 @! D2 \; n7 A+ u. Z9 ~* y; Y& B& M
( p0 Y/ u5 o2 N& I7 w
void open(); 1 b- u2 E7 v: I3 V0 X- D" m" s, ~6 F3 v5 O, I# p, O& x
void close(); % @3 O. w) e! h L5 p7 [- r& Z! S6 R: s; |5 C
}3 S$ V) z1 Y/ w7 z
7 H3 |9 u) \1 j' f6 i% O 其他具体的Door类型可以extends使用abstract class方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstract class和interface没有大的区别。! J }) [8 W4 D U; I# z
$ R% z0 B0 n9 J' \' m+ d/ q% z/ W 如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢(在本例中,主要是为了展示abstract class和interface反映在设计理念上的区别,其他方面无关的问题都做了简化或者忽略),下面将罗列出可能的解决方案,并从设计理念层面对这些不同的方案进行分析。 8 d9 e+ i, m1 G: N$ } , w r# O l, X# t( T6 K 解决方案一:- Q1 t0 K( M+ U* d9 W4 P
' |1 v2 m, v- S, N9 L' n1 M 简单的在Door的定义中增加一个alarm方法,如下: . Z S$ c6 L5 ^: ~* F$ u , A, z3 d; L7 r abstract class Door { 6 d& ]9 j) l$ l& F3 Y0 u8 c6 I0 k6 C- A! G6 `- Q+ E+ q% D
abstract void open(); & n' {/ C& h! b: Z 2 W+ K3 N6 Z3 v0 x% ^8 i abstract void close();& S: M5 q# \8 B0 `
, \' u% ?+ ]# j& l3 B3 a( z
abstract void alarm(); 5 }8 N0 p* D; o" U( d9 u* t8 Z9 D
}3 p9 T2 ^, P7 ]& Q# |; L
4 n- h8 t1 c# F ]1 `5 H4 _
或者2 N8 z1 F- G% f+ m
- G2 k6 ~7 ?1 L+ c
interface Door {4 f* n% j2 Q) {* I
$ R( P) z' d3 P2 r- S7 S( `
void open();2 q* ?& A4 D2 c" ]
/ Z" z6 ? t8 O void close(); $ s2 c$ R8 B4 m7 g# s: C W. K d! k! ?' ]8 g- X2 F
void alarm();, U" s: M, K$ V
0 z2 ^& K y/ p }; E1 q, N t- \3 O
6 B O; E$ W$ L8 ?1 m
那么具有报警功能的AlarmDoor的定义方式如下: ( n: w' r1 `0 Z8 D4 V$ J( } + ^0 r5 j! f# c class AlarmDoor extends Door {+ n: V# z" p1 `* ^8 s
- e! y( R# p* P% x2 ^ void open() { … } , o1 I6 ^, M, ^5 u 4 ^: d) {+ t, \9 R( O void close() { … } & i8 t5 o1 z1 c/ u; [ - f' h" c. j7 ]6 Q% S: f2 @ void alarm() { … } A2 h. T A4 f& r( J# q0 X+ |' }
) Z$ q% K7 G5 M0 D
} " h( V5 O+ y: ^4 a2 e6 |8 ] . z. K% N) n$ x 或者 " G' g0 f6 a) [6 m. i1 ^ % e G p5 c5 `! X6 D class AlarmDoor implements Door {6 R0 { h8 P6 b. l
! F2 d: \4 d; y3 _: B X$ m+ S$ j0 a
void open() { … }, j5 h% L9 ?2 J, d
7 J% I% h+ e6 M2 R9 o' a- M
void close() { … }0 ~1 e1 `+ _& ]
2 h# X& l1 W4 H. m; I void alarm() { … } - L9 I/ W$ j0 `6 Q4 ]$ i# v2 x* u# I. C1 w/ ? x5 K D3 x" i
} ' P. a z1 S7 E" `) ?) ^. I# Z6 n1 Z" V) }# @1 s5 F% ] ]
这种方法违反了面向对象设计中的一个核心原则ISP(Interface Segregation Priciple),在Door的定义中把Door概念本身固有的行为方法和另外一个概念“报警器“的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为“报警器“这个概念的改变(比如:修改alarm方法的参数)而改变。/ K$ C" v, l. {' ]! _2 M- d% ^
! `& ~( @: ]! U5 Q2 Y# L, C9 ^
解决方案二:% @8 G% K# l2 P( @) ^* \7 s6 x
c+ [% T l( E- z: f 既然open、close和alarm属于两个不同的概念,根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有:这两个概念都使用abstract class方式定义;两个概念都使用interface方式定义;一个概念使用abstract class方式定义,另一个概念使用interface方式定义。 $ o$ Y) \% {0 U! ]4 ~ ( ~9 C9 D! ?. ~ X6 G 显然,由于Java语言不支持多重继承,所以两个概念都使用abstract class方式定义是不可行的。后面两种方式都是可行的,但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。 6 F! p+ }) m( z( d如果两个概念都使用interface方式来定义,那么就反映出两个问题:1、我们可能没有理解清楚问题领域,AlarmDoor在概念本质上到底是Door还是报警器?2、如果我们对于问题领域的理解没有问题,比如:我们通过对于问题领域的分析发现AlarmDoor在概念本质上和Door是一致的,那么我们在实现时就没有能够正确的揭示我们的设计意图,因为在这两个概念的定义上(均使用interface方式定义)反映不出上述含义。 / n, _, y$ \1 e5 Z A9 R+ J7 p$ Q5 l2 i* {6 m0 _
如果我们对于问题领域的理解是:AlarmDoor在概念本质上是Door,同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢?前面已经说过,abstract class在Java语言中表示一种继承关系,而继承关系在本质上是“is a”关系。所以对于Door这个概念,我们应该使用abstarct class方式来定义。另外,AlarmDoor又具有报警功能,说明它又能够完成报警概念中定义的行为,所以报警概念可以通过interface方式定义。如下所示: % W$ ~, i0 m; H |* V . t: I0 x# E" g8 K( I0 r# K( q( l abstract class Door {: ?9 @0 s' R1 W* ?
0 O/ ?" v* Q/ ?4 i2 }% C
abstract void open(); 6 @3 \; Y- V; v$ s1 U F ' Z8 d$ I9 T+ a' G1 M: H" ? abstract void close();2 Y( T- U5 F$ @