单例对象的实例成员或类的静态成员

时间:2013-12-09 10:14:10

标签: c# java oop design-patterns singleton

我正在编写一个监听器类,它会随机创建一个HttpListener 未使用的端口并监听它。我试着把它变成单身。不过我 在听时很难保持单个对象null 随机HTTP端口失败10次。

(我已将端口号和HttpListener实例显示为singleton的成员 实例。然而,还有许多其他成员我没有展示过 简单。)这是我的代码:

class Listener
{
    private static Listener listener = null;  //singleton instance

    //member variables
    private HttpListener httpListener = null;
    private int port = -1;

    static Listener()
    {
        listener = new Listener();
    } 

    private Listener()
    {
        try
        {
            port = //randomly generate
            httpListener = new HttpListener();
            //start listening
        }   
        catch(Exception ex)
        {
            //cant listen on randomly chosen port
            listener = null;
        httpListener = null;
            port = -1;
            return;            
        }   
    }
}

但是在catch()内,listener = nulllistener设置为null 只是一会儿。当默认构造函数返回时,它返回一个新的 Listener的实例,因此静态构造函数中的listener总是有 分配给它的实例。因此,默认情况下不是listener = null 构造函数应该是this = null,这是无效的。

因此我将整个代码移到静态构造函数中。但这迫使我

  • 制作实例成员(porthttpListenerstatic
  • 或使用listener.portlistener.httpListener无处不在

    类侦听器 {     private static Listener listener = null; //单例实例

    //member variables
    private HttpListener httpListener = null;
    private int port = -1;
    
    static Listener()
    {
        listener = new Listener();
    
    try
        {
        listener.port = //randomly generate
            listener.httpListener = new HttpListener();
            //start listening
        }   
        catch(Exception ex)
        {
            //cant listen on randomly chosen port
        listener = null;       
            return;            
        } 
    } 
    
    private Listener()
    {  
    }
    

    }

我不明白

Q1 是否要port& httpListener static? (我觉得有点反对OOP原则)或

Q2 是否将它们作为实例成员并在任何地方使用listener.? (这是有问题的,因为在我的实际代码中有很多这样的成员和方法,我必须在任何地方附加listener.

或者我认为这一切都错了,应该遵循不同的方法?

2 个答案:

答案 0 :(得分:1)

当前的问题是,在失败时,实例构造函数将静态成员侦听器设置为null。但是,控制然后返回静态构造函数,该构造函数将静态成员侦听器设置为在实例构造函数中创建的对象。因此,你所看到的行为。

我认为你的(实例)构造函数试图做太多。我会将'start listening'逻辑移动到一个单独的方法中,并从实例构造函数以外的任何地方调用它。这将使你的错误处理更容易,例如。

class Listener
{
    public static Listener listener = null;  //singleton instance

    //member variables
    private HttpListener httpListener = null;
    private int port = -1;

    static Listener GetListener()
    {
        if (listener != null)
        {
            return listener;
        }

        try
        {
            listener = new Listener();
            listener.StartListening();
            return listener;
        }
        catch (Exception)
        {
            //cant listen on randomly chosen port
            listener.Cleanup();
            listener = null;
            throw;
        }
    }

    private Listener()
    {
        port = RandomlyGenerate();
        httpListener = new HttpListener();
    }

    private void StartListening()
    {
        //start listening
    }

    private void Cleanup()
    {
        httpListener.Close();
        httpListener = null;
        port = -1;
    }
}

答案 1 :(得分:0)

正如BartoszKP在评论中提到的那样,你可能会更好地使用工厂模式。

如果您有兴趣为事件提供一个共同的位置,那么可以将该事件放在工厂类中,或者在Listener类中实现静态事件。

public class ListenerFactory {

    public IListener CreateListener(URI uri, int port) {
        Listener l = new Listener();
        l.MessageReceived += OnMessageReceived;
        // do whatever with l. loop until connection, or use l.Start() for instance
        return l;
    }

    public static event EventHandler<MessageEventArgs> ListenerMessageReceived;

    private static void OnMessageReceived(object sender, MessageEventArgs e) {
        // trigger ListenerMessageReceived
    }
}

public interface IListener {
    event EventHandler<MessageEventArgs> MessageReceived;
    void Send(byte[] data);
}

public class Listener : IListener {
    // implement interface
}

然后,当您需要新的监听器时,只需调用new ListenerFactory().Create(host, port);,如果您想收听所有消息,则可以为ListenerFactory.MessageReceived订阅收到的消息。

使用此模式,您可以一次创建多个连接,而不是依赖一个类来处理所有这些连接。


您应该将httpListener的初始化代码移动到自己的方法,以避免重新创建侦听器。通过执行此操作,并添加用于获取侦听器实例的属性,如果静态构造函数无法连接,则类可以使用Listener.Instance.Start()重新连接。

public class Listener
{
    private static Listener listener = null;  //singleton instance

    //member variables
    private HttpListener httpListener = null;
    private int port = -1;

    static Listener()
    {
        listener = new Listener();
        // start listener
        try {
             listener.Start();
         }
         catch { }
    }

    // Use this method in other classes to start listener if it fails
    // in static constructor
    public static Listener Instance { get { return listener; } }

    private Listener()
    {
    }

     public bool IsConnected {
         get { return httpListener != null; }
     }

     public void Start() 
     {
        if (IsConnected) { return; }
        try
        {
            port = //randomly generate
            httpListener = new HttpListener();
            //start listening
        }   
        catch(Exception ex)
        {
            //cant listen on randomly chosen port
        httpListener = null;
            port = -1;
            return;            
        }   
    }
}