gpt4 book ai didi

java - Model-View-Presenter被动 View : bootstraping - who displays the view initially?

转载 作者:搜寻专家 更新时间:2023-10-31 19:46:13 24 4
gpt4 key购买 nike

在“被动 View 模型 View 呈现器”模式中,谁负责显示 View ?我已经找到了其他MVP版本的相关答案,但是它们似乎不适用于被动 View 版本。

我有一个使用Java Swing的具体示例。这很简单,但是基本上我们有一个SwingCustomersView,它在内部构建一个带有表(客户列表)和显示当前所选客户年龄的标签的JPanel。当在表中选择一个客户时,演示者将从模型中检索所选的客户年龄。我认为该示例是MVP Passive View的正确实现,但是如果我错了,请更正我。

问题是我们如何引导这些类?例如,如果我们想在JFrame中显示SwingCustomersView。一个人会怎么做?我想像的东西如下:

void launcher() {
CustomersModel model = new CustomersModel();
SwingCustomersView view = new SwingCustomersView();
CustomersPresenter presenter = new CustomersPresenter(view, model);
}

这是初始接线,但什么也没显示。我们如何实际显示 View ?显示 View 是(1) launcher(),(2) SwingCustomersView或(3) CustomersPresenter的责任吗?不幸的是,我不认为这些都是很好的,从下面我的想法中可以看出。也许还有另一种方式?

(1.a):启动器

使 SwingCustomersView扩展JFrame并将其内部JPanel添加到其自身的内容 Pane 中。然后我们可以这样做:
void launcher() {
CustomersModel model = new CustomersModel();
SwingCustomersView view = new SwingCustomersView();
CustomersPresenter presenter = new CustomersPresenter(view, model);
view.setVisible(true); // Displays the view
}

但是,在这种情况下,我们不使用 presenter实例进行任何操作。那不是很奇怪吗?它就在这里进行接线,我们也可以删除变量并执行 new CustomersPresenter(view, model)

(2):SwingCustomersView

使 SwingCustomersView在应该向其添加内部JPanel的构造函数中采用 Container:
void launcher() {
CustomersModel model = new CustomersModel();
JFrame frame = new JFrame("Some title");
SwingCustomersView view = new SwingCustomersView(frame.getContentPane());
CustomersPresenter presenter = new CustomersPresenter(view, model);
frame.pack();
frame.setVisible(true) // Displays the view
}

但是,与(1)相同的问题是: presenter实例不执行任何操作。好像很奇怪此外,使用(1)和(2)都可以在演示者挂机之前显示 View ,我想这在某些情况下可能会导致奇怪的结果。

(3):客户演示者

使 CustomersPresenter负责显示 View 。然后我们可以这样做:
void launcher() {
CustomersModel model = new CustomersModel();
SwingCustomersView view = new SwingCustomersView();
CustomersPresenter presenter = new CustomersPresenter(view, model);
presenter.show() // Displays the view
}

这样可以解决构造后不将其用于任何东西的问题。但是我不更改 CustomersView接口(interface)或使 CustomersPresenter过于依赖底层GUI实现,就看不到该怎么做。此外,显示 View 听起来不像演示逻辑,因此似乎不属于演示者。

例子
public class CustomersModel {
private List<Customer> customers;

public CustomersModel() {
customers = new ArrayList<Customer>();
customers.add(new Customer("SomeCustomer", "31"));
customers.add(new Customer("SomeCustomer", "32"));
}

public List<Customer> getCustomers() {
return customers;
}
}

public class Customer {
public String name;
public String age;

public Customer(String name, String age) {
this.name = name;
this.age = age;
}
}

public interface CustomersView {
void addCustomerSelectionChangeListener(ItemListener listener);
void onNewActiveCustomer(String age);
void onNewCustomers(List<String> newCustomers);
}

public class SwingCustomersView implements CustomersView {
// Swing components here all put into a main JPanel

public void addCustomerSelectionChangeListener(ItemListener listener) {
// Add event listener to table
}

public void onNewActiveCustomer(String age) {
// Display age in label beneath table
}

public void onNewCustomers(List<String> newCustomers) {
// Display customers in table
}
}

public class CustomersPresenter {
private final CustomersView view;
private final CustomersModel model;

public CustomersPresenter(CustomersView view, CustomersModel model) {
this.view = view;
this.model = model;
initPresentationLogic();
populateView();
}

private void initPresentationLogic() {
view.addCustomerSelectionChangeListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
String selectedName = (String)e.getItem();
List<Customer> customers = model.getCustomers();
for (Customer c : customers)
if (c.name.equals(selectedName))
view.onNewActiveCustomer(c.age);
}
});
}

private void populateView() {
List<Customer> customers = model.getCustomers();
List<String> names = new ArrayList<String>();
for (Customer c : customers)
names.add(c.name);

// View will now populate its table, which in turn will call customerSelectionChangeListener
// so the view 'automagically' updates the selected contact age too
view.onNewCustomers(names);
}
}

最佳答案

选项(3)一直。演示者的工作是“控制” View ,包括使其可见。是的,您需要添加到 View 的界面以允许这种情况发生,但这没什么大不了的。请记住,您可以使 View 尽可能被动。没有逻辑!

工作示例:

我偶然发现了使用MVC架构的简单Swing游戏的this example。由于我是使用MVP而不是MVC编写Swing应用程序的,因此如果这个例子是MVC的真实和纯粹的例子,我不能授权。在我看来,这还可以,并且作者trashgod在使用Swing的SO方面已经证明了自己,所以我会认为它是合理的。

作为练习,我决定使用MVP架构重写它。

驱动程序:

正如您在下面的代码中看到的那样,这非常简单。您应该跳出来的是关注点分离(通过检查构造函数):

  • 模型类是独立的,不了解 View 或演示者。
  • View 接口(interface)是由独立的GUI类实现的,它们都不了解模型或演示者。
  • Presenter 类同时了解模型和 View 。

  • 代码:
    import java.awt.*;

    /**
    * MVP version of https://stackoverflow.com/q/3066590/230513
    */
    public class MVPGame implements Runnable
    {
    public static void main(String[] args)
    {
    EventQueue.invokeLater(new MVPGame());
    }

    @Override
    public void run()
    {
    Model model = new Model();
    View view = new Gui();
    Presenter presenter = new Presenter(model, view);
    presenter.start();
    }
    }

    以及我们将用于游戏的GamePiece:
    import java.awt.*;

    public enum GamePiece
    {
    Red(Color.red), Green(Color.green), Blue(Color.blue);
    public Color color;

    private GamePiece(Color color)
    {
    this.color = color;
    }
    }

    模型:首先,模型的工作是:
  • 为UI提供数据(应要求提供)
  • 数据验证(应要求)
  • 数据的长期存储(应要求)

  • 代码:
    import java.util.*;

    public class Model
    {
    private static final Random rnd = new Random();
    private static final GamePiece[] pieces = GamePiece.values();

    private GamePiece selection;

    public Model()
    {
    reset();
    }

    public void reset()
    {
    selection = pieces[randomInt(0, pieces.length)];
    }

    public boolean check(GamePiece guess)
    {
    return selection.equals(guess);
    }

    public List<GamePiece> getAllPieces()
    {
    return Arrays.asList(GamePiece.values());
    }

    private static int randomInt(int min, int max)
    {
    return rnd.nextInt((max - min) + 1) + min;
    }
    }

    View :此处的想法是通过尽可能多地剥离应用程序逻辑来使它尽可能“笨拙”(目标是不使用任何逻辑)。好处:
  • 该应用程序现在可以100%进行JUnit的测试,因为Swing代码
  • 中未混入任何应用程序逻辑
  • 您可以在不启动整个应用程序的情况下启动GUI,这使得原型(prototype)制作的速度更快

  • 代码:
    import java.awt.*;
    import java.awt.event.*;
    import java.util.List;

    public interface View
    {
    public void addPieceActionListener(GamePiece piece, ActionListener listener);
    public void addResetActionListener(ActionListener listener);
    public void setGamePieces(List<GamePiece> pieces);
    public void setResult(Color color, String message);
    }

    和GUI:
    import java.awt.*;
    import java.awt.event.*;
    import java.util.List;
    import javax.swing.*;

    /**
    * View is "dumb". It has no reference to Model or Presenter.
    * No application code - Swing code only!
    */
    public class Gui implements View
    {
    private JFrame frame;
    private ColorIcon icon;
    private JLabel resultLabel;
    private JButton resetButton;
    private JButton[] pieceButtons;
    private List<GamePiece> pieceChoices;

    public Gui()
    {
    frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    icon = new ColorIcon(80, Color.WHITE);
    }

    public void setGamePieces(List<GamePiece> pieces)
    {
    this.pieceChoices = pieces;

    frame.add(getMainPanel());
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
    }

    public void setResult(Color color, String message)
    {
    icon.color = color;
    resultLabel.setText(message);
    resultLabel.repaint();
    }

    private JPanel getMainPanel()
    {
    JPanel panel = new JPanel(new BorderLayout());
    panel.add(getInstructionPanel(), BorderLayout.NORTH);
    panel.add(getGamePanel(), BorderLayout.CENTER);
    panel.add(getResetPanel(), BorderLayout.SOUTH);
    return panel;
    }

    private JPanel getInstructionPanel()
    {
    JPanel panel = new JPanel();
    panel.add(new JLabel("Guess what color!", JLabel.CENTER));
    return panel;
    }

    private JPanel getGamePanel()
    {
    resultLabel = new JLabel("No selection made", icon, JLabel.CENTER);
    resultLabel.setVerticalTextPosition(JLabel.BOTTOM);
    resultLabel.setHorizontalTextPosition(JLabel.CENTER);

    JPanel piecePanel = new JPanel();
    int pieceCount = pieceChoices.size();
    pieceButtons = new JButton[pieceCount];

    for (int i = 0; i < pieceCount; i++)
    {
    pieceButtons[i] = createPiece(pieceChoices.get(i));
    piecePanel.add(pieceButtons[i]);
    }

    JPanel panel = new JPanel(new BorderLayout());
    panel.add(resultLabel, BorderLayout.CENTER);
    panel.add(piecePanel, BorderLayout.SOUTH);

    return panel;
    }

    private JPanel getResetPanel()
    {
    resetButton = new JButton("Reset");

    JPanel panel = new JPanel();
    panel.add(resetButton);
    return panel;
    }

    private JButton createPiece(GamePiece piece)
    {
    JButton btn = new JButton();
    btn.setIcon(new ColorIcon(16, piece.color));
    btn.setActionCommand(piece.name());
    return btn;
    }

    public void addPieceActionListener(GamePiece piece, ActionListener listener)
    {
    for (JButton button : pieceButtons)
    {
    if (button.getActionCommand().equals(piece.name()))
    {
    button.addActionListener(listener);
    break;
    }
    }
    }

    public void addResetActionListener(ActionListener listener)
    {
    resetButton.addActionListener(listener);
    }

    private class ColorIcon implements Icon
    {
    private int size;
    private Color color;

    public ColorIcon(int size, Color color)
    {
    this.size = size;
    this.color = color;
    }

    @Override
    public void paintIcon(Component c, Graphics g, int x, int y)
    {
    Graphics2D g2d = (Graphics2D) g;
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
    RenderingHints.VALUE_ANTIALIAS_ON);
    g2d.setColor(color);
    g2d.fillOval(x, y, size, size);
    }

    @Override
    public int getIconWidth()
    {
    return size;
    }

    @Override
    public int getIconHeight()
    {
    return size;
    }
    }
    }

    立即可能看不到的是View界面有多大。对于GUI上的每个Swing组件,您可能需要:
  • 添加/删除组件的监听器,该监听器的类型很多(ActionListener,FocusListener,MouseListener等)。
  • 获取/设置组件
  • 上的数据
  • 设置组件的“可用性”状态(启用,可见,可编辑,可聚焦等)。

  • 这会很快变得笨拙。作为解决方案(此示例中未显示),将为每个字段创建一个 key ,并且GUI用其 key 注册每个组件(使用HashMap)。然后,代替View定义方法,例如:
    public void addResetActionListener(ActionListener listener);
    // and then repeat for every field that needs an ActionListener

    您将有一个方法:
    public void addActionListener(SomeEnum someField, ActionListener listener);

    其中“SomeEnum”是一个 enum,它定义了给定UI上的所有字段。然后,当GUI收到该调用时,它会查找适当的组件以对该方法进行调用。所有这些繁重的工作都将在实现View的抽象父类(super class)中完成。

    演示者:的职责是:
  • 使用其初始值初始化 View
  • 通过附加适当的监听器
  • 来响应View上的所有用户交互
  • 必要时更新View的状态
  • 从 View 中获取所有数据,然后传递给模型进行保存(如有必要)

  • 代码(请注意,这里没有Swing):
    import java.awt.*;
    import java.awt.event.*;

    public class Presenter
    {
    private Model model;
    private View view;

    public Presenter()
    {
    System.out.println("ctor");
    }

    public Presenter(Model model, View view)
    {
    this.model = model;
    this.view = view;
    }

    public void start()
    {
    view.setGamePieces(model.getAllPieces());
    reset();

    view.addResetActionListener(new ActionListener()
    {
    public void actionPerformed(ActionEvent e)
    {
    reset();
    }
    });

    for (int i = 0; i < GamePiece.values().length; i++)
    {
    final GamePiece aPiece = GamePiece.values()[i];
    view.addPieceActionListener(aPiece, new ActionListener()
    {
    public void actionPerformed(ActionEvent e)
    {
    pieceSelected(aPiece);
    }
    });
    }
    }

    private void reset()
    {
    model.reset();
    view.setResult(Color.GRAY, "Click a button.");
    }

    private void pieceSelected(GamePiece piece)
    {
    boolean valid = model.check(piece);
    view.setResult(piece.color, valid ? "Win!" : "Keep trying.");
    }
    }

    请记住,MVP体系结构的每个部分都可以/将委派给其他类(对其他2个部分隐藏)来执行其许多任务。 Model,View和Presenter类只是代码库层次结构中的高级部分。

    关于java - Model-View-Presenter被动 View : bootstraping - who displays the view initially?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22029808/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com