Wzorzec MVC w javie

0

Witam

Miesiąc temu zacząłem naukę javy, chcę zaprojektować prosty edytor map do gry 2d i mam pewne pytania. Programować obiektowo potrafię mvc wyniosłem z php ale nie wiem jak ma to wyglądać w javie. Chcę napisać możliwie najlepszej jakości kod (mając na uwadze moje niewielkie doświadczenie w tym języku).

Czy jeśli tworzę okno edytora, to ma ono być obiektem np. FrameEditor i przechowywać w sobie, FrameEditorController, FrameEditorView i FrameEditorModel?

W oknie głównym edytora mają znajdować się inne okienka/panele/przyciski/menu i dla każdego komponentu tworzyć taką strukturę klas? Jeśli tak to w jaki sposób połączyć klacy FrameEditor z klasą np. MainMenu?

W internecie znalazłem kilka przykładów ale są one proste ilustrują one programy które zawierają 1 widok 1 model i 1 kontroler i nie wiem jak ma to wyglądać.

1

Spring IoC

0

Dzięki, frameworka nie znam zaraz będę czytał. Jeśli ktoś ma jakieś inne sugestie także będę wdzięczny.

0

Tak czytam o IoC i raczej nie o to mi chodziło. Robię klasę menu EditorMenubarController dziedziczącą po JMenuBar ma ona w sobie EditorMenuBarModel i EditorMenuBarView. Teraz do menu chcę dodać podmenu i tu pytanie jak to zrobić tj. w którym miejscu aplikacji tworzyć to podmenu czy to ma być w kontrolerze EditorMenubarController?

0

Moim zdaniem powinieneś zainteresować się wzorcem Model-View-Presenter.

Na Twoim przykładzie Map byłaby klasą modelu. Umożliwiałaby zarejestrowania słuchacza zmian (np. wzorzec Observer). Częścią interfejsu takiego słuchacza mogłaby być np. metoda:

void registerChangeListener(IMapChangeListener listener);

Prezenter zarejestrowałby się jako słuchacza zmiany w mapie. Miałby też dostęp do interfejsu widoku, np.

interface IView
{
  void displayMap(Map map);
...
}

Za tworzenie kontrolek, menu itp odpowiadałaby konkretna implementacja danego IView. Prezenter oraz Model nie muszą nic wiedzieć na temat konkretnej implementacji Widoku! Nie wiedzą nawet, czy jest to GUI czy konsola, czy ma menu i przyciski itp.!

Dla danego Widoku tworzysz sobie jeden Prezenter. Wszystkie zdarzenia inicjowane przez użytkownika idą przez Prezentera, który wykonuje operacje na Modelu.

0

@cannabis IoC jest przydatne w każdym większym programie ;)

0

Czytałem o nim i na pewno się przyda, brakowało mi tego w php, nie wiedziałem jak to się nazywa :)

Dzięki mychal o to właśnie mi chodziło, spróbuję jakiś prosty kod napisać i wrzucę dla pewności czy to dobrze rozumiem.

0

Piszę sobie kod na próbę i napotkałem pewien problem którego nie rozumiem a nie wiem jak google o to zapytać.

Mam takie klasy:

public abstract class BModel extends Observable {

}

public class MainFrameModel extends BModel {

    private int counter;

    public void setValue(int value) {
        this.counter = value;
        setChanged();
        notifyObservers(counter);
    }

    public void increaseValue() {
        ++counter;
        setChanged();
        notifyObservers(counter);
    }

}

public abstract class BController implements ActionListener {

    protected BModel model;
    protected BView view;

    public void setModel(BModel model) {
        this.model = model;
    }

    public BModel getModel() {
        return this.model;
    }

    public void setView(BView view) {
        this.view = view;
    }

    public BView getView() {
        return this.view;
    }
}

public class MainFrameController extends BController {

        //błąd
    public MainFrameController() {
        this.model.increaseValue();
    }

    @Override
    public void actionPerformed(ActionEvent e) {

    }

    public void addModel(MainFrameModel m){
        this.model = m;
    }

    public void addView(BView v){
        this.view = v;
    }

        //błąd
    public void initModel(int x){
        model.setValue(x);
    }

}

i w klasie MainFrameController w liniach z komentarzem błąd jest błąd. Nie wiem jak to rozwiązać bo obiekt BModel (model bazowy dla wszystkich modeli), nie posiada tych metod a dziedziczący MainFrameController już je ma. Do kontrolera MainFrameController ładuję model MainFrameModel nie BModel.

3

Ehh, mam wrażenie, że próbujesz to zrobić jakoś tak na siłę. Po co Ci ten abstakcyjny model? Przygotowałem Ci gotowy przykład z opisem. Jest to okienkowa aplikacja ze stoperem :P. W załączniku projekt IntelliJ.

Klasa Main - nic szczególnego, ale żeby było kompletnie:

public class Main {
    public static void main(String[] args) {
        MainFrame mainFrame = new MainFrame();
        mainFrame.setVisible(true);
    }
}

Interfejsy - ICounterChangeListener oraz ICounterView.

public interface ICounterChangeListener {
    void onCounterChanged();
}
public interface ICounterView {
    void displayCounterValue(int counterValue);
}

Zaczyna się robić ciekawie - klasa modelu - Counter. Daje możliwosć zwiększania wartości co 1 sekundę. O zmianie wartości informuje swoich słuchaczy.

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class Counter {
    private int value;
    private List<ICounterChangeListener> changeListeners = new ArrayList<>();
    private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    private ScheduledFuture currentTimer;

    public Counter() {
    }

    private void notifyChangeListeners() {
        for (ICounterChangeListener listener : changeListeners) {
            listener.onCounterChanged();
        }
    }

    private void increaseValue() {
        value++;
        notifyChangeListeners();
    }

    public int getValue() {
        return value;
    }

    public void start() {
        value = 0;
        if (currentTimer == null) {
            currentTimer = executor.scheduleAtFixedRate(() -> increaseValue(), 0, 1, TimeUnit.SECONDS);
        }
    }

    public void addChangeListener(ICounterChangeListener listener) {
        changeListeners.add(listener);
    }

    public void removeChangeListener(ICounterChangeListener listener) {
        changeListeners.remove(listener);
    }

    public void stop() {
        if (currentTimer != null) {
            currentTimer.cancel(true);
            currentTimer = null;
            value = 0;
            notifyChangeListeners();
        }
    }
}

Klasa Prezentera, reaguje na zdarzenia GUI oraz na zmiany Modelu. Komunikuje zmiany Modelu Widokowi (poprzez ICounterView). Prezenter (podobnie jak Kontroler) powinien być możliwie chudziutki i delegować zadania.

public class MainFramePresenter implements ICounterChangeListener {
    private final ICounterView counterView;
    private Counter counter = new Counter();

    public MainFramePresenter(ICounterView counterView) {
        this.counterView = counterView;
        this.counter.addChangeListener(this);
    }

    @Override
    public void onCounterChanged() {
        this.counterView.displayCounterValue(counter.getValue());
    }

    public void startCounter() {
        this.counter.start();
    }

    public void stopCounter() {
        this.counter.stop();
    }
}

Na koniec implementacja widoku (zrobiona raczej od niechcenia, byle było :P)

import javax.swing.*;
public class MainFrame extends JFrame implements ICounterView {
    private JLabel lCounterValue;
    private MainFramePresenter presenter;

    public MainFrame() {
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        initializeGUI();
        presenter = new MainFramePresenter(this);
    }

    private void initializeGUI() {
        setTitle("Counter");
        setResizable(false);
        setSize(250, 100);
        lCounterValue = new JLabel();

        JPanel labelPanel = new JPanel();
        labelPanel.setSize(100, 15);
        labelPanel.setLocation(10, 10);
        labelPanel.add(lCounterValue);

        JButton btnStartCounter = new JButton("Start");
        btnStartCounter.setSize(100, 15);
        btnStartCounter.setLocation(10, 40);
        btnStartCounter.addActionListener((evt) -> presenter.startCounter());

        JButton btnStopCounter = new JButton("Stop");
        btnStopCounter.setSize(100, 15);
        btnStopCounter.setLocation(120, 40);
        btnStopCounter.addActionListener((evt) -> presenter.stopCounter());

        getContentPane().add(btnStopCounter);
        getContentPane().add(btnStartCounter);
        getContentPane().add(labelPanel);
    }

    @Override
    public void displayCounterValue(final int counterValue) {
        SwingUtilities.invokeLater(() -> lCounterValue.setText(String.valueOf(counterValue)));
    }
}
0

Ojej dzięki, że ci się chciało to zrobić :)

Tej konstrukcji nie rozumiem i nie wiem jak to poprawić bo eclipse mi błędy wyświetla (Syntax Error) fragment klasy Counter:

currentTimer = executor.scheduleAtFixedRate(() -> increaseValue(), 0, 1, TimeUnit.SECONDS);

oraz w MainFrame:

btnStartCounter.addActionListener((evt) -> presenter.startCounter());
...
btnStopCounter.addActionListener((evt) -> presenter.stopCounter());
...
SwingUtilities.invokeLater(() -> lCounterValue.setText(String.valueOf(counterValue)));

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0