3 Swing 应用程序设计:哪个是最好的? [关闭]
Posted
技术标签:
【中文标题】3 Swing 应用程序设计:哪个是最好的? [关闭]【英文标题】:3 Swing applications designs : which is the best? [closed] 【发布时间】:2011-10-21 01:28:20 【问题描述】:我是桌面应用程序开发的新手,今年夏天我要交付一个相当大的项目。问题是代码必须非常清晰,所以我更新它时不会(太多)麻烦。
因此,我想要一个良好的“关注点分离”。对我来说最困难的部分是 View-Controller 分离。
现在,我阅读了很多教程、讨论等。我已经设计了 3 种不同方式的迷你应用程序。该应用程序很简单:单击一个按钮,将标签转换为“Hello world”。
你觉得这三个设计怎么样?
有没有更好的设计来满足我的期望?
设计 1
View1.java:
public View1()
initComponents();
this.controller = new Controller1(this);
private Controller1 controller;
public void updateLabel(String message)
this.jLabel1.setText(message);
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt)
this.controller.doSomething();
private void initComponents()
...
jButton1.addActionListener(new java.awt.event.ActionListener()
public void actionPerformed(java.awt.event.ActionEvent evt)
jButton1ActionPerformed(evt);
);
...
Controller1.java:
public class Controller1
public Controller1(View1 v)
this.view = v;
public void doSomething()
this.view.updateLabel("Hello world");
private View1 view;
设计 2
View2.java:
public View2()
initComponents();
this.controller = new Controller2(this);
jButton1.addActionListener(new java.awt.event.ActionListener()
public void actionPerformed(java.awt.event.ActionEvent evt)
controller.doSomething();
);
public void updateLabel(String message)
this.jLabel1.setText(message);
private Controller2 controller;
...
Controller2.java:
public class Controller2
public Controller2(View2 v)
this.view = v;
public void doSomething()
this.view.updateLabel("Hello world");
private View2 view;
设计 3
View3.java:
public View3()
initComponents();
this.controller = new Controller3(this);
this.jButton1.addActionListener(this.controller.listener);
private Controller3 controller;
public void updateLabel(String message)
this.jLabel1.setText(message);
...
Controller3.java:
public class Controller3
public Controller3(View3 v)
this.view = v;
this.listener = new MyListener(v);
private View3 view;
public MyListener listener;
MyListener.java:
public class MyListener implements ActionListener
private View3 view;
public MyListener(View3 v)
this.view = v;
public void actionPerformed(java.awt.event.ActionEvent evt)
this.view.updateLabel("Hello world");
【问题讨论】:
我一直在项目的模型方面工作。但我仍然想知道全球设计。我将在我的视图中有很多数据,可能由用户更改。最好的选择是视图和模型之间的直接链接:当文本字段更改时,模型对象会更新。那会很好。 您还可以查看 Swing 以及它如何模拟绑定。它非常简单,并且没有绑定到视图。我确实同意更新模型会很好。但是进行此更新的服务可能应该是一个单独的类,而不是在视图中。祝你好运! 【参考方案1】:我不喜欢这些设计。您将控制器与视图紧密耦合。假设您想在将来更改控制器实现,因此您必须进入所有类并更改类。相反,您应该将其注入。有很多库可以通过 Guice 或 Spring 之类的注释为您执行此操作,但我不会涉及这些。这是一个更好的设计。
public class View
private Controller controller;
public View(Controller controller)
this.controller = controller;
这是一个更简洁的设计,因为视图不必知道控制器的实现是什么。您可以稍后创建一个子类并传递它。
所以现在有了上面的设计,我想你可以看到你不应该将 View 传递给控制器。这又是不好的耦合。相反,您可以传递一个 onCallback 类,该类将在完成后执行。这是理解它的代码
jButton1.addActionListener(new ActionListener()
public void actionPerformed(ActionEvent evt)
controller.doSomething(new Runnable()
public void run()
updateLabel("Hello world");
);
);
然后在你的控制器中做
public void doSomething(Runnable callback)
// do work
SwingUtilties.invokeLater(callback);
如果您看起来完全符合我的建议,那就是移除任何类型的耦合。视图不应该要求控制器,它应该被给予。控制器不应该知道它应该只执行回调的视图。这很重要,因为如果您决定不使用 Swing,那么您的控制器中的 Swing 包就不会存在所有这些依赖项。
希望这一切都有帮助!
【讨论】:
另见outline和相关example。 哇,这是很酷的东西。如果我做对了,您将一个类传递给控制器,并且在其他一些过程之后控制器会回调该类。但是如果我的回调类变大了怎么办?您建议将其写在 view.java 中还是在另一个类中? 如果回调方法变得更复杂,那么我认为修改的正确位置仍然存在。这里的想法是视图应该负责它自己的回调而不是另一个实体。我不建议在另一个类中编写它,因为您必须将所有摆动组件传递给其他类。这成就很多吗?尝试将计算数据的类与更新 UI 的类解耦。当 UI 发生变化时,这是未来的一大胜利。 @user777466 已经有一段时间了。你有机会看看这是否对你有用吗?【参考方案2】:决定哪种模式最好在很大程度上取决于您要解决的问题。 Swing is already an MVC framework,因此您必须考虑在其之上添加另一层间接性是否值得。
由于您是 UI 编程新手,我建议您先将系统的 walking skeleton 放在一起,然后根据您从中学到的知识,决定您的架构。精心设计的架构可以轻松测试和重用组件。 MVP 和 MVVM 是 UI 设计模式的两种众所周知的方式。
对于您的玩具问题,您可以实现 MVP 或 MVVM,如下所示。请记住,您通常还会在每个接口之间使用接口,并且如果可以更改,则在 Model 上会有观察者。
MVP
public class Model
public String getWhatIWantToSay()
return "Hello World";
public class Presenter implements ActionListener
private final View view;
private final Model model;
public Presenter(Model model, View view)
this.model = model;
this.view = view;
view.addButtonListener(this);
public void actionPerformed(ActionEvent e)
view.setText(model.getWhatIWantToSay());
public class View
private JButton button = new JButton();
private JLabel label = new JLabel();
public void addButtonListener(ActionListener listener)
button.addActionListener(listener);
public void setText(String text)
label.setText(text);
MVVP
public class ModelView extends Observable
private final Model model;
private String text = "";
public ModelView(Model model)
this.model = model;
public void buttonClicked()
text = model.getWhatIWantToSay();
notifyObservers();
public class View implements Observer
private JButton button = new JButton();
private JLabel label = new JLabel();
private final ModelView modelView;
public View(final ModelView modelView)
this.modelView = modelView;
modelView.addObserver(this);
button.addActionListener(new ActionListener()
public void actionPerformed(ActionEvent e)
modelView.buttonClicked();
);
public void update(Observable o, Object arg)
label.setText(modelView.text);
【讨论】:
我真的很喜欢你提出的 MVP 设计,这对我来说非常清楚。虽然我会检查您指向 Swing 架构概述的链接,因为我觉得可能有一些隐藏的机制可以节省我一些时间。 我个人在工作中在 Swing 遗留代码库上使用 MVP。与 MVVM 相比,您必须创建更多类,并且测试起来有点困难,但您可以获得很多可重用性。 很好的答案,但不幸的是链接已经失效。【参考方案3】:我认为设计 2 是满足您的标准的最佳选择。
设计 1 的问题:在视图方面太复杂了。额外的方法使它看起来几乎就像里面有一个控制器。简单的更改将变得难以实施。
设计 3 的问题:这对控制器施加了太多压力。控制器不应该知道正在发生什么 Swing 事件。在该设计中,如果您希望基于 JList 而不是 JButton 发生操作,则必须更改视图和控制器,这很糟糕。
关于您的代码的其他 cmets:
使用 import 语句,这样您就不必在代码中包含类的包,例如:java.awt.event.ActionListener()
。
您在几个地方使用this.
是不必要的,这只会增加噪音。
正如 Amir 指出的那样,您的视图和控制器之间的耦合非常紧密,这是不必要的。
【讨论】:
【参考方案4】:另一种设计方法可能是这样的:
型号
package biz.tugay.toypro.model;
public interface LabelService
String getDateInRandomLocale();
package biz.tugay.toypro.model;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.concurrent.ThreadLocalRandom;
public class LabelServiceImpl implements LabelService
private final Locale availableLocalesJava[];
public LabelServiceImpl()
this.availableLocalesJava = DateFormat.getAvailableLocales();
@Override
public String getDateInRandomLocale()
final int randomIndex = ThreadLocalRandom.current().nextInt(0, availableLocalesJava.length);
final Locale locale = availableLocalesJava[randomIndex];
final Calendar calendar = Calendar.getInstance();
final DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.LONG, locale);
return dateFormat.format(calendar.getTime());
查看
package biz.tugay.toypro.view;
import biz.tugay.toypro.model.LabelService;
import javax.swing.*;
public class DateInRandomLocaleLabel extends JLabel
private final LabelService labelService;
public DateInRandomLocaleLabel(final LabelService labelService)
this.labelService = labelService;
public void showDateInRandomLocale()
final String dateInRandomLocale = labelService.getDateInRandomLocale();
setText(dateInRandomLocale);
package biz.tugay.toypro.view;
import javax.swing.*;
public class RandomizeDateButton extends JButton
public RandomizeDateButton()
super("Hit Me!");
package biz.tugay.toypro.view;
import javax.swing.*;
import java.awt.*;
public class DateInRandomLocalePanel extends JPanel
public DateInRandomLocalePanel(final JLabel dateInRandomLocaleLabel, final JButton randomizeDateButton)
final GridLayout gridLayout = new GridLayout(1, 2);
setLayout(gridLayout);
add(dateInRandomLocaleLabel);
add(randomizeDateButton);
package biz.tugay.toypro.view;
import javax.swing.*;
public class MainFrame extends JFrame
public void init()
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setSize(400, 50);
setVisible(true);
控制器
package biz.tugay.toypro.controller;
import biz.tugay.toypro.view.DateInRandomLocaleLabel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class RandomizeDateButtonActionListener implements ActionListener
private DateInRandomLocaleLabel dateInRandomLocaleLabel;
@Override
public void actionPerformed(final ActionEvent e)
dateInRandomLocaleLabel.showDateInRandomLocale();
public void setDateInRandomLocaleLabel(final DateInRandomLocaleLabel dateInRandomLocaleLabel)
this.dateInRandomLocaleLabel = dateInRandomLocaleLabel;
最后是我如何启动应用程序:
package biz.tugay.toypro;
import biz.tugay.toypro.controller.RandomizeDateButtonActionListener;
import biz.tugay.toypro.model.LabelService;
import biz.tugay.toypro.model.LabelServiceImpl;
import biz.tugay.toypro.view.DateInRandomLocaleLabel;
import biz.tugay.toypro.view.DateInRandomLocalePanel;
import biz.tugay.toypro.view.MainFrame;
import biz.tugay.toypro.view.RandomizeDateButton;
import javax.swing.*;
public class App
public static void main(String[] args)
final LabelService labelService = new LabelServiceImpl();
// View
final DateInRandomLocaleLabel dateInRandomLocaleLabel = new DateInRandomLocaleLabel(labelService);
final RandomizeDateButton randomizeDateButton = new RandomizeDateButton();
final DateInRandomLocalePanel dateInRandomLocalePanel = new DateInRandomLocalePanel(dateInRandomLocaleLabel, randomizeDateButton);
final MainFrame mainFrame = new MainFrame();
mainFrame.getContentPane().add(dateInRandomLocalePanel);
// Controller
final RandomizeDateButtonActionListener randomizeDateButtonActionListener = new RandomizeDateButtonActionListener();
// Bind Controller to the View..
randomizeDateButton.addActionListener(randomizeDateButtonActionListener);
// Bind View to the Controller..
randomizeDateButtonActionListener.setDateInRandomLocaleLabel(dateInRandomLocaleLabel);
// Show the main frame..
SwingUtilities.invokeLater(new Runnable()
@Override
public void run()
mainFrame.init();
);
这就是应用程序的样子:
我认为没有只有 1 个正确答案,这取决于您希望如何绑定您拥有的组件..
您可能会发现以下内容也很有用:
http://www.tugay.biz/2017/05/trying-to-understand-model-view.html http://www.tugay.biz/2017/05/swingynotes-design-modified.html http://www.tugay.biz/2017/05/refactoring-spring-swing-mvc.html【讨论】:
以上是关于3 Swing 应用程序设计:哪个是最好的? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章
使用java swing做个界面,中间的容器部分是加载一个网页,该如何解决、可以提供代码参考最好