可扩展的Java应用程序开发模式

上一篇 / 下一篇  2012-08-27 10:45:44 / 个人分类:Java

7\ s eG7V'?8f0?P.R0  摘要:这个程序最简单的版本是在主类中做每件事情,主类包含一些持有孩子姓名的数组和向量(Vector),一个另一个最简单的方法是为每个孩子创建一个对象,每个对象有一个象isFemal()这样的方法来帮助我们确定要显示的是男的还是女的。51Testing软件测试网 Kf E#M,g1Mi\ v%t

51Testing软件测试网Y9A fA7z}[

   现在很多JAVA书籍,在介绍SWING的事件模型时,为了使JAVA初学者容易理解和上手,通常是在主类(自己的JFrame子类或JPanel)中 做所有事情:要么由主类作为监听器,在接口的方法中通过判定事件源来决定具体做何事;要么在主类中使用内隐类来实现监听器,(因为内隐类可以方便的访问主 类中的成员变量)。而很多JAVA初学者在往后的编程中也采用这种模式(至少我以前是这样的)。其实,这种做法虽然简单,却缺乏可读性、可扩展性和可维护 性。之前我看到了一篇关于这个问题的文章(来自于《JavaPro》),现整理出来与大家分享。51Testing软件测试网 ^ kR Y^0`r!@;Z

@4X2iuG0~~0  下面我将用一个简单的例子来说明如何使用模式来解决这个问题。

w7D1h#|]7HjLf051Testing软件测试网_&X!B&W2xEkSL

  现在,假设我们要编写一个使用几个单选按钮从一个长列表中选择不同分组的程序。单击Female单选按钮时,只有女孩的名字被显示;当你点击Male按钮时,程序显示男孩的名字。51Testing软件测试网;bK*j;z;w Y$U)aOy

51Testing软件测试网*IR2X'{z"z*^6w

   这个程序最简单的版本是在主类中做每件事情,主类包含一些持有孩子姓名的数组和向量(Vector),一个另一个最简单的方法是为每个孩子创建一个对 象,每个对象有一个象isFemal()这样的方法来帮助我们确定要显示的是男的还是女的。在程序中我们把每个孩子的类称为Swimmer:51Testing软件测试网\ V"x vs)S

public class Swimmer{51Testing软件测试网1} aY&p5T,T*u4v3r
private String name;
u#l-ByR5~ j8Z*R3f0private int age;51Testing软件测试网#q$H*Ox2}8t;g [S
private String club;51Testing软件测试网Z#J ?2~+_W0|c\%Y1O
private boolean female;
0BUdo7x;O-`@J0public Swimmer(String name,int age,String club,boolean female){
@ }}0[GR&z![ g0this.name=name;51Testing软件测试网(Ig4kg#H@%b
this.age=age;
*{kv u1YB0this.club=club;
:BU Q3b;POE,O0this.female=female;51Testing软件测试网Kou ut w1X/DfV
}51Testing软件测试网z)n1i1WM6e4K&?E
public boolean isFemale(){
,T m T[ V0a v$RG0return female;51Testing软件测试网5`/k0T4r"CQ;i Q
}51Testing软件测试网f,rX/QR1_x,YBD
public String getName(){
6X)cATo%R[{0return name;51Testing软件测试网s'Pf4l,ts
}
-vP"f D5tuq0public int getAge(){
9d-{M8c/`t n X a0return age;
%wd5s+ki'W0}
k/jt'JDt0public String getClub(){
/J"y+lq6k6M)X:NA0return club;
ic$yml)q0Gp |On0}51Testing软件测试网_\Mq3eMZ
}
51Testing软件测试网|.hJdT#~S/^Ng

  下一步我们要做的是决定整个列表中的哪些孩子会被显示。我们可以在主用户接口类中做到这点,但是最好的是在一个叫做Swimmers的集合类中来做,这个类有一个getList()方法,它带有布尔型的参数来决定是男孩还是女孩:51Testing软件测试网oY"T:D+o7C\Y@g)e

//get a vector of swimmers who are (female)51Testing软件测试网8bT{!X I4u
public Vector getList(boolean female){51Testing软件测试网U7E!h ?]&G c
Vector v=new Vector();
i0H.uZ7ta|P0for(int i=0;i51Testing软件测试网+U+p2P Qzb*^-LR
Swimmer swm=(Swimmer)kids.elementAt(i);51Testing软件测试网;nm.q7|fP
if(swm.isFemal()==female) v.add(swm);51Testing软件测试网&EUNg+aq-k
}51Testing软件测试网pe-d&c"Y6Kq uB
return v;
/z ^])F't&L0}

TY'Y7{B^1y;u_0  我们又提供了一个不带参数的多态getList()方法来返回整个列表。

ZSELuGsI051Testing软件测试网n#pI]$B

  现在让我们进入程序中的第一个重要的部分,为了显示这些孩子的姓名,我们需要为三个单选按钮都添加一个事件监听器,使得每个都能显示出正确的孩子们:

9Ss8BykU~0public void actionPerformed(ActionEvent e){51Testing软件测试网(H]l*b*Z
//listen for button clicks and do the right thing
"t \h"lh1G%i0Object bj=e.getSource();
?'`P4Kg i0if(obj==female) loadFemales();
'CV-Za9B0if(obj==male) loadMales();
%Y%d&[ dP2A/|0if(obj==both) loadBoth();51Testing软件测试网Y,b KAG
}51Testing软件测试网d |'@o4a
private void loadFemales(){51Testing软件测试网 u#y4K.R2C0A} hpm
//display female swimmers51Testing软件测试网e#[UJ8U7\D
Vector v=swimmers.getList(true);
%TNd+m*oH3nyR0loadList(v);
.WK:A6V;e d]C@0}51Testing软件测试网|U/e9l;Cj ws1x0r
private void loadMales(){51Testing软件测试网^^"FR I7e
//display male swimmers
Qk HhU.l:?A0Vector v=swimmers.getList(false);51Testing软件测试网C(HO r6Ah
loadList(v);
:?5_(Xc'_.u7Uq0}51Testing软件测试网Wx*RI}+Y
51Testing软件测试网Q8~3_(B3g

  尽管这个方法在简单、小型的情况下工作得很好,但是它没有良好的可扩展性。如果你有10个按钮,扩展actionPerformed()方法来测试每个按钮和调用某些对应的操作,会使得可读性非常差。相反,我们最好在包含用户接口的类中去掉所有的判定。51Testing软件测试网 A&J8hw E%]

51Testing软件测试网$t zQP1J

  做到这点的一个途径是使用Command模式。当我们使用这个模式的时候,需要我们创立一个Command接口:

h@zh"u4\'_Q,N051Testing软件测试网lo3ZlAH

`3uP!JH@;X0
//the Command interface
d `+j%`n ]d/O0public interface Command{
8d)Q1H(fi?k%XA$S0public void execute();
aY Z~h:RL0}

k?*a-a*H ~"to9k0  我们将三个单选按钮扩展成特定的带有Command接口的类,我们就可以把对命令的执行从JFrame类转移到每个按钮的类。同时,我们创建一 个叫做SexButton的基类,并将ActionListener代码移到基类中,这样就不需要分别在每个按钮中添加事件监听器:

`z6J CD8D!p051Testing软件测试网v3Ey$G2K|q!l

ykILhG1@:W&~0
//abstract radio button class51Testing软件测试网5^tW|5]
public abstract class SexButton
P^%|X [X-Y0extends JRadioButton implements Command{51Testing软件测试网m-{m`8F^U\s
protected Swimmers simmers;
&`BH p3@([0//JawList is a subclass of JScrollPane contained a JList;51Testing软件测试网#OMP'ItGx6f&T M
protected JawList kidList;
9@7L#A9e@Sz0public SexButton(String title,Swimmers sw,
/XN7e m:S0JawList klist,ActionListener al){51Testing软件测试网VB-H2x'NQ)D-K
super(title);
m9gOj7G&Ks0swimmers=sw;51Testing软件测试网"H8Pyb9W D
kidList=klist;
1@ \3K:F ap/T0addActionListener(al);51Testing软件测试网rGI B!|:]
}
7H+~!Z [a P0//abstract execute method
,Y#b5f6gHf K0public abstract void execute();
O:S}h8X9V(kF0}

V2Z*X3|+v0F:\"z0  注意这个类必须被扩展以使得它有用,因为我们没有具体化execute()方法。这个基本的抽象按钮类只是我们从中导出的具体类的一个模板,这实际也是模板设计模式的一个简单例子。

,C#x:x ?)~[0

i&u6dR$}qj0

T.x,i6]3n`"`j E0Ar0
//radio button to select female swimmers51Testing软件测试网pr1g?t
public class FemaleButton extends SexButton{51Testing软件测试网;d6J/H VV/Z i+o$VSr
public execute(){
oFp(YVh6xw0Vector v=swimmers.getList(true);
d+@5ns)sl)c-c,z!G0loadList(v);
XF@&~BvH zP0}
Ug7f?9DU-qX0private void loadList(Vector v){51Testing软件测试网 g U+Nz N)c Z#f_
kidList.clear();
r#@c*es8qLK0for(int i=0;i51Testing软件测试网^,ob4\d g5w3OX-y
Swimmer swm=(Swimmer)v.elementAt(i);
V"G9{-l8QWbA0kidList.add(swm.getName());
s4k2t!]@z5sql.i0}
.`9z)g ^FAH0}
Et;@'N5ub T$Ep0}

!V'nQYvC*Y0  现在,来看一下我们完成的工作。所有的按钮都成为了Command按钮了,actionPerformed()方法也简化成下面这样简单的方法:

:m(x&J*i%LkY,M0

eUJ4];X7W0

(};l e;H!GHs/L:OZ.f0public void actionPerformed(ActionEvent e){
Ex_B,F,P2k:Q0Command cmd=(Command)e.getSource();
Fd3|Qa-k0h d}0cmd.execute();51Testing软件测试网YO2Yv7j$s
}
F+y[ R5i5~N_1`2{0

I,JmL_Q0  正如你看到的,这更加简单而且完全是可扩展的。

BB|}0Y8](b4Sp051Testing软件测试网;qC;s_ t LR

  但我们还有要做的事情。现在我们写了三个单选按钮类,它们知道如何载入一个列表框。在我们匆忙地想要从主类之外得到消息时,我们要求每个按钮都知道列表框。如果我们想要改成一个不同的显示方式,就必须修改这三个类。

:~+sW&PuQ;b051Testing软件测试网(] Q,]Nc;K&lohh

  如果我们在按钮和列表之间创建一个mediator(调停者)类就会好得多,因为它们彼此不需要知道对方。Mediator(调停者)设计模式 能够做到这一点。当一个按钮被点击时,我们创建一个调停者类载入列表。这样所有按钮只知道调停者,哪个列表被载入也只有调停者才知道,当我们想要改成一个 不同的显示方式就只需修改Mediator:

6X$U'g8D S&?0

fm?P}4t-[(?0

|f"I-p/oi3eFY*t.N0
public class Mediator{51Testing软件测试网O(OaYB Iy3GxG9n)C
private JawList kidList;
k3i2A0^+E0public Mediator(JawList klist){51Testing软件测试网!k;V+uoY,w&Q'vI.e
kidList=klist;51Testing软件测试网,fE[,j3kJ^,u
}
M0]\*YX0public void loadList(Vector v){51Testing软件测试网@5]4C/^FS
kidList.clear();
F5j'H2CHNS0for(int i=0;i
^8W e)lg O$t0Swimmer sw=(Swimmer)v.elementAt(i);
1D7o-K?z0D1O0kidList.add(sw.getName());51Testing软件测试网9Bx9St ay
}
Eiy3k mDO v0}
h8{H#i0Y p'M Y0}
51Testing软件测试网,CTL&`3gK

  这样我们的基类变成这样:

\ MaQYY i051Testing软件测试网`{+E F H0d z7I` ~

1NTeQ0CA5F d0
public abstract class SexButton
xN,FI UQ0extends JRadioButton implements Command{51Testing软件测试网l:bYJn2H%U#_"I,X
protected Swimmers swimmers;
7r6t4a0Z8M0protected Mediator med;
+|s+^|8G0public SexButton(String title,Swimmers sw,Mediator md,ActionListener al){
({^ ]9H-j`G"V0super(title);51Testing软件测试网 hm |j3o'R2@C7d
swimmers=sw;
W5tK5TD%l.V9pHm0med=md;
.kq&v!R'fh6`]4o0addActionListener(al);
;J}\1Y@.k0}
_1b Ir'~0m0public abstract void execute();51Testing软件测试网d%v)Lz'v7`
}
51Testing软件测试网 _XD#?B X.zc3~y

  而FemalButton则变成这样:51Testing软件测试网:G"_BY }e s$\

51Testing软件测试网}vl&Bro#E

51Testing软件测试网*H c-q/Y8l

public class FemaleButton extends SexButton{51Testing软件测试网Gm3h\V
//use the mediator to load the list51Testing软件测试网[F-Dw.L;`S'j
public void execute(){51Testing软件测试网V(l}2N7L
Vector v=swimmers.getList(true);
0t$UX"_P1F#A0y0med.loadList(v);
d_0z\~S8q0}
6`DwTB{2_0}
51Testing软件测试网4L v"b q*k~Cd

  你看,我们使用Mediator来分离按钮和列表,使它们互相不知道对方。

[ Z4D X6D051Testing软件测试网#V:CZ8ZFSX UD_

  除了可以使用命令、调停者、模板模式改进这个不到100行的Java代码的简单程序外,还可以使用观察者(Observer)模式。这个程序使用JListData将数据传给列表并从列表中获得数据,并用LawList观察JListData的变化。

uR'PK t'ha0

ZM,? q,GR e0

]a+H7U%~9ud,cP:ql0public class JListData extends AbstractListModel{
KH V}GV.oHey)f M0private Vector data;
[8v*B.~z)AgcI0public JListData(){51Testing软件测试网1~kx9^|%ep
data=new Vector();51Testing软件测试网7ojEe,?*h e2tF7K
}
J$Wx3l!}H;C9@d0public int getSize(){51Testing软件测试网;hM7@ I,w+C&QPwd
return data.size();
4@ XdT)P(CKd0}51Testing软件测试网(h mM0OEBUs
public Object getElementAt(int index){
*W c`7k `T~c6]0return data.elementAt(index);
1a-k'IAsSD0}51Testing软件测试网 \,f Yq!rG+G4@
public void addElement(String s){
6V?_)ad o [S9X0data.addElement(s);
*JpMc\0fireIntervalAdded(this,data.size()-1,data.size());
T{.P6fV0}51Testing软件测试网T&r1TBi)N!i
public void removeElement(String s){
|H&{D5U'wg,Q0data.removeElement(s);51Testing软件测试网Cp|&s d2e,^9B9k.jL
fireIntervalRemoved(this,0,data.size());51Testing软件测试网N1@MPH.h
}51Testing软件测试网#`*j8l:P7nc
public void clear(){
)})G3U8y&M9s-a`7d7o H&l0int size=data.size();51Testing软件测试网a6LI{8Ow`Y
data.clear();51Testing软件测试网G ~mL2W#_p,t
fireIntervalRemoved(this,0,size);
a$ax`Z?#Vf0}51Testing软件测试网uRn EhC
}
0R5x.N zO6wtx/v0public class JawList extends JScrollPane{51Testing软件测试网?aM'pc%W
private JList listwindow;
5j+f*Z&g Z0private JListData listContents;51Testing软件测试网~"j&|e*zBUh
public JawList(){
d(MJYFK3H6f'a0listContents=new JList51Testing软件测试网8olbsc9}6v j R

TAG:

 

评分:0

我来说两句

Open Toolbar