我定义了一个class InfoAction implements java.swing.Action
,它需要从调用上下文中检索一些信息才能执行自己。为此,我定义了一个提供这些信息的接口:
public interface InfoProvider {
Info getInfo();
}
InfoAction类中的actionPerformed
看起来像
public void actionPerformed(ActionEvent e) {
Info info=e.getSource().getInfo();
// ... treat Info ...
}
然后,我想使用这些操作创建各种Swing对象,每个Swing对象都为该操作提供自己的上下文。
InfoAction infoAction = MyApp.getInstance().getAction(MyApp.RENAME_ACTION); // Retrieving one action
JButton button = new JButton(infoAction); // TODO: inject the InfoProvider interface
JMenuItem menuItem = new JMenuItem(infoAction); // TODO: inject the InfoProvider interface
我一直纠结于如何将InfoProvider接口添加到那些摇摆对象中。
天真的我试着写
InfoAction infoAction = MyApp.getInstance().getAction(MyApp.RENAME_ACTION);
JButton button = new JButton(infoAction) implements InfoProvider {
Info getInfo() {
return MyGui.this.getInfo();
}
};
JMenuItem menuItem = new JMenuItem(infoAction) implements InfoProvider {
Info getInfo() {
return MyGui.this.getInfo();
}
};
但这不是一个有效的代码。
我希望避免创建所有Swing部件的子类,例如private class InfoButton extends JButton implements InfoProvider {...}
、private class InfoMenuItem extends JMenuItem implements InfoProvider {...}
。
所以我想知道在保持干净代码的同时,还有什么其他选择可以做到这一点
使用反射是否是一个不错的选择?去掉接口并测试actionEvent.getSource((是否有getInfo((方法?
附言:我正在使用Java8
您不需要专门化每个支持操作的组件(或计时器或其他操作事件源(。相反,创建一个专门的Action
实现来在组件/事件源和实际操作之间进行中介。例如
public class InfoProviderAction implements Action, InfoProvider {
final PropertyChangeSupport listeners = new PropertyChangeSupport(this);
final PropertyChangeListener relay = ev -> listeners.firePropertyChange(
ev.getPropertyName(), ev.getOldValue(), ev.getNewValue());
final Action target;
final InfoProvider actualProvider;
public InfoProviderAction(Action target, InfoProvider actualProvider) {
this.target = target;
this.actualProvider = actualProvider;
}
@Override
public void actionPerformed(ActionEvent e) {
target.actionPerformed(new ActionEvent(
this, e.getID(), e.getActionCommand(), e.getWhen(), e.getModifiers()));
}
@Override
public Info getInfo() {
return actualProvider.getInfo();
}
@Override
public void addPropertyChangeListener(PropertyChangeListener listener) {
boolean hadListeners = listeners.hasListeners(null);
listeners.addPropertyChangeListener(listener);
if(!hadListeners) target.addPropertyChangeListener(relay);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener listener) {
boolean hadListeners = listeners.hasListeners(null);
listeners.removePropertyChangeListener(listener);
if(hadListeners && !listeners.hasListeners(null))
target.removePropertyChangeListener(relay);
}
@Override
public boolean isEnabled() {
return target.isEnabled();
}
@Override
public void setEnabled(boolean b) {
target.setEnabled(b);
}
@Override
public Object getValue(String key) {
return target.getValue(key);
}
@Override
public void putValue(String key, Object value) {
target.putValue(key, value);
}
}
然后,您的MyGui
类可以像一样使用它
InfoAction infoAction = MyApp.getInstance().getAction(MyApp.RENAME_ACTION);
infoAction = new InfoProviderAction(infoAction, this::getInfo);// inject the InfoProvider
JButton button = new JButton(infoAction);
JMenuItem menuItem = new JMenuItem(infoAction);
组件将InfoProviderAction
视为其动作,它将显示与包装动作相同的属性和行为。而实际操作将把InfoProviderAction
视为偶数源,按预期实现InfoProvider
接口。
如果操作期望源是一个组件,则可能会出现问题。但操作不应假定这一点,因为这会导致Timer
或全局密钥绑定作为事件源出现问题。
有一些东西可以与Swing一起使用:
- JComponent.putClientProperty
- JComponent.getClientProperty
当你构建你的对象时,你可以配置它:
class ComponentFactory {
public static <T extends JComponent> T configure(T component) {
// the MyGui.this won't compile here, but it is only to explain
// what you can do with your current code.
component.putClientProperty(InfoProvider.class, MyGui.this);
}
}
并将对象构建为:
ComponentFactory.configure(new JButton(infoAction));
或者在ComponentFactory
:中为其创建一个静态方法
ComponentFactory.createButton(infoAction);
在您的操作中,您可以读取属性:
public void actionPerformed(ActionEvent e) {
Object src = e.getSource();
if (src instanceof JComponent) {
JComponent c = (JComponent) src;
Info info = (Info) c.getClientProperty(InfoProvider.class);
...
}
}
注意:
既然是Swing,它就有可能成为Serializable
——我认为这在2022年不是问题。
putClientProperty
的医生说:
clientProperty字典不支持JComponent的大规模扩展,在设计新组件时也不应将其视为子类化的替代方案。
对于一个属性应该是可以的,否则您将不得不使用子类化并为每个这样的组件实现InfoProvider。