) N8 V* i4 i8 E7 Q* K1 V. ~ void alarm() { … } * `: t' p3 N, ~( e, _8 [; ?/ D6 n7 h1 k' r2 z
} 2 k% V/ n9 L7 a I7 `- O2 e: \' X3 `: D, `* |
或者 " q0 y. U! V- [* a7 _9 o' N A) P, x# x
class AlarmDoor implements Door { , _2 K, O' ~# L! S) X( Z- h l: ?; B' K) @& `$ B
void open() { … } ; B4 N6 z7 x: W$ A% v. s7 J2 l9 q, H
void close() { … }0 d' a) v) T* v
7 w% \! ~9 p4 U void alarm() { … } 9 v7 s; e" V& P9 y$ a. u! s4 p( k5 L* P* g& E. s& y
}- _ {# E- w% ]( K F# a
6 Q" E# Z" o6 V3 O. b+ `
这种方法违反了面向对象设计中的一个核心原则ISP(Interface Segregation Priciple),在Door的定义中把Door概念本身固有的行为方法和另外一个概念“报警器“的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为“报警器“这个概念的改变(比如:修改alarm方法的参数)而改变。# X: v0 l" y& G6 T- G6 ]/ A
: e# n s3 P0 ~" q8 Q2 N; w 解决方案二: " w0 ]. {) s1 x# k/ t) F6 f: |% N. S5 Y& y
既然open、close和alarm属于两个不同的概念,根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有:这两个概念都使用abstract class方式定义;两个概念都使用interface方式定义;一个概念使用abstract class方式定义,另一个概念使用interface方式定义。* A* L5 m U* N8 \( L& A
- P5 D+ g: S9 J) @
显然,由于Java语言不支持多重继承,所以两个概念都使用abstract class方式定义是不可行的。后面两种方式都是可行的,但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。 . G3 y; m2 h9 m- x6 p如果两个概念都使用interface方式来定义,那么就反映出两个问题:1、我们可能没有理解清楚问题领域,AlarmDoor在概念本质上到底是Door还是报警器?2、如果我们对于问题领域的理解没有问题,比如:我们通过对于问题领域的分析发现AlarmDoor在概念本质上和Door是一致的,那么我们在实现时就没有能够正确的揭示我们的设计意图,因为在这两个概念的定义上(均使用interface方式定义)反映不出上述含义。 9 p. w- `/ ?" F$ p! h$ o5 _8 y. A1 |! a3 s) z, o/ C6 s9 C
如果我们对于问题领域的理解是:AlarmDoor在概念本质上是Door,同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢?前面已经说过,abstract class在Java语言中表示一种继承关系,而继承关系在本质上是“is a”关系。所以对于Door这个概念,我们应该使用abstarct class方式来定义。另外,AlarmDoor又具有报警功能,说明它又能够完成报警概念中定义的行为,所以报警概念可以通过interface方式定义。如下所示:8 ?1 D8 K( |: x1 h
2 a; L" K. x- X) G
abstract class Door { ' w3 H1 z: D2 M3 q: }4 t) S4 z' ?7 E7 x
abstract void open(); ' o. I4 D; P' o" Q5 D Y9 j) l8 c0 \7 k abstract void close();* \9 R4 m' o/ J! S2 V' Q' X
6 f# `3 S l1 @ n# y } " u# h% H# V$ i8 Q5 f& }! Y; \ H8 l* B7 i/ o2 B
interface Alarm {/ N& W% r: {% f
: m# x; t' h: b' z1 K' D5 y
void alarm();" {5 t A' s f( z
4 E. x) D* V* G3 @
} / z* g8 T* u4 \+ N , E# s) L+ u9 Q$ e class AlarmDoor extends Door implements Alarm { 1 V4 p _& G" q6 w/ Y9 h' w C- u- N9 k/ _; i
void open() { … } $ b2 T! I9 t* j; [8 Q8 h 9 E, O8 N: o5 v0 [ void close() { … }/ K2 a4 @# u0 f2 ^
3 K! P/ \" v2 e$ w void alarm() { … }7 _/ _; P; n8 t& k
6 f3 }$ j& Z9 ~( B6 u- L }8 p$ s4 J- M% ]% ]7 ?) m
& ~4 H2 d+ Z! ^ 这种实现方式基本上能够明确的反映出我们对于问题领域的理解,正确的揭示我们的设计意图。其实abstract class表示的是“is a”关系,interface表示的是“like a”关系,大家在选择时可以作为一个依据,当然这是建立在对问题领域的理解上的,比如:如果我们认为AlarmDoor在概念本质上是报警器,同时又具有Door的功能,那么上述的定义方式就要反过来了。* C! z. d! C% ]. w. h M
其实在某些时候,这两个特殊的类的使用是可以互为替换的,但是在较大规模的系统工程设计时,如果不能正确运用好它的话,可能会破坏程序的结构,增强模块间的耦合度,给程序的维护与升级带来较大的不便,希望大家能够细细体会。4 \' d; D, | D