这对于使用Swing之类的东西已经很难理解了,它已经使用了MVC的形式,尽管更像VC-M,其中模型与视图和控制器是分离的,但是视图和控制器是结合在一起的。
考虑一下a
JButton,您没有提供一个控制器来管理用户按下键或用鼠标单击它时如何触发它,这是在内部完成的,并且在发生操作时会收到通知。
考虑到这一点,您需要允许视图是半自我管理的。例如,根据您的要求,该视图将具有一个按钮和文本字段。
视图本身将管理用户和按钮本身之间的交互(
ActionListener例如,维护内部),但随后将向控制器提供有关控制器可能感兴趣的任何状态更改的通知。
从MVC的角度来看,视图和模型彼此之间一无所知,而控制器将对其进行管理。这与Swing的工作方式有点矛盾,因为Swing允许您将模型直接传递到视图,几乎可以看到任何Swing组件。
这并不意味着您无法使事情起作用,而是您需要知道该概念在哪里会动摇,或者需要“按摩”以使它更好地工作。
通常,当我处理这类事情时,我会退后一步,例如看更广阔的前景。
- 您有一个可以接受文本并产生文本或对其进行更改的视图
- 您有一个可以加载和修改文本,但几乎不提供其他事件的模型
- 您有一个控制器,该控制器希望从模型中获取文本并将其提供给视图,并监视视图对文本的更改并在模型中进行更新
现在,MVC在“代码到接口(不是实现)”的概念上可以很好地工作,在那种程度上,我倾向于从合同开始。
查看合同…
public interface TextView { public void setText(String text); public String getText(); public void addTextViewObserver(TextViewObserver observer); public void removeTextViewObserver(TextViewObserver observer);}public interface TextViewObserver { public void textWasChanged(TextView view);}现在,视图的要求之一是当文本以某种有意义的方式更改时生成事件,为此,我使用了一种简单的观察者模式来实现。现在您可以争辩说控制器是观察者,但是在我看来,控制器可能具有我不想公开给视图的功能(例如,例如模型)
示范合同…
接下来是模型…
public interface TextModel { public String getText(); public void setText(String text);}真的很简单。现在,您可能会考虑
Exception在这些方法中添加某种方法,以允许模型由于某种原因而失败,但该方法
Exception应尽可能地通用(甚至是custom
Exception),以便可以替换实现你需要
控制器合同…
最后,控制器…
public interface TextViewController { public TextView getTextView(); public TextModel getTextModel();}再次,非常简单。您可能对控制器有更复杂的要求,但是对于本示例,这就是我们真正需要的。
实施…
视图…
public class TextViewPane extends JPanel implements TextView { private JTextField textField; private JButton updateButton; private List<TextViewObserver> observers; public TextViewPane() { observers = new ArrayList<>(25); textField = new JTextField(25); updateButton = new JButton("Update"); updateButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { fireTextWasChanged(); } }); setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridwidth = GridBagConstraints.REMAINDER; add(textField, gbc); add(updateButton, gbc); } @Override public void setText(String text) { textField.setText(text); } @Override public String getText() { return textField.getText(); } @Override public void addTextViewObserver(TextViewObserver observer) { observers.add(observer); } @Override public void removeTextViewObserver(TextViewObserver observer) { observers.remove(observer); } protected void fireTextWasChanged() { for (TextViewObserver observer : observers) { observer.textWasChanged(this); } }}模型…
public class SimpleTextModel implements TextModel { private String text = "This is some text"; @Override public String getText() { return text; } @Override public void setText(String text) { this.text = text; }}控制器…
public class SimpleTextController implements TextViewController, TextViewObserver { private TextView view; private TextModel model; public SimpleTextController(TextView view, TextModel model) { this.view = Objects.requireNonNull(view, "TextView can not null"); this.model = Objects.requireNonNull(model, "TextModel can not be null"); view.addTextViewObserver(this); } @Override public TextView getTextView() { return view; } @Override public TextModel getTextModel() { return model; } @Override public void textWasChanged(TextView view) { getTextModel().setText(view.getText()); }}把它放在一起…
TextViewPane view = new TextViewPane();TextModel model = new SimpleTextModel();TextViewController controller = new SimpleTextController(view, model);Jframe frame = new Jframe("Testing");frame.setDefaultCloseOperation(Jframe.EXIT_ON_CLOSE);frame.add(view);frame.pack();frame.setLocationRelativeTo(null);frame.setVisible(true);现在,所有这些只是一个可能解决方案的示例。例如,您可以具有一个控制器实现,该控制器实现具有模型或视图或两者的特定实现。
关键是,您根本不在乎。控制器不在乎视图的实现方式,仅在乎它将生成
textWasChanged事件。该模型根本不关心视图(反之亦然),而控制器也不关心模型,只是它会获取并设置一些文本。
对于更复杂的示例,您可以看一下Java和GUI-根据MVC模式,ActionListeners在哪里?
经过思考
- 这只是解决问题的一种可能方法。例如,您可以将视图限制为单个观察者。
- 您应该一直在思考“我可以更改MVC的任何一部分,并且它仍然可以工作吗?” 这使您考虑更改实施的任何一部分可能对周围合同产生的潜在问题。您应该指出,每一层的实现方式都无关紧要
- 视图可以充当另一个子视图的控制器(或充当子视图的另一个控制器的容器)。有时这可能会吓到人们,但是视图有可能充当一个或多个子控制器/视图的父容器,这使您可以开发复杂的UI
- 不要在合同中公开实现细节,例如,该模型不应抛出
SQLException
,因为另一个实现可能不基于基于SQL的解决方案。不要公开UI元素,这意味着所有实现都将需要实现那些元素。如果我想要实现JComboBox
向用户呈现而不是的视图,会发生什么JTextField
?这也是我不在ActionListener
视图协定中使用a的原因,因为我不知道textWasChanged
视图的实现实际上如何生成事件



