单例包装本地全局状态的替代方案

Alternative to singleton wrapping native global state

在我的应用程序中,我在调用本机 C 代码的 JNI 库之上编写了一个抽象层。这个本机库(准确地说是 FFmpeg)公开了一些全局函数来更改日志记录行为。

特别是,我正在使用将日志消息转发到自定义处理程序的回调。问题是只能有一个这样的回调。创建此回调的新实例意味着本机库将改为调用它,无论它是否已注册为新回调。如果你想要另一个,那么你必须创建一个不同的回调 class 就像你在 C 中声明一个新函数一样。

对我来说似乎有两种选择:

(1) 允许此单例的多个实例 class。我已经在使用回调的私有静态实例 class。它不能在其他地方实例化,至少,我的 subclass 这个回调。

(2) 限制用户多次实例化此单例的其他方法class。

第一种方法存在的问题是:

(1) API 具有误导性。它只是一个在全局状态上运行的代理对象。然而对于用户来说,似乎每个实例都在自己的状态下运行。

(2) 同步。我不能使用多个锁。使用静态私有锁让我们回到问题#1。当然至少不能改变锁。

我应该放弃整个事情并使用单例吗?不管你怎么剪,class 都会改变全局状态。只是这种状态不是我能控制的。

遇到这种情况怎么办?我有任何可行的选择吗?

当状态在原生中是全局的时,我会说最好在 API 中对其进行建模,而不是给人一种它不是全局的印象。

(2) Some other way to restrict users from instantiating this singleton class more than once.

听起来您没有最好的单例实现,请参阅下面我的。

您可以维护一个回调列表并在本机事件触发时调用每个回调:

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();
    }
}

用法:

WhateverEvent.INSTANCE.register(new CallBack(){
   public void 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 中。在公开场合,它似乎又不再是全球性的了。