在Swing对象中注入接口实现



我定义了一个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。

相关内容

  • 没有找到相关文章

最新更新