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做个界面,中间的容器部分是加载一个网页,该如何解决、可以提供代码参考最好

使用java swing做个界面,中间的容器部分是加载一个网页,该如何解决、可以提供代码参考最好

将 Swing 应用程序转换为 Java EE

数据库设计问题 - 哪个是最好的解决方案?

哪个 ORM 框架可以最好地处理 MVCC 数据库设计?

头歌:Python开发技术—面向对象程序设计3 详细注释(第1关:工资结算系统+第2关:设计LFU缓存类)