Java 如何解决对全局设置/事件注册表的类型安全异议?

Java 如何解决对全局设置/事件注册表的类型安全异议?,java,events,design-patterns,application-settings,Java,Events,Design Patterns,Application Settings,我正在使用多个项目和插件开发客户机的Java应用程序。它们的设计非常脆弱,因为在这些分区中共享任何信息都涉及到一些上帝类实例化和注册实现特定接口的类。接口在未经检查的情况下大量增加,并且通常包含客户机不需要的契约方法 为了将一切解耦,我提出了几个处理“设置”和“事件”的clearinghouse对象。任何类都可以请求设置,指定枚举中的设置类型和期望值的类型。任何类都可以注册一个设置源,该设置源实现一个定义如何获取该设置的接口(例如,通过检查某个类中的某个变量,网络是否可用),但更多的时候,我可以

我正在使用多个项目和插件开发客户机的Java应用程序。它们的设计非常脆弱,因为在这些分区中共享任何信息都涉及到一些上帝类实例化和注册实现特定接口的类。接口在未经检查的情况下大量增加,并且通常包含客户机不需要的契约方法

为了将一切解耦,我提出了几个处理“设置”和“事件”的clearinghouse对象。任何类都可以请求设置,指定枚举中的设置类型和期望值的类型。任何类都可以注册一个设置源,该设置源实现一个定义如何获取该设置的接口(例如,通过检查某个类中的某个变量,网络是否可用),但更多的时候,我可以将一些变量从原始类中完全分离出来,它作为一个匿名设置源永久浮动,只接受并发布一些设置值

//code from caller requesting the setting value
long netID = Setting.GetLongSetting(Setting.Type.NetworkID);

//one of those self-defined settings
Setting.RegisterLongSource(Setting.Type.NetworkID, new LongSource() {
  private long myID=-1;

  @Override
  public long getValue() {
    return myID;
  }

  @Override
  public void setValue(long l) {
    myID = l;
  }

  @Override
  public Setting.Type getType() {
    return Setting.Type.NetworkID;
  }
});
使用大量从C#借用的EventArg对象以类似的方式注册事件

这些设计有一些问题,我的客户要求我考虑更多的编译时安全性。例如,事件中有三种可能出现错误。事件侦听器不可能获得它没有注册的事件类型(错误1),但我至少应该检查一下。无法保证EventArg类型是正确的类型,或者ObjectEventArg中传递的对象将符合预期(错误2)。无法保证传递的引用不为null(错误1)。所有这些都很大程度上取决于在编译时无法保证的代码的良好行为。就这一点而言,有人可以尝试为接受字符串而不是长字符串的时间创建设置,谁说哪个是对的

我能做些什么来改进这个设计,避免一些未知因素,并提供编译时保证

-------------编辑--------------- 根据@Jason Albano的回答,我可以更像这样重写我的解决方案

//Settings now return a defined type; It could still be null or the wrong type was registered
//if I go all the way with his solution I won't need to cast, but I will have one register/get method for each type.
NetworkIDSetting netID = (NetworkIDSetting) Setting.GetSetting(Setting.Type.NetworkID);

//I can still create a self-defined setting
class NetworkIDSetting() {
  private long myID=-1;

  public long getValue() {
    return myID;
  }

  public void setValue(long l) {
    myID = l;
  }

  @Override
  public Setting.Type getType() {
    return Setting.Type.NetworkID;
  }
}

//i'm less certain how to create a setting that is bound more tightly to another class.
abstract class ScreenWidthSetting {
  public abstract int getWidth();

  //set width is not allowed in this case

  @Override
  public Setting.Type getType() {
    return Setting.Type.ScreenWidth;
  }
}

//then instantiate in the required class
Setting.RegisterLongSource(new ScreenWidthSetting() {
  @Override
  public int getWidth() {
    this.getScreen().getWidth();
  }
});

//definition of an event class
class MessageReceivedEvent {
  public Message myMessage;
  public MessageReceivedEvent(Message m) {
    myMessage = m;
  }

  @Override
  public Event.Type getEventType() {
    return Event.Type.MessageReceived;
  }
}

//code from an event generator
Events.Event(new MessageReceivedEvent(messageInstance));

//I can still create an anonymous event listener with many more guarantees
//again, if I go the whole way I will have a multiplicity of very similar methods
Events.RegisterListener(Event.Type.MessageReceived, new Event.MessageListener() {
  @Override
  public void HandleEvent(MessageEvent e) {
     Message m = e.myMessage;
     if(m!=null)
     {
       this.handleMessage(m);
     }
     else
     {
       //error 3 is still unavoidable
     }
  }
} 

维护类型安全正是设计的目的

访问者模式将对象上执行的操作与对象本身分开。在本例中,操作是处理程序,对象是事件。此模式的工作原理是,方法调用的行为因调用方法的对象和提供给方法的参数而异

对于处理程序和事件,我们可以创建以下内容:

public interface Handler {
    void handle(EventA a);
    void handle(EventB b);
}

public interface Event {
    void accept(Handler handler);
}

public class EventA implements Event {

    @Override
    public void accept(Handler handler) {
        handler.handle(this);
    }
}

public class EventB implements Event {

    @Override
    public void accept(Handler handler) {
        handler.handle(this);
    }
}

在每个
事件
具体类中使用相同的
处理程序.handle(this)
主体继续实现
accept
方法可能看起来很乏味,但这是模式的关键。
引用维护发出调用的类的类型。因此,对于
EventA
类,当调用
accept
时,调用
handler.handle(this)
将导致调用
handler\handle(EventA a)
。同样,调用<代码>接受> <代码>在一个对象>代码>事件b>代码>中,将导致<代码>处理程序>句柄(事件B)被调用。

好的,这是要考虑的问题。我没有从访问者的角度考虑这一点,但我在问题中描述的解决方案确实使用了双重分派。匿名Event.Listener或Setting.Source具有处理leEvent、SetValue和GetValue的方法。当事件或设置请求发生时,它们由事件或设置类调度。我的类型安全问题可以通过您的方法解决,其中每个事件都是不同的类型。在您的解决方案中有和EventA和EventB。这些不同类型中的每一个可能都有事件参数的定义以及事件类型的隐式定义。如果我定义了许多事件类和事件监听器类型,我可以用更安全的类型注册它们。这真的解决了我的问题吗?我可以保证给定的事件需要特定类型的EventArg。C#这样做。我的解决方案没有。也许我应该。我仍然不能保证分派的事件没有空引用。也许没人能。我可以将时间定义为具有一个或两个数据版本的事件。我很高兴这能起作用,但是每种新的事件类型都有大量额外的代码。我对其他解决办法持开放态度。
public interface Handler {
    void handle(EventA a);
    void handle(EventB b);
}

public interface Event {
    void accept(Handler handler);
}

public class EventA implements Event {

    @Override
    public void accept(Handler handler) {
        handler.handle(this);
    }
}

public class EventB implements Event {

    @Override
    public void accept(Handler handler) {
        handler.handle(this);
    }
}