Java 单件包装本机全局状态的替代方案
在我的应用程序中,我在调用本机C代码的JNI库之上编写了一个抽象层。确切地说,本机库FFmpeg公开了一些全局函数来更改日志记录行为 特别是,我正在使用一个回调函数将日志消息转发给自定义处理程序。问题是这样的回调只能有一个。创建此回调的新实例意味着本机库将调用它,而不管它是否已注册为新回调。如果需要另一个回调类,那么必须创建一个不同的回调类,就像在C中声明一个新函数一样 在我看来,似乎有两种选择: 1允许此单例类的多个实例。我已经在使用回调类的私有静态实例。它不能在别处实例化,至少不能在我的回调子类中实例化 2限制用户多次实例化此单例类的其他方法 我对第一种方法存在的问题是: 1该API具有误导性。它只是一个在全局状态下运行的代理对象。然而对于用户来说,似乎每个实例都在其自己的状态下运行 2.同步。我不能使用多个锁。使用静态私有锁让我们回到问题1。当然,至少一个锁是不能变异的 我应该放弃这一切,还是用单身汉?不管你如何削减它,这个类都会改变全局状态。只是这种状态超出了我的控制范围Java 单件包装本机全局状态的替代方案,java,design-patterns,singleton,native,Java,Design Patterns,Singleton,Native,在我的应用程序中,我在调用本机C代码的JNI库之上编写了一个抽象层。确切地说,本机库FFmpeg公开了一些全局函数来更改日志记录行为 特别是,我正在使用一个回调函数将日志消息转发给自定义处理程序。问题是这样的回调只能有一个。创建此回调的新实例意味着本机库将调用它,而不管它是否已注册为新回调。如果需要另一个回调类,那么必须创建一个不同的回调类,就像在C中声明一个新函数一样 在我看来,似乎有两种选择: 1允许此单例类的多个实例。我已经在使用回调类的私有静态实例。它不能在别处实例化,至少不能在我的回调
在这种情况下该怎么办?我有什么可行的替代方案吗?当状态在本机中是全局的时,我会说最好在API中对其进行建模,而不要给人留下它不是全局的印象 2限制用户多次实例化此单例类的其他方法 听起来你没有最好的单例实现,请看下面我的 您可以维护回调的列表,并在本机事件触发时调用它们中的每一个:
public interface CallBack {
void onWhatever();
}
public enum WhateverEvent {
INSTANCE;
private final ArrayList<CallBack> callBacks = new ArrayList<>();
public void register(CallBack callBack){
callBacks.add(callBack);
}
private void theSingleRealCallBackTheNativeCalls(){
for(CallBack callBack : callBacks)
callBack.onWhatever();
}
}
如果有一些信息可用于发送:
public enum WhateverEvent {
INSTANCE;
private final HashMap<Integer, CallBack> callBacks = new HashMap<>();
public void register(int id, CallBack callBack){
callBacks.put(id, callBack);
}
private void theSingleRealCallBackTheNativeCalls(int id){
CallBack callBack : callBacks.get(id);
if (callback != null)
callBack.onWhatever();
}
}
然后您可以将其隐藏在API中。公开地说,它似乎不再是全局的。因此,您的问题是本机允许调用1个函数,而您确实需要很多函数。一般来说,只需处理本地第三方库中的全局状态抽象。常识告诉我只使用一个单例,但我的完美主义告诉我要找到一个更面向对象的替代方案。回调中是否有你可能用来路由呼叫的信息?例如,视频id int。然后您可以秘密地将调用路由回非单例处理程序。处理程序寄存器是单例的一部分。处理程序本身是实现LogHandler接口的类,正如您的回答所示。实际上,我的私有静态回调已经维护了一个处理程序列表。回调不是当前的问题。这是抽象本地全局状态问题的一部分。我只是想看看其他开发人员在这种情况下会做什么或已经做了什么。但你的回答似乎建议我应该使用单例,而不是试图找到一种方法来避免它?当国家在本地是全球性的,我会说最好是这样建模。不要给人一种不是全局的印象。这个特定的回调不提供id。这不是问题,因为日志消息被转发给每个注册的处理程序。我还可以将它转发给最后一个注册的处理程序。但是,我确实有其他回调,它们会向我传递指向不透明对象的指针。正如您所建议的,我使用了一个地址为id的伪指针,并将其映射到相应的处理程序,它成功了!没有任何东西使用这些指针,因此不必担心非法内存访问。至于设计问题,我想我不得不接受单例解决方案
public enum WhateverEvent {
INSTANCE;
private final HashMap<Integer, CallBack> callBacks = new HashMap<>();
public void register(int id, CallBack callBack){
callBacks.put(id, callBack);
}
private void theSingleRealCallBackTheNativeCalls(int id){
CallBack callBack : callBacks.get(id);
if (callback != null)
callBack.onWhatever();
}
}