MqZk@RG?0 解决方案二:
2eSj[&_051Testing软件测试网^%r#z"k%ouU 既然open、close和alarm属于两个不同的概念,根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有:这两个
概念都使用abstract class方式定义;两个概念都使用interface方式定义;一个概念使用abstract
class方式定义,另一个概念使用interface方式定义。51Testing软件测试网P3vZl:H4I
z1k2Y0{
ZB&Wo+k0 显然,由于Java语言不支持多重继承,所以两个概念都使用abstract class方式定义是不可行的。后面两种方式都是可行的,但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。我们一一来分析、说明。51Testing软件测试网a#i?(R~:k$X
O7p)s-}Or#b[Q,I0 如果两个概念都使用interface方式来定义,那么就反映出两个问题:1、我们可能没有理解清楚问题领域,AlarmDoor在概念本质上
到底是
Door还是报警器?2、如果我们对于问题领域的理解没有问题,比如:我们通过对于问题领域的分析发现AlarmDoor在概念本质上和Door是一致
的,那么我们在实现时就没有能够正确的揭示我们的设计意图,因为在这两个概念的定义上(均使用interface方式定义)反映不出上述含义。
l:B?
jz8d6p;d:w051Testing软件测试网0{-V#J6E"OoG
Q 如果我们对于问题领域的理解是:AlarmDoor在概念本质上是Door,同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我
们的意思呢?前面已经说过,abstract class在Java语言中表示一种继承关系,而继承关系在本质上是"is
a"关系。所以对于Door这个概念,我们应该使用abstarct
class方式来定义。另外,AlarmDoor又具有报警功能,说明它又能够完成报警概念中定义的行为,所以报警概念可以通过interface方式定
义。如下所示:51Testing软件测试网Fd.Gk~r;x%Q
51Testing软件测试网m r2p-bKBZ51Testing软件测试网9iq``L;Z
abstract class Door { 51Testing软件测试网M;{+Yg0S][;Sg abstract void open(); 51Testing软件测试网u1mz"LV:N&? abstract void close(); 51Testing软件测试网2lq~r,m+f#q,s b } o2F%V8?]DS)^,j0interface Alarm { 51Testing软件测试网~%`)R"n3w*yJyq`K7L void alarm(); 51Testing软件测试网`{)Fk/l[-xb } 51Testing软件测试网j!]
Y\Md:M,nF,?:_ class AlarmDoor extends Door implements Alarm { !_Vpb,lX^0 void open() { … } UIC%qLz)L3sB0 void close() { … } 51Testing软件测试网R"kBc/r*g o void alarm() { … } lQl u3Z@mH0F0} |
rrAsXp7W;m
|0 这种实现方式基本上能够明确的反映出我们对于问题领域的理解,正确的揭示我们的设计意图。其实abstract class表示的是“is
a”关系,interface表示的是“like
a”关系,大家在选择时可以作为一个依据,当然这是建立在对问题领域的理解上的,比如:如果我们认为AlarmDoor在概念本质上是报警器,同时又具有
Door的功能,那么上述的定义方式就要反过来了。51Testing软件测试网eM$kN3b
51Testing软件测试网s'oc4nZe 接口与抽象类各自的优缺:
-A@po&R@0oL1zh0ll}D$R-a0 接口缺点:如果向一个java接口加入一个新的方法时,所有实现这个接口的类都得编写具体的实现。
vy)Z)`Q0t1B:~*Wm1Z0 接口优点:一个类可以实现多个接口,接口可以让这个类不仅具有主类型的行为,而且具有其他的次要行为,比如 HashMap主要类型是Map,而Cloneable接口使它具有一个次要类型,这个类型说明它可以安全的克隆.51Testing软件测试网PON2hx@kd$[
Jk6^)LQJf0 抽象类的缺点:一个类只能由一个超类继承,所以抽象类作为类型定义工具的效能大打折扣。
+ijS~#I2@1o5Y051Testing软件测试网6|4AU9AC,n+{G 抽象类的优点:具体类可从抽象类自动得到这些方法的缺省实现。
uM~1Q2R6t051Testing软件测试网jP6w\t A tC 抽象类与接口的区别:
P
riX
|g051Testing软件测试网a&Us.NY c\ 1、抽象类可以包含部分方法的实现,这是抽象类优于接口的一个主要地方。51Testing软件测试网[+C8cy2u9C$PuC
6YYwSF/k,q E5^0 2、由于Java的单继承,每个类只能从一个抽象类继承,但是每个类可以实现多个接口,使用接口还可以实现Mixin混合类型的类。接口可以继承多个接口,即接口间可以多重继承。
S ?q] b(MQ,X-?0r8EFn&S\0 3、将类抽取出通用部分作为接口容易,要作为抽象类则不太方便,因为这个类有可能已经继承自另一个类。
*b1_'Mz4P9B@0L'pu.V6b4ID+na!G0 4、可以将接口和抽象类一起使用。在集合框架体系中,顶层接口Collection定义了一些方法,同时又提供了几个抽象类
AbstractCollection、AbstractList、AbstractMap实现了一些方法,这样具体的集合实现类可以选择从抽象类中继承
或直接实现接口。
-_"m7G m'N _!]'g051Testing软件测试网/Op-_FHM J 抽象类和接口在语法和设计原则上的区别:51Testing软件测试网X.aK\6j t.Z
51Testing软件测试网0]B]F%T!~)_R 1、类是对对象的抽象,可以把抽象类理解为把类当作对象,抽象成的类叫做抽象类
$R_
o4y [5]`0AT;y4f4O({'{0 接口只是一个行为的规范或规定,微软的自定义接口总是后带able字段,证明其是表述一类类“我能做。。。”抽象类更多的是定义在一系列紧密相关的类间,而接口大多数是关系疏松但都实现某一功能的类中
n(m|"}9s051Testing软件测试网jD!rN4c%@,S 2、接口基本上不具备继承的任何具体特点,它仅仅承诺了能够调用的方法;51Testing软件测试网%eS(aB
ec#J
U
ZBSZ MS y0 3、一个类一次可以实现若干个接口,但是只能扩展一个父类51Testing软件测试网j)yW T8A%SQkl4c
e
y#fg#d[N_0 4、接口可以用于支持回调,而继承并不具备这个特点.
/w,?|m3e0Y$]U.XU-S0 5、抽象类不能被密封。
T#V+imi[4X6c051Testing软件测试网.D3zN7r)i
{1K&\+jwo 6、抽象类实现的具体方法默认为虚的,但实现接口的类中的接口方法却默认为非虚的,当然您也可以声明为虚的。(C#)
V.h%?l"|!Y0D6`y+j}tw YH6T0 7、(接口)与非抽象类类似,抽象类也必须为在该类的基类列表中列出的接口的所有成员提供它自己的实现。但是,允许抽象类将接口方法映射到抽象方法上。51Testing软件测试网yZMhS&O
51Testing软件测试网pj"ME`:n\ 8、抽象类实现了oop中的一个原则,把可变的与不可变的分离。抽象类和接口就是定义为不可变的,而把可变的座位子类去实现。51Testing软件测试网Ht"l4@*Ln
9R JSTvlY0 9、好的接口定义应该是具有专一功能性的,而不是多功能的,否则造成接口污染。如果一个类只是实现了这个接口的中一个功能,而不得不去实现接口中的其他方法,就叫接口污染。
d9{/[)c;T;~0#JxxtR4L0 10、尽量避免使用继承来实现组建功能,而是使用黑箱复用,即对象组合。因为继承的层次增多,造成最直接的后果就是当你调用这个类群中某一类,
就必须把他们全部加载到栈中!后果可想而知.(结合堆栈原理理解)。同时,有心的朋友可以留意到微软在构建一个类时,很多时候用到了对象组合的方法。比如
asp.net 中,Page类,有Server
Request等属性,但其实他们都是某个类的对象。使用Page类的这个对象来调用另外的类的方法和属性,这个是非常基本的一个设计原则。51Testing软件测试网!Q {kh t&d4m,o8Kj
R*S)|o*n0 11、如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法51Testing软件测试网eR"K!]Y[,Pk*p
51Testing软件测试网;O
U/]
g"_,w^'l 如果预计要创建组件的多个版本,则创建抽象类。抽象类提供简单的方法来控制组件版本。如果创建的功能将在大范围的全异对象间使用,则使用接口。如果要设计小而简练的功能块,则使用接口。如果要设计大的功能单元,则使用抽象类。
$w[/DxY k5E
D051Testing软件测试网;z'^*t/K-V&l1A0w 如果要在组件的所有实现间提供通用的已实现功能,则使用抽象类。
pl4]J1V6v0