我编写了一个使用Swing for GUI的应用程序,通过GUI接受文件,解析Input,将其保存在DataList
中并将其发送到服务器。我关心我的程序的整个设计,我认为这不是很好。我正在使用Netbeans来设计GUI,并且有一个类MainClass
,它启动该GUI并具有对GUI的静态引用。还有一些ExecClasses
执行上述解析和发送数据。
+----------------------+ | MainClass (static) | |----------------------| +------+ -DataList +-----+ | | | | static| +-+--------------+-----+ |static reference | | |reference | |new () | new () | | | | | | | | | +-+--------v----+ +--v-----------+--+ | | | | | SwingGUIClass | | ExecClasses | | | | | +--/\-----------+ +-----------------+ | Input file
此处简要概述了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
可以执行ExecClass1
中的方法。我之所以选择这种方法是因为我有一个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。所以我的问题是:你会如何设计这个以及你能给我哪些一般指示?
答案 0 :(得分:7)
首先,不要在事物上做静态引用的逻辑,你可以在多个上下文中使用。例如,将来,您可以要求GUI界面的多个窗口与您的多个服务实例进行交互。
此外,你正在扼杀可测试性。
单独处理GUI和应用程序逻辑 - 不要在您的应用程序逻辑(exec类)中考虑GUI文本字段等。只需考虑输入和输出,并提供一个相互通信的类(控制器)。您可以向控制器中的应用程序逻辑提供数据,获取结果并在GUI中显示,如:
public void processFile( SomeInputFromGui input ) {
SomeResult result = applicationLogicObject.process( input );
guiObject.showResult( result );
}
您的组件应松散耦合,因此您可以重复使用并测试它们。您可以通过简单的依赖注入来实现这一点,例如将依赖项放在contructors / setter中:
public void initApplication() {
AppLogic logic = new AppLogic();
AppWindow window = new AppWindow();
AppController controller = new Controller( logic , window );
}
这是控制器初始化方法的非常简单的草案。有了它,您可以在其他地方测试/重用您的逻辑或GUI,如单元测试。
要从您的窗口移动业务逻辑,所有事件都被触发(按钮等),您可以创建一个界面,该界面将适用于您的窗口:
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 { ...
当然,您可以将其进一步拆分为单独的数据访问和商务逻辑。
并记住你所有的gui动作(事件,更新)都应该在swing事件线程中运行,所以你应该使用SwingUtils,因为 swing不是线程安全的:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
.... queued action that changes the gui ...
}
});
请记住,不要在逻辑类中使用new
对对象即时进行硬编码,例如,不要在控制器中设置new Window
和new ApplicationDataModel
- 因为您无法测试控制器独立或重复使用不同的逻辑/窗口实现 - 您可以创建一些类只是为了准备您的应用程序依赖项(创建组件并链接它们)和“启动它” - 它通常被称为工厂。 / p>
如果您的逻辑会变得更加复杂,请将其拆分为更多服务对象,您可以使用命令模式在gui中生成命令并对其进行处理(例如在线程安全队列中)在应用程序服务中 - 这也是撤消/重做能力的良好起点。
最后一件事 - 如果你有任何长时间运行的处理任务(即使花了1秒我们可以说它已经运行很长时间),记住直接调用它或在swingUtils中会冻结你的gui,所以对于lenghty操作创建单独的具有Thread,Executors,Runnable,SwingWorker等的线程(您可以使用观察者模式来监视进度等)。
请记住,这确实是一个很大的话题,本文仅提及一些小的一般性建议。
要采取的“其他方法”可以是使用已经提供的架构来创建Eclipse RCP或Netbeans Platform等GUI应用程序。