我试图使用注释创建一个好的MVC模型,我将在大型GUI项目中使用它。我想尊重最多的规则和指南,并且能够去除MVC的每个部分。代码必须美观,可测试和可维护。我写了一个示例项目来向您展示我的模型,您如何看待它?不要温柔哈哈
发射器:
package example;
import javax.swing.SwingUtilities;
/**
* Test class.
*
* @author AwaX
* @date Feb 10, 2015
*/
public class Example_MVC {
public static void main (String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run () {
final Model model = new Model();
final Controller controller = new Controller(model);
controller.showGui();
}
});
}
}
数据模型:
package example;
import java.util.Observable;
/**
* Data model.
*
* @author AwaX
* @date Feb 10, 2015
*/
public class Model extends Observable {
private int counter;
/**
* Instanciate a default data model.
*/
public Model () {
super();
this.counter = 0;
}
/**
* Notify data model observers with new update.
*
* @param arg
* Allow to specify a parameter for the observer.
*/
public final void notifyChanged (Object... arg) {
setChanged();
if (arg.length == 0) {
notifyObservers();
} else if (arg.length == 1 && arg[0] != null) {
notifyObservers(arg[0]);
} else {
throw new IllegalArgumentException("Only one argument allowed");
}
clearChanged();
}
public void inc () {
this.counter++;
notifyChanged(this.counter);
}
public int getCounter () {
return counter;
}
public void setCounter (int c) {
this.counter = c;
notifyChanged(this.counter);
}
}
控制器:
package example;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import example.View.ActionCallback;
/**
* UI controller.
*
* @author AwaX
* @date Feb 10, 2015
*/
public class Controller {
private final Model model;
private final View view;
private final ScheduledExecutorService scheduler;
/**
* Instanciate the controller.
*
* @param model
* Data model.
*/
public Controller (final Model model) {
this.model = model;
this.view = new View(model);
this.view.addListener(this);
this.model.addObserver(this.view);
this.scheduler = Executors.newScheduledThreadPool(1);
this.scheduler.scheduleAtFixedRate(new IncrementTask(), 0, 1000, TimeUnit.MILLISECONDS);
}
/**
* Show the GUI.
*/
public void showGui () {
this.view.showGui();
}
@ActionCallback (event = "action1")
public void action1 () {
System.out.println("# Action 1");
}
@ActionCallback (event = "action2")
public void action2 () {
System.out.println("# Action 2");
}
/**
* Increment the data model periodically.
*
* @author AwaX
* @date Feb 10, 2015
*/
private class IncrementTask implements Runnable {
@Override
public void run () {
model.inc();
}
}
}
观点:
package example;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;
import javax.swing.AbstractAction;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;
/**
* User interface.
*
* @author AwaX
* @date Feb 10, 2015
*/
public class View extends JFrame implements Observer {
private static final long serialVersionUID = 4240474354742402647L;
@Retention (RetentionPolicy.RUNTIME)
@Target (ElementType.METHOD)
public @interface ActionCallback {
String event();
}
private final ArrayList<Object> listeners;
private JButton btn1;
private JButton btn2;
private JTextField tf;
/**
* Instanciate the user interface.
*
* @param model
* Data model.
*/
public View (final Model model) {
super("MVC Example");
this.listeners = new ArrayList<>();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
createGui();
}
/**
* Show the GUI.
*/
public void showGui () {
setMinimumSize(new Dimension(300, 200));
pack();
setLocationRelativeTo(null);
setVisible(true);
}
/**
* Allow an object to subscribe for UI events.
*
* @param o
* The subscriber object.
*/
public void addListener (Object o) {
if (!this.listeners.contains(o)) {
this.listeners.add(o);
}
}
/**
* Create the GUI and add listeners.
*/
private void createGui () {
this.btn1 = new JButton("Button 1");
this.btn2 = new JButton("Button 2");
this.tf = new JTextField();
setLayout(new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS));
add(this.btn1);
add(this.btn2);
add(this.tf);
this.btn1.setAction(new ButtonAction("action1"));
this.btn2.setAction(new ButtonAction("action2"));
}
@Override
public void update (Observable o, Object arg) {
if (o instanceof Model) {
Model model = (Model) o;
this.tf.setText("" + model.getCounter());
}
}
/**
* Listener on button actions.
*
* @author AwaX
* @date Feb 10, 2015
*/
private class ButtonAction extends AbstractAction {
private static final long serialVersionUID = 9077483825287720181L;
public ButtonAction (final String name) {
super(name);
}
@Override
public void actionPerformed (ActionEvent e) {
for (Object o : listeners) {
for (Method m : o.getClass().getMethods()) {
for (Annotation a : m.getAnnotations()) {
if (a instanceof ActionCallback) {
ActionCallback callback = (ActionCallback) a;
if (callback.event().equals(e.getActionCommand())) {
try {
m.invoke(o);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) {
e1.printStackTrace();
}
}
}
}
}
}
}
}
}