2009-12-18 15 views
1

我有一个应用程序,可以在应用程序加载时从用户那里获取一些数据,并且在整个应用程序中都需要这个应用程序,在整个应用程序中保留持有这些数据的对象的最佳方法是什么应用程序的生命周期?在整个应用程序中都需要的对象

此数据需要可用于应用程序生存期间创建的大多数其他对象。

我前些天学会了单身人士不一定是个好主意。特别是在我的应用程序所在的多线程环境中。

我一直在使用Singletons来处理这个问题,但我想知道这是否是处理这个问题的好方法?

编辑:

让我elobarate:我所要求的用户名和应用程序启动时,现在我知道保持在内存中的密码,密码是出于安全和不好的做法,但在整个登录过程有不少地方我需要这些数据来检查各种东西,所以这就是我使用Singleton的地方。

回答

1

在这种情况下,单是适当的选择。你只是不想开始推动那里很多不相关的东西 - 你希望课堂保持凝聚力,而不仅仅是一堆“物业”。就多线程而言,您可以将适当的控件放在单例类上,没有问题。然而,你使用的锁和保护类型是特定于你的实现的(尽管你的问题没有足够的细节来回答这个问题)。

0

人们说Singleton不一定是个好主意的原因是因为它鼓励像你这样的场景。需要一个静态对象是一个不好的部分 - 一个Singleton被视为一个实现者。

这就是说,我不能真正评论它是否在你的应用程序中是必要的,因为你没有详细说明它。但是,如果您真的需要在应用程序的生命周期中保存在对象中的静态数据,请继续前进并使用单例。 You can make them thread-safe as well.

1

我对此的第一反应是,如果您的应用程序中大多数其他类型都需要一段数据,则可能需要更好地封装它。这听起来像是违反了单一职责原则。但是,如果不了解更多关于您的情况的信息,很难说可以采取何种补救措施。

+0

确实,值得看看编写几个包含密码的Authentication/ConnectionBroker类,并实际执行与安全性相关的功能,而不是将其分散在整个应用程序中。 –

+0

哦,我在看到您的编辑之前回答。当涉及到安全性时,您绝对*必须*预先处理身份验证并将其转换为可通过Thread.CurrentPrincipal访问的IPrincipal。通过评论来讨论这个问题可能太复杂了,所以如果你需要这方面的帮助,可以问一个新问题,并把我的链接:) –

1

从描述您的情况的方式来看,您似乎只想在启动时保存一个字符串,然后在其他任何地方只能读取它。如果是这样的话,你可以真的只是这样做:

internal static class LoginInfo 
{ 
    internal static string Username; 
    internal static string Password; 
} 

然后在您的应用程序的任何地方,你就可以说:

var usr = LoginInfo.Username; 
var pwd = LoginInfo.Password; 

现在我敢肯定,每个人都评论说是为一个可怕的设计实践,但我准备忍受那个:)

现在,如果你打算一直改变这个值,如果这不仅仅是一个字符串而是一些更复杂的对象,那么线程安全肯定会成为一个问题。你总是可以在一个属性上创建一个线程安全的getter。

+0

我喜欢你的想法!出于好奇,是什么让它成为如此糟糕的设计实践? –

2

我建议不要使用定义它自己的单例的类,因为这通常意味着您在单元测试期间会很痛苦。

如果你使用一个通用的单例,你将拥有相同的功能,但在测试/远离单例模式(例如去多用户)时有更多的好处。

请注意,单身人士是用委托进行初始化的。这里的基本原理是代理只会被调用一次,即使两个线程在某种程度上同时注册单例...

使用接口可以让您在编写单元测试时更轻松,因为您可以模拟零件你对你的测试感兴趣的单身人士(或者你的超级快速 - 在演示到CEO补丁/调试会话前2分钟)。

它可能是矫枉过正的存储登录/传递元组,但这种模式保存我的培根更多的时间比我关心计数。

public static class Singleton<T> 
{ 
    private static T instance; 
    private static readonly object sync = new object(); 
    static bool registered = false; 

    public static T Instance 
    { 
     get 
     { 
      return instance; 
     } 
    } 

    public static void Register(Func<T> constructor) 
    { 
     lock (sync) 
     { 
      if (!registered) 
      { 
       instance = constructor(); 
       registered = true; 
      } 
     } 
    } 
} 

class Demo 
{ 
    class Data 
    { 
     public string Pass { get; set; } 
     public string Login { get; set; } 
    } 

    void SimpleUsage() 
    { 
     string login = "SEKRIT"; 
     string pass = "PASSWORD"; 

     // setup 
     Singleton<Data>.Register(() => new Data { Login = login, Pass = pass }); 

     // 
     var ltCommander = Singleton<Data>.Instance; 
    } 

    /// <summary> 
    /// Using an interface will make the singleton mockable for tests! 
    /// That's invaluable when you'll want to fix something FAST without running the whole app! 
    /// </summary> 
    interface IData 
    { 
     string Login { get; } 
     string Password { get; } 
    } 

    class UnitTestFriendlyData : IData 
    { 
     public UnitTestFriendlyData(string login, string password) 
     { 
      Login = login; 
      Password = password; 
     } 


     public string Login { get; private set; } 
     public string Password { get; private set; } 
    } 

    void SmarterUsage() 
    { 
     // same setup, but through the interface. 
     Singleton<IData>.Register(() => new UnitTestFriendlyData("login", "pass")); 

     // and same for the retrieval 
     var data = Singleton<IData>.Instance; 

    } 


    void UnitTestSetupWithMoq() 
    { 
     // Register a mock. 
     var mock = new Mock<IData>(); 
     mock.SetupProperty(x => x.Login, "Login"); 
     mock.SetupProperty(x => x.Pass, "Pass"); 
     Singleton<IData>.Register(() => mock.Object); 

     // and same for the retrieval 
     var data = Singleton<IData>.Instance; 

    } 

} 
相关问题