Java 3个Swing应用程序设计:哪一个是最好的?
我是桌面应用程序开发的新手,今年夏天我有一个相当大的项目要交付。问题是,代码必须非常清晰,所以当我更新它时,我不会遇到(很多)麻烦 因此,我想要一个良好的“关注点分离”。对我来说,最困难的部分是视图-控制器分离 现在,我已经阅读了很多教程、讨论等,我用3种不同的方式设计了一个迷你应用程序。该应用程序很简单:点击一个按钮,将标签转换为“Hello world” 你觉得这三种设计怎么样 是否有更好的设计来满足我的期望 设计1 View1.java:Java 3个Swing应用程序设计:哪一个是最好的?,java,model-view-controller,swing,architecture,Java,Model View Controller,Swing,Architecture,我是桌面应用程序开发的新手,今年夏天我有一个相当大的项目要交付。问题是,代码必须非常清晰,所以当我更新它时,我不会遇到(很多)麻烦 因此,我想要一个良好的“关注点分离”。对我来说,最困难的部分是视图-控制器分离 现在,我已经阅读了很多教程、讨论等,我用3种不同的方式设计了一个迷你应用程序。该应用程序很简单:点击一个按钮,将标签转换为“Hello world” 你觉得这三种设计怎么样 是否有更好的设计来满足我的期望 设计1 View1.java: public View1() { init
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");
}
}
这些设计我都不喜欢。您正在将控制器与视图紧密耦合。假设您希望在将来更改控制器实现,因此必须进入所有类并更改类。相反,你应该让它注射。有很多lib可以通过诸如或之类的注释来为您实现这一点,但我不打算详细介绍这些。这是一个更好的设计
public class View{
private Controller controller;
public View(Controller controller) {
this.controller = controller;
}
}
这是一个更简洁的设计,因为视图不必知道控制器的实现是什么。您可以稍后创建一个子类并传递它
现在,通过上面的设计,我认为您可以看到不应该将视图传递给控制器。这又是一个不好的耦合。相反,您可以传递一个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包的依赖
希望这一切都有帮助 我认为设计2是满足您标准的最佳选择 设计问题1:视图方面太复杂。额外的方法使它看起来像里面有一个控制器。简单的更改将变得难以实现 设计3的问题:这会给控制器带来太多压力。控制器不应该知道正在发生什么Swing事件。在这种设计中,如果希望基于JList而不是JButton执行操作,则必须更改视图和控制器,这是错误的 关于代码的其他注释:
- 使用import语句,这样就不必在代码中包含类的包,如:
java.awt.event.ActionListener()
- 您可以使用
在一些不需要的地方使用,这只会增加噪音此功能。
- 正如Amir指出的,视图和控制器之间的耦合非常紧密,这是不必要的
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);
}
}
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);
}
}
MVVP
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);
}
}
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);
}
}
另一种设计方法可以是这样的: 型号
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();
}
});
}
}
这就是应用程序的外观:
我认为没有唯一正确的答案,这取决于您希望如何连接现有组件
您可能会发现以下内容也很有用: