Java Swing设计指南
我编写了一个使用Swing for GUI的应用程序,通过GUI接受文件,解析Input,将其保存在DataList
并将其发送到服务器。 我关心我的程序的整个设计,我认为这不是很好。 我正在使用Netbeans来设计GUI,并有一个MainClass
类,它启动该GUI并具有GUI的静态引用。 还有一些ExecClasses
执行上述解析和发送数据。
+ ---------------------- + | MainClass(静态)| | ---------------------- | + ------ + -DataList + ----- + | | | | 静态| + - + -------------- + ----- + |静态 参考| | |引用 | | new()| new()| | | | | | | | | + - + -------- v ---- + + - v ----------- + - + | | | | | SwingGUIClass | | ExecClasses | | | | | + - / \ ----------- + + ----------------- + | 输入文件
这里是MainClass
的简短概述:
public class MainClass { private static MainClass mainClass; private static ExecClass1 ex1; private static ExecClass2 ex2; private static ExecClass3 ex3; public static void startExecClass2(String param){ ex2 = new ExecClass2(param); }
我正在使用这些引用,以便SwingGUIClass
可以在SwingGUIClass
中执行一个方法。 我选择了这种方法,因为我有一个TextArea需要从其中一个ExecClasses中获取数据并将其显示在GUI中。 因为我无法从ExecClass修改TextArea。
public class SwingGUIClass { [...] private void ButtonActionPerformed(java.awt.event.ActionEvent evt) { Label.setText(MainClass.getList()); } private void Button2ActionPerformed(java.awt.event.ActionEvent evt) { MainClass.startExecClass2(Button2.getText()); }
我知道这远非伟大的设计,并没有遵循一些良好的实践指南,例如MVC。 所以我的问题是: 你如何设计这个以及你可以给我哪些一般指示?
首先,不要在事物上做静态引用的逻辑,可以在多个上下文中使用。 例如,将来,您可以要求GUI界面的多个窗口与您的多个服务实例交互。
你也在杀死可测试性。
分别处理GUI和应用程序逻辑 – 不要在应用程序逻辑(exec类)中考虑GUI文本字段等。只需考虑输入和输出,并提供一个相互通信的类(控制器)。 您可以向控制器中的应用程序逻辑提供数据,获取结果并在GUI中显示,如:
public void processFile( SomeInputFromGui input ) { SomeResult result = applicationLogicObject.process( input ); guiObject.showResult( result ); }
您的组件应该是松耦合的,因此您可以重用并测试它们。 您可以通过简单的dependency injection实现这一点,例如将依赖项放在contructors / setter中:
public void initApplication() { AppLogic logic = new AppLogic(); AppWindow window = new AppWindow(); AppController controller = new Controller( logic , window ); }
这是控制器初始化方法的非常简单的草案。 有了它,您可以在其他地方测试/重用您的逻辑或GUI,例如unit testing。
要从窗口中移动业务逻辑,触发所有事件(按钮等),您可以创建一个适用于您的窗口的界面:
public interface ProcessingController { public void processFile( File x ); public void checkIntegrity(); public SomeDataValues getCurrentDataValues(); }
您可以在控制器( implements
)中实现此逻辑,并将其用作GUI事件接收器:
window.setProcessingController( controller );
…
private void ButtonActionPerformed(java.awt.event.ActionEvent evt) { processingController.processText( jMyTextField.getText() ); }
现在,您可以与窗口和控制器进行双向通信。
这些是基本点,为您提供可测试性和能力,使您可以根据需要制作尽可能多的逻辑/控制器/窗口。 此外,您还有松耦合组件:您可以为测试目的注入几乎空的AppLogic存根,或者伪造AppWindow以模拟测试中的用户操作。 当然要替换组件,您应该提取干扰并提供特定的实现:
SwingAppWindow implements ApplicationUserInterface { ... SQLDataManager implements ApplicationDataLogic { ... BasicController implements ProcessingController { ...
当然,您可以进一步拆分它以分离数据访问和bussines逻辑 。
并记住你所有的gui动作(事件,更新)应该在swing事件线程中运行,所以你应该使用SwingUtils,因为swing不是线程安全的 :
SwingUtilities.invokeLater(new Runnable() { public void run() { .... queued action that changes the gui ... } });
记住不要在逻辑类中使用new
硬编码对象即时,例如不要在控制器中创建new Window
和new ApplicationDataModel
– 因为你不能独立地测试你的控制器或者在不同的逻辑/窗口实现中重用它 – 你可以创建一些类只是为了准备你的应用程序依赖项(创建组件并链接它们)和“启动它” – 它通常被称为工厂 。
如果你的逻辑会增长并变得更加复杂,可以将它分成更多的服务对象,你可以使用命令模式在你的gui中生成命令并在应用程序服务中处理它(例如在线程安全队列中) – 这也是很好的开始指向撤消/重做能力。
最后一件事 – 如果你有任何长时间运行的处理任务(即使花了1秒我们可以说它已经运行很长时间),记住直接调用它或在swingUtils中会冻结你的gui,所以对于lenghty操作创建单独的线程Thread,Executors,Runnable,SwingWorker等(你可以使用观察者模式来监控进度等)。
请记住,这确实是一个很大的话题,本文仅提到一些小的一般性建议。
要采取的“其他方法”可以是使用已经提供的架构来创建Eclipse RCP或Netbeans Platform等GUI应用程序。