为了账号安全,请及时绑定邮箱和手机立即绑定

听众放置坚持传统(非中介)MVC模式

/ 猿问

听众放置坚持传统(非中介)MVC模式

慕姐4208626 2019-08-23 10:38:31

听众放置坚持传统(非中介)MVC模式

我正在Swing中实现一个程序,我在Swing中阅读了Nirmal对这种模式的实现,这似乎表明了对整个“职责分离”概念的相当优雅的处理。

但是,由于我正在开发一个比Nirml发布的更复杂的程序,它由一个JFrame容器组成,我寻求指导如何正确实现MVC。

我的程序将由子容器等组成。我很好奇Controller应该如何实现定义和分配View的所有侦听器背后的逻辑..或者如果为每个View组件定义侦听器的控制器是否实用?

看来我在View的顶级容器中需要一个方法来允许Controller调用视图来向相关组件添加一个Listener?所以我需要一个方法链,每个方法都将侦听器从顶层容器传递到持有组件的直接容器。最后用容器调用addActionListener()就可以了。

这是在MVC中处理侦听器的正确方法吗?

是否在MVC中强制控制View中每个组件的所有侦听器,或者是一种有用的实践?这也意味着我在顶级容器(View)中创建方法,以便为Controller提供一种方法,将侦听器分配给子容器中的每个组件?


查看完整描述

3 回答

?
弑天下

好的,首先,Swing已经实现了MVC的一种形式,尽管是以VC-M的形式。这意味着你不应该试着直接将Swing限制在一个纯粹的MVC中,因为你会非常失望并且花费大量的时间来制作他们不应该做的黑客攻击。

相反,您可以围绕Swing包装MVC,允许它围绕API工作。

在我看来,一个控制器不需要知道,也不应该关心视图或模型是如何实现的,但它应该只关心它如何与它们一起工作(我有太多的开发人员掌握了UI组件和他们一起做他们不应该做的事情,并在我们改变实施时打破API。最好隐藏那种细节)

在这种情况下,您可以将视图视为自包含实体 - 它具有控件并且它可以独立于控制器执行操作。控制器不关心实现细节。它所关心的是获取信息,并在合同描述的某些事件发生时被告知。它不应该关心它是如何产生的。

例如,假设您有登录视图。控制器只想知道用户输入的用户名和密码以及何时应该验证该信息。

假设您实现了视图/控制器以公开JTextFieldJPasswordField开始,但稍后,您的用户希望将用户名选择限制为特定列表(可能由模型提供)。现在,您的控制器中存在实施细节,这些细节已不再适用,您必须为此新用例手动更改或创建新的MVC。

相反,如果您只是声明该视图具有用户名和密码的getter以及某种类型的事件侦听器会告诉控制器何时需要验证凭据?那么现在,您只需提供一个新视图,无需修改控制器。控制器不会关心如何生成这些值。

至于你问题的更大方面。

我的程序将由子容器等组成。我很好奇Controller应该如何实现定义和分配View的所有侦听器背后的逻辑..或者如果定义每个View组件的侦听器的控制器甚至是实用的?

看来我在View的顶级容器中需要一个方法来允许Controller调用视图来向相关组件添加一个Listener?所以我需要一个方法链,每个方法都将侦听器从顶层容器传递到持有组件的直接容器。最后用容器调用addActionListener()就可以了。

这是在MVC中处理侦听器的正确方法吗?

一般的答案是,不,这不是正确的方法。

每个子视图都将成为自己的MVC,它专注于自己的需求。父MVC可以使用由子MVC提供的事件或其他功能来进行更新或甚至修改其他子MVC的状态。

这里需要记住的重要一点是,视图可以充当其他视图的控制器,但是,您可以选择使用一系列允许视图管理的控制器。

想象一下像“巫师”。它有一系列步骤,从用户收集各种信息,每个步骤需要有效才能进入下一步。

现在,您可能想要直接将导航集成到此,但更好的想法是将导航细节分离为自己的MVC。

当被询问时,向导将向用户呈现步骤,用户将填写信息,可能触发事件。然后,这些事件将允许导航MVC决定用户是否可以移动到下一步骤或前一步骤。

两个MVC将由第三个“主”MVC控制,它将帮助管理状态(从向导中侦听事件并更新导航状态)

让我们试试一个问题,这个问题可以在这里被问到很多,一个小测验!

测验有问题,每个问题都有提示,正确答案,一系列可能的答案,我们也希望存储用户得到的答案。

测验API

所以,下面我们有测验MVC的基本轮廓,我们有一个问题,它由一个模型管理,有一个控制器和一个视图和一系列观察者(听众)

合同(接口)

public interface Question {
    public String getPrompt();
    public String getCorrectAnswer();
    public String getUserAnswer();
    public String[] getOptions();
    public boolean isCorrect();}/**
* This is a deliberate choice to separate the update functionality
* No one but the model should ever actually -apply- the answer to the
* question
*/public interface MutableQuestion extends Question {
    public void setUserAnswer(String userAnswer);}public interface QuizModel {
    public void addQuizObserver(QuizModelObserver observer);
    public void removeQuizObserver(QuizModelObserver observer);
    public Question getNextQuestion();
    public Question getCurrentQuestion();
    public int size();
    public int getScore();
    public void setUserAnswerFor(Question question, String answer);}public interface QuizModelObserver {
    public void didStartQuiz(QuizModel quiz);
    public void didCompleteQuiz(QuizModel quiz);
    public void questionWasAnswered(QuizModel model, Question question);}public interface QuizView extends View {
    public void setQuestion(Question question);
    public boolean hasAnswer();
    public String getUserAnswer();
    public void addQuizObserver(QuizViewObserver observer);
    public void removeQuizObserver(QuizViewObserver observer);}public interface QuizViewObserver {
    public void userDidChangeAnswer(QuizView view);}public interface QuizController {
    public QuizModel getModel(); // This is the model
    public QuizView getView();
    public void askNextQuestion();}

我个人致力于“代码与接口(不是实现)”的原则,我也故意用这个想法来证明这一点。

如果仔细观察,您会注意到视图或模型实际上彼此之间没有任何关系。这全部由控制器控制

我在这里做的一件事就是为控制器提供一个askNextQuestion,因为控制器不知道何时应该发生(你可能会想到使用它userDidChangeAnswer,但这意味着用户只能尝试回答一次)问题,有点意思)

实施

现在,通常情况下,我希望有一些abstract实现来填补“常见”功能,我已经放弃了大部分并直接进入默认实现,这主要是为了演示目的。

public class DefaultQuestion implements MutableQuestion {

    private final String prompt;
    private final String correctAnswer;
    private String userAnswer;
    private final String[] options;

    public DefaultQuestion(String prompt, String correctAnswer, String... options) {
        this.prompt = prompt;
        this.correctAnswer = correctAnswer;
        this.options = options;
    }

    @Override
    public String getPrompt() {
        return prompt;
    }

    @Override
    public String getCorrectAnswer() {
        return correctAnswer;
    }

    @Override
    public String getUserAnswer() {
        return userAnswer;
    }

    @Override
    public String[] getOptions() {
        List<String> list = new ArrayList<>(Arrays.asList(options));
        Collections.shuffle(list);
        return list.toArray(new String[list.size()]);
    }

    public void setUserAnswer(String userAnswer) {
        this.userAnswer = userAnswer;
    }

    @Override
    public boolean isCorrect() {
        return getCorrectAnswer().equals(getUserAnswer());
    }}public abstract class AbstractQuizModel implements QuizModel {

    private List<QuizModelObserver> observers;

    public AbstractQuizModel() {
        observers = new ArrayList<>(25);
    }

    @Override
    public void addQuizObserver(QuizModelObserver observer) {
        observers.add(observer);
    }

    @Override
    public void removeQuizObserver(QuizModelObserver observer) {
        observers.remove(observer);
    }

    protected void fireDidStartQuiz() {
        for (QuizModelObserver observer : observers) {
            observer.didStartQuiz(this);
        }
    }

    protected void fireDidCompleteQuiz() {
        for (QuizModelObserver observer : observers) {
            observer.didCompleteQuiz(this);
        }
    }

    protected void fireQuestionWasAnswered(Question question) {
        for (QuizModelObserver observer : observers) {
            observer.questionWasAnswered(this, question);
        }
    }}public class DefaultQuizModel extends AbstractQuizModel {

    private List<MutableQuestion> questions;
    private Iterator<MutableQuestion> iterator;

    private MutableQuestion currentQuestion;
    private boolean completed;

    private int score;

    public DefaultQuizModel() {
        questions = new ArrayList<>(50);
    }

    public void add(MutableQuestion question) {
        questions.add(question);
    }

    public void remove(MutableQuestion question) {
        questions.remove(question);
    }

    @Override
    public Question getNextQuestion() {
        if (!completed && iterator == null) {
            iterator = questions.iterator();
            fireDidStartQuiz();
        }
        if (iterator.hasNext()) {
            currentQuestion = iterator.next();
        } else {
            completed = true;
            iterator = null;
            currentQuestion = null;
            fireDidCompleteQuiz();
        }
        return currentQuestion;
    }

    @Override
    public Question getCurrentQuestion() {
        return currentQuestion;
    }

    @Override
    public int size() {
        return questions.size();
    }

    @Override
    public int getScore() {
        return score;
    }

    @Override
    public void setUserAnswerFor(Question question, String answer) {
        if (question instanceof MutableQuestion) {
            ((MutableQuestion) question).setUserAnswer(answer);
            if (question.isCorrect()) {
                score++;
            }
            fireQuestionWasAnswered(question);
        }
    }}public class DefaultQuizController implements QuizController {

    private QuizModel model;
    private QuizView view;

    public DefaultQuizController(QuizModel model, QuizView view) {
        this.model = model;
        this.view = view;
    }

    @Override
    public QuizModel getModel() {
        return model;
    }

    @Override
    public QuizView getView() {
        return view;
    }

    @Override
    public void askNextQuestion() {
        Question question = getModel().getCurrentQuestion();
        if (question != null) {
            String answer = getView().getUserAnswer();
            getModel().setUserAnswerFor(question, answer);
        }
        question = getModel().getNextQuestion();
        getView().setQuestion(question);
    }}public class DefaultQuizViewPane extends JPanel implements QuizView {

    private final JLabel question;
    private final JPanel optionsPane;
    private final ButtonGroup bg;

    private final List<JRadioButton> options;
    private String userAnswer;

    private final List<QuizViewObserver> observers;

    private final AnswerActionListener answerActionListener;

    private final GridBagConstraints optionsGbc;

    protected DefaultQuizViewPane() {

        setBorder(new EmptyBorder(4, 4, 4, 4));

        question = new JLabel();
        optionsPane = new JPanel(new GridBagLayout());
        optionsPane.setBorder(new EmptyBorder(4, 4, 4, 4));

        answerActionListener = new AnswerActionListener();

        optionsGbc = new GridBagConstraints();
        optionsGbc.gridwidth = GridBagConstraints.REMAINDER;
        optionsGbc.weightx = 1;
        optionsGbc.anchor = GridBagConstraints.WEST;

        options = new ArrayList<>(25);

        bg = new ButtonGroup();

        observers = new ArrayList<>(25);

        setLayout(new BorderLayout());

        add(question, BorderLayout.NORTH);
        add(optionsPane);

    }

    protected void reset() {
        question.setText(null);
        for (JRadioButton rb : options) {
            rb.removeActionListener(answerActionListener);
            bg.remove(rb);
            optionsPane.remove(rb);
        }
        options.clear();
    }

    @Override
    public void setQuestion(Question question) {
        reset();
        if (question != null) {
            this.question.setText(question.getPrompt());

            for (String option : question.getOptions()) {
                JRadioButton rb = makeRadioButtonFor(option);
                options.add(rb);
                optionsPane.add(rb, optionsGbc);
            }
            optionsPane.revalidate();
            revalidate();
            repaint();
        }
    }

    @Override
    public void addQuizObserver(QuizViewObserver observer) {
        observers.add(observer);
    }

    @Override
    public void removeQuizObserver(QuizViewObserver observer) {
        observers.remove(observer);
    }

    protected void fireUserDidChangeAnswer() {
        for (QuizViewObserver observer : observers) {
            observer.userDidChangeAnswer(this);
        }
    }

    protected JRadioButton makeRadioButtonFor(String option) {
        JRadioButton btn = new JRadioButton(option);
        btn.addActionListener(answerActionListener);
        bg.add(btn);

        return btn;
    }

    @Override
    public boolean hasAnswer() {
        return userAnswer != null;
    }

    @Override
    public String getUserAnswer() {
        return userAnswer;
    }

    @Override
    public JComponent getViewComponent() {
        return this;
    }

    protected class AnswerActionListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            userAnswer = e.getActionCommand();
            fireUserDidChangeAnswer();
        }

    }}

真的没什么特别的。这里唯一值得关注的是控制器如何管理模型和视图之间的事件

导航API

导航API非常基础。它允许您控制用户是否可以实际导航到下一个或上一个元素(如果操作应该对用户可用)以及随时禁用任一操作

(再一次,我专注于一个简单的设计,实际上,有一些控制权来修改模型的状态以改变导航可以在哪些方向上工作会很好,但是我故意将它留在了很简单)

合同(接口)

public enum NavigationDirection {
    NEXT, PREVIOUS;}public interface NavigationModel {
    public boolean canNavigate(NavigationDirection direction);

    public void addObserver(NavigationModelObserver observer);
    public void removeObserver(NavigationModelObserver observer);

    public void next();
    public void previous();}public interface NavigationModelObserver {
    public void next(NavigationModel view);
    public void previous(NavigationModel view);}public interface NavigationController {
    public NavigationView getView();
    public NavigationModel getModel();

    public void setDirectionEnabled(NavigationDirection navigationDirection, boolean b);}public interface NavigationView extends View {

    public void setNavigatable(NavigationDirection direction, boolean navigtable);
    public void setDirectionEnabled(NavigationDirection direction, boolean enabled);

    public void addObserver(NavigationViewObserver observer);
    public void removeObserver(NavigationViewObserver observer);}public interface NavigationViewObserver {
    public void next(NavigationView view);
    public void previous(NavigationView view);}

实施

public static class DefaultNavigationModel implements NavigationModel {

    private Set<NavigationDirection> navigatableDirections;
    private List<NavigationModelObserver> observers;

    public DefaultNavigationModel() {
        this(true, true);
    }

    public DefaultNavigationModel(boolean canNavigateNext, boolean canNavigatePrevious) {
        navigatableDirections = new HashSet<>(2);
        observers = new ArrayList<>(25);
        setCanNavigate(NavigationDirection.NEXT, canNavigateNext);
        setCanNavigate(NavigationDirection.PREVIOUS, canNavigatePrevious);
    }

    public void setCanNavigate(NavigationDirection direction, boolean canNavigate) {
        if (canNavigate) {
            navigatableDirections.add(direction);
        } else {
            navigatableDirections.remove(direction);
        }
    }

    @Override
    public boolean canNavigate(NavigationDirection direction) {
        return navigatableDirections.contains(direction);
    }

    @Override
    public void addObserver(NavigationModelObserver observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(NavigationModelObserver observer) {
        observers.remove(observer);
    }

    protected   void fireMoveNext() {
        for (NavigationModelObserver observer : observers) {
            observer.next(this);
        }
    }

    protected   void fireMovePrevious() {
        for (NavigationModelObserver observer : observers) {
            observer.previous(this);
        }
    }

    @Override
    public void next() {
        fireMoveNext();
    }

    @Override
    public void previous() {
        fireMovePrevious();
    }}public static class DefaultNavigationController implements NavigationController {

    private final NavigationModel model;
    private final NavigationView view;

    public DefaultNavigationController(NavigationModel model, NavigationView view) {
        this.model = model;
        this.view = view;

        view.setNavigatable(NavigationDirection.NEXT, model.canNavigate(NavigationDirection.NEXT));
        view.setNavigatable(NavigationDirection.PREVIOUS, model.canNavigate(NavigationDirection.PREVIOUS));

        view.addObserver(new NavigationViewObserver() {
            @Override
            public void next(NavigationView view) {
                if (getModel().canNavigate(NavigationDirection.NEXT)) {
                    getModel().next();
                }
            }

            @Override
            public void previous(NavigationView view) {
                if (getModel().canNavigate(NavigationDirection.PREVIOUS)) {
                    getModel().previous();
                }
            }
        });
    }

    @Override
    public NavigationView getView() {
        return view;
    }

    @Override
    public NavigationModel getModel() {
        return model;
    }

    @Override
    public void setDirectionEnabled(NavigationDirection navigationDirection, boolean enabled) {
        getView().setDirectionEnabled(navigationDirection, enabled);
    }}public static class DefaultNavigationViewPane extends JPanel implements NavigationView {

    private final List<NavigationViewObserver> observers;

    private final JButton btnNext;
    private final JButton btnPrevious;

    public DefaultNavigationViewPane() {
        btnNext = new JButton("Next >");
        btnNext.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                fireMoveNext();
            }
        });
        btnPrevious = new JButton("< Previous");
        btnPrevious.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                fireMovePrevious();
            }
        });
        setLayout(new FlowLayout(FlowLayout.RIGHT));

        add(btnPrevious);
        add(btnNext);

        observers = new ArrayList<>();
    }

    @Override
    public void addObserver(NavigationViewObserver observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(NavigationViewObserver observer) {
        observers.remove(observer);
    }

    protected void fireMoveNext() {
        for (NavigationViewObserver observer : observers) {
            observer.next(this);
        }
    }

    protected void fireMovePrevious() {
        for (NavigationViewObserver observer : observers) {
            observer.previous(this);
        }
    }

    @Override
    public JComponent getViewComponent() {
        return this;
    }

    @Override
    public void setNavigatable(NavigationDirection direction, boolean navigtable) {
        switch (direction) {
            case NEXT:
                btnNext.setVisible(navigtable);
                break;
            case PREVIOUS:
                btnPrevious.setVisible(navigtable);
                break;
        }
    }

    @Override
    public void setDirectionEnabled(NavigationDirection direction, boolean enabled) {
        switch (direction) {
            case NEXT:
                btnNext.setEnabled(enabled);
                break;
            case PREVIOUS:
                btnPrevious.setEnabled(enabled);
                break;
        }
    }}

测验大师

现在,这些是两个不同的API,它们没有任何共同点,因此,我们需要某种控制器来桥接它们

合同(接口)

public interface QuizMasterController {
    public QuizController getQuizController();
    public NavigationController getNavigationController();
    public QuizMasterView getView();}public interface QuizMasterView extends View {
    public NavigationController getNavigationController();
    public QuizController getQuizController();
    public void showScoreView(int score, int size);
    public void showQuestionAndAnswerView();}

好的,所以你可能会问自己一个显而易见的问题,模型在哪里?好吧,它不需要一个,它只是导航和测验API之间的桥梁,它不管理它自己的任何数据......

实施

public class DefaultQuizMasterController implements QuizMasterController {

    private QuizController quizController;
    private NavigationController navController;

    private QuizMasterView view;

    public DefaultQuizMasterController(QuizController quizController, NavigationController navController) {
        this.quizController = quizController;
        this.navController = navController;

        view = new DefaultQuizMasterViewPane(quizController, navController);

        // Setup the initial state
        quizController.askNextQuestion();

        navController.getModel().addObserver(new NavigationModelObserver() {
            @Override
            public void next(NavigationModel view) {
                getQuizController().askNextQuestion();
                getNavigationController().setDirectionEnabled(NavigationDirection.NEXT, false);
            }

            @Override
            public void previous(NavigationModel view) {
                // NOOP
            }
        });

        quizController.getView().addQuizObserver(new QuizViewObserver() {
            @Override
            public void userDidChangeAnswer(WizeQuiz.QuizView view) {
                getNavigationController().setDirectionEnabled(NavigationDirection.NEXT, true);
            }
        });

        quizController.getModel().addQuizObserver(new QuizModelObserver() {
            @Override
            public void didStartQuiz(QuizModel quiz) {
                getView().showQuestionAndAnswerView();
            }

            @Override
            public void didCompleteQuiz(QuizModel quiz) {
                getView().showScoreView(quiz.getScore(), quiz.size());
                getNavigationController().setDirectionEnabled(NavigationDirection.NEXT, false);
            }

            @Override
            public void questionWasAnswered(QuizModel model, Question question) {
            }
        });

        navController.setDirectionEnabled(NavigationDirection.NEXT, false);
    }

    @Override
    public QuizController getQuizController() {
        return quizController;
    }

    @Override
    public NavigationController getNavigationController() {
        return navController;
    }

    @Override
    public QuizMasterView getView() {
        return view;
    }}public class DefaultQuizMasterViewPane extends JPanel implements QuizMasterView {

    private QuizController quizController;
    private NavigationController navController;

    private QuestionAndAnswerView qaView;
    private ScoreView scoreView;

    private CardLayout cardLayout;

    public DefaultQuizMasterViewPane(QuizController quizController, NavigationController navController) {
        this.quizController = quizController;
        this.navController = navController;

        quizController.getModel().addQuizObserver(new QuizModelObserver() {
            @Override
            public void didStartQuiz(QuizModel quiz) {
            }

            @Override
            public void didCompleteQuiz(QuizModel quiz) {
            }

            @Override
            public void questionWasAnswered(QuizModel model, Question question) {
                qaView.updateScore();
            }
        });

        scoreView = new ScoreView();
        qaView = new QuestionAndAnswerView();

        qaView.updateScore();

        cardLayout = new CardLayout();
        setLayout(cardLayout);

        add(qaView, "view.qa");
        add(scoreView, "view.score");
    }

    @Override
    public JComponent getViewComponent() {
        return this;
    }

    @Override
    public NavigationController getNavigationController() {
        return navController;
    }

    @Override
    public QuizController getQuizController() {
        return quizController;
    }

    @Override
    public void showScoreView(int score, int size) {
        scoreView.updateScore();
        cardLayout.show(this, "view.score");
    }

    @Override
    public void showQuestionAndAnswerView() {
        cardLayout.show(this, "view.qa");
    }

    protected class QuestionAndAnswerView extends JPanel {

        private JLabel score;

        public QuestionAndAnswerView() {

            setLayout(new BorderLayout());

            add(getQuizController().getView().getViewComponent());

            JPanel south = new JPanel(new BorderLayout());
            south.add(getNavigationController().getView().getViewComponent(), BorderLayout.SOUTH);

            score = new JLabel();
            score.setHorizontalAlignment(JLabel.RIGHT);
            south.add(score, BorderLayout.NORTH);

            add(south, BorderLayout.SOUTH);
        }

        protected void updateScore() {
            score.setText(getQuizController().getModel().getScore() + "/" + getQuizController().getModel().size());
        }

    }

    protected class ScoreView extends JPanel {

        private JLabel score;

        public ScoreView() {

            setLayout(new GridBagLayout());
            score = new JLabel("You scored:");
            add(score);

        }

        protected void updateScore() {
            score.setText("You scored: " + getQuizController().getModel().getScore() + "/" + getQuizController().getModel().size());
        }

    }}

现在,实现很有意思,它实际上有两个“状态”或“视图”,“问答”视图和“分数视图”。这也是故意的,因为我真的不想要另一个MVC。Q&A视图已经以任何方式管理两个MVC:P

基本上,它的作用是监视测验API,以便在用户更改问题的答案时,它会告诉导航API它可以移动到下一个问题。它还监视开始和已完成的事件,为这些状态显示所需的视图。

它还监视导航事件的导航API。在此示例中,我们只能向单一方向移动,即使导航API配置为其他方式,测验API也不提供该功能

把它放在一起

现在,我选择单独构建每个部分,可以想象,您可以QuizMasterController构建NavigationAPI本身,因为它知道测验API仅允许向前导航,同样我们可以更改导航API以允许这些状态通过模型修改或模型改变,这些都是可行的解决方案,我刚刚去了一个直接的例子。

NavigationModel navigationModel = new DefaultNavigationModel(true, false);NavigationView navigationView = new DefaultNavigationViewPane();NavigationController navigationController = new NavWiz.DefaultNavigationController(navigationModel, navigationView);DefaultQuizModel quizModel = new DefaultQuizModel();quizModel.add(new DefaultQuestion(
                "Which pop duo was the first western band to play in The Peoples Republic of China?",
                "Wham",
                "Wham", "Simon and Garfunkel", "Chas and Dave", "Right Said Fred"));quizModel.add(new DefaultQuestion(
                "Timber selected from how many fully grown oak trees were needed to build a large 3 decker Royal Navy battle ship in the 18th century?",
                "3,500",
                "50", "500", "1,500", "3,500"));quizModel.add(new DefaultQuestion(
                "Speed skating originated in which country?",
                "Netherlands",
                "Russia", "Netherlands", "Canada", "Norway"));quizModel.add(new DefaultQuestion(
                "Off the coast of which country did the Amoco Cadiz sink?",
                "France",
                "South Africa", "France", "USA", "Spain"));quizModel.add(new DefaultQuestion(
                "The song 'An Englishman in New York' was about which man?",
                "Quentin Crisp",
                "Quentin Crisp", "Sting", "John Lennon", "Gordon Sumner"));QuizView quizView = new DefaultQuizViewPane();QuizController quizController = new DefaultQuizController(quizModel, quizView);QuizMasterController quizMasterController = new DefaultQuizMasterController(quizController, navigationController);JFrame frame = new JFrame("Testing");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.add(quizMasterController.getView().getViewComponent());frame.pack();frame.setLocationRelativeTo(null);frame.setVisible(true);

最后我们最终会得到像...

如果不是粗糙的话,这一点都不算什么,但旨在提供一些关于如何实现复杂的复合MVC的想法


查看完整回答
反对 回复 2019-08-23
?
忽然笑

“这是在MVC中处理监听器的正确方法吗?” - 不是我的想法。正如我之前所说,每个“子视图”可能是它自己的MVC,父控制器控制它们。请记住,Swing拥有自己的MVC实现(或VC-M),你需要包装你的MVC,不要试图让Swing成为一个纯粹的MVC,你只会让自己失望。在我看来,一个View应该代表一个独立的工作单元,通过使用观察者和getter提供输出并通过setter输入,控制器无需知道视图是如何实际实现的

查看完整回答
反对 回复 2019-08-23
?
慕前端131612

作为个人抓地力,我将避免从控制器提供对视图组件的任何访问,因为这表明实现必须遵守实现可能不需要/不需要的特定合同。相反,定义一个视图是gurentees的动作/属性/回调,并让实现弄清楚如何实现这一点 

查看完整回答
反对 回复 2019-08-23

添加回答

回复

举报

0/150
提交
取消
意见反馈 邀请有奖 帮助中心 APP下载
官方微信