【问题标题】:Why do I need the Singleton design pattern?为什么我需要单例设计模式?
【发布时间】:2010-10-03 17:07:17
【问题描述】:

我尝试学习设计模式,但真的很难理解 OOD 的主要思想。我用经典方法创建了我的软件。另一方面,我想学习OOD。为什么我需要单身人士和其他人?我编写了一些简单的程序:其中一个是古典的(我的风格),另一个是单例模式。请教我为什么需要单例。我的方法比它更好更清晰:)

我的风格:(C#)


  public partial class Singletonsuz : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Loadbalancer balancer = new Loadbalancer();

        for (int i = 0 ; i < 15 ; i++)
        {
            string server = balancer.Server;
            Response.Write("Dispatch Request to: " + server);
        }
    }
}
class Loadbalancer
{
    private List<string> _servers = new List<string>();
    private Random _random = new Random();
    public Loadbalancer()
        {
            _servers.Add("ServerI");
            _servers.Add("ServerII");
            _servers.Add("ServerIII");
            _servers.Add("ServerIV");
            _servers.Add("ServerV");
        }
    public string Server
    {
        get
        {
            int r = _random.Next(_servers.Count);
            return _servers[r].ToString();
        }
    }
}

单身:


    public partial class SingletonDP2 : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            LoadBalancer balancer = LoadBalancer.GetLoadBalancer();
            for (int i = 0; i < 15; i++)
            {
                string server = balancer.Server;
                Response.Write("Dispatch Request to: " + server );
            }
        }

        class LoadBalancer
        {
            private static LoadBalancer _instance;
            private List<string> _servers = new List<string>();
            private Random _random = new Random();

            private static object syncLock = new object();
            protected LoadBalancer()
            {
                _servers.Add("ServerI");
                _servers.Add("ServerII");
                _servers.Add("ServerIII");
                _servers.Add("ServerIV");
                _servers.Add("ServerV");
            }

            public static LoadBalancer GetLoadBalancer()
            {
                if (_instance == null)
                {
                    lock (syncLock)
                    {
                        if (_instance == null)
                        {
                            _instance = new LoadBalancer();
                        }
                    }
                }
                return _instance;
            }

            public string Server
            {
                get
                {
                    int r = _random.Next(_servers.Count);
                    return _servers[r].ToString();
                }
            }
        }
    }

【问题讨论】:

标签: c# .net design-patterns oop


【解决方案1】:

单例模式是widely recognised,并不是真正的模式。它更像是一个如何呈现模式的示例。

您可能会发现Visitor 等更通用的模式更有用。

【讨论】:

  • 被广泛认为不是一种模式?不会这么说,这不是博客文章所说的。潜在有害?这更像是这样,但在错误的时间或错误的数量,包括水在内的一切都是如此。
【解决方案2】:

设计模式用于在大型代码库中构建代码。您可以使用适合您正在开发的应用程序的设计模式,而不是每个程序员都使用自己的编码风格并从左到右连接系统。设计模式基本上说明了您应该如何构建代码,以及不同的系统应该如何相互交互。

使用设计模式可以加快开发时间,并可能迫使您以一种可以防止进一步出现问题的方式进行思考。如果您想在该领域工作,那么学习或至少了解多种设计模式是至关重要的。如果您像牛仔一样开始编写代码,破坏和破坏所选的成语,那么一家拥有大型软件项目的公司将不会对您友好。因为这会使团队中的其他开发人员感到困惑,并且通常会使事情变得更难理解。

【讨论】:

    【解决方案3】:

    你真的不需要模式。您需要的是针对具体问题的解决方案。 模式只是众所周知问题的通用解决方案,因此被认为是良好且有效的解决方案。

    模式的问题在于,您很容易发现自己正在寻找问题的解决方案。即,您开始搜索适合问题的模式,而您应该反过来思考:考虑问题,然后尝试您的解决方案是否与模式匹配。

    当你有一把锤子时,一切看起来都像钉子......

    【讨论】:

    【解决方案4】:

    单例通常只是用来证明某些全局状态的存在。

    如果您有全局状态,请接受它并且不觉得有必要将其包装在像单例这样的模式中,除非在以下有限的情况下:

    您正在从库中呈现此全局状态。

    如果您将其公开为一个普通的公共静态字段,那么更改您依赖全局状态的决定可能会变得极其困难(这可能会发生)。

    在这种情况下,向外界展示价值不是作为单例,而是作为恰好是静态定义的 默认 将允许更改设计(并阻止 API 的用户对待好像它只能是唯一的实例)。

    这实际上只是您当前正在实施的工厂

    因此,构造的“隐藏”是重要的部分,而不是返回值的全局性质。

    如果类的消费者是同一构建过程的一部分(即,如果您以某种方式更改类,受影响的代码将在下一次构建时直接受到影响),则需要进行此包装以允许更改这是一个有争议的问题,因为如果需要,您可以直接更改它。这是您不需要它指南的应用程序。

    【讨论】:

    • 是的,工厂模式肯定更有意义
    【解决方案5】:

    单例模式是一种官方认可的方式,类似于现代 OOP 语言中的全局变量。这可以防止全局变量名冲突等,因为它与一个类隔离,因此更容易避免重复的类名(因为你的编译器会出错)。使用“惰性实例化”创建对象也更容易:您可以等到第一次需要该对象时才在单例函数中实例化它。从长远来看,这可以节省一些 CPU 周期。

    【讨论】:

      【解决方案6】:

      当您向其他人提供代码/库时,单身人士更多...

      您的第一个示例允许多个实例,因此不遵循单例模式。

      第二个示例不允许多个实例...在第一个示例中具有公共构造函数允许 LoadBalancer 的多个实例。因为在第二个示例中它是受保护的(私有的)并且调用者必须使用“GetLoadBalancer”来检查现有实例,所以它只强制执行一个副本。

      【讨论】:

        【解决方案7】:

        设计模式不是设计或开发方法。它们是一个词汇表:它们有助于为软件架构中出现的重复模式命名。根据我的经验,设计一个 FROM 模式的软件最终会导致带有许多单一用途类的毛茸茸的软件,这增加了程序员必须考虑的事情的数量(并且软件开发足够复杂,可以避免让你的大脑充满噪音)。

        但是,设计模式在后期会非常方便。始终从您的特定问题和领域开始,尝试找到解决方案,并确定过程中的模式。不要从模式开始,试图将你的问题强行融入其中。必须了解最常见的模式,因为它可以简化程序员(无论是开发人员还是库用户)之间的交流并促进良好实践。

        例如,假设您的问题涉及数据集。在某些时候,您已经构建了数据结构和算法。现在您(或其他人)需要以更高级别的方式访问您的数据。这是 Iterator 或 Visitor 模式可以应用的典型情况。这就是在 C++ STL 中完成的方式,其中所有集合类都理解迭代器。但是,您无需预先考虑可能会或可能不会在此处或那里应用的模式,一旦确定了模式或需求,总有时间重构事物。

        设计模式最初来自建筑和架构,在很多方面与软件开发非常相似。根据我的经验,理解 DP 的最佳方式是通过与建筑类比:软件就是建筑,模式是建筑元素的组织方式:窗户、门、走廊、楼梯、灯光……建筑师不会考虑他们的元素想用,但想想自己想要得到的效果。例如,建筑师可能会认为:这个楼梯需要光线。为了实现这一点,他可能会根据建筑限制、建筑规范、客户的品味等使用窗户、天窗、玻璃块、人造灯等。他不会在考虑他试图解决的问题之前随意选择元素解决,除非他试图达到某种效果或风格。此外,如果市场上有另一种解决方案(例如反射阳光隧道),那么他可能会将其集成到他未来项目的可用设计模式中。 OTOH,如果他养成在思考问题之前先思考解决方案的习惯,他就会冒着错过替代解决方案、使问题复杂化或根本不解决问题的风险。

        在这里,您使用了单例模式来处理看似需要动态初始化的全局对象。在这种情况下,单例是可接受的解决方案。但是,有时由于外部约束(例如,您需要按特定顺序初始化对象),您可能需要更复杂的解决方案,而 Singleton 将不再适用。或者该对象只需要静态初始化,一个普通的全局变量就可以满足您的需求。

        过度使用设计模式与不在需要的地方使用它们一样糟糕。选择是否使用某种模式通常需要具备软件设计和开发方面的知识和经验,尤其是您所在的领域。

        【讨论】:

        • 我认为你没有读过 Christopher Alexander 的“模式语言”......他最初的(建筑)模式集包括“每个房间两侧的光”之类的东西,而不是“窗户”。但我确实同意软件模式应该是描述性的,而不是规范性的。
        【解决方案8】:

        嗯,你需要知道顾客在哪里使用最重要的东西不是代码是特定于上下文的 如果要确保对应用程序中对象的方法和属性的单点访问,可以使用单例。例如,在处理对数据库中表的访问的类中使用。 然而,即使你真的不需要它们,单例也会在应用程序中传播

        【讨论】:

          【解决方案9】:

          在您的示例中,只有一个 LoadBalancer 实例并没有真正的好处。 这使得理解 Singleton 可能带来的好处变得更加困难。

          您示例中的 LoadBalancer 仅返回随机服务器。

          想象一个 Loadbalancer 类,它跟踪他/她已经返回了哪些服务器,这样 LoadBalancer 可以返回负载最少的服务器。

          在这种情况下,所有 Singletonsuz 实例都必须与同一个 LoadBalancer 通信:如果它们都只是创建自己的(不知道其他 LoadBalancer 做了什么),那么就不会有真正的平衡和跟踪返回的服务器将毫无用处。

          如果他们都需要与同一个实例通信,他们可以调用静态 GetLoadBalancer,然后该单个实例可以返回一个返回次数最少的服务器。

          希望对你有帮助

          一月

          【讨论】:

            【解决方案10】:

            就面试问题而言,这是最著名的设计模式之一。

            想象一栋 G+4 层的建筑。每层有2个单位,大楼内共8个单位。如果每个公寓平均有 4 名成员,那么您最少需要 32 名成员使用电梯。

            现在考虑一栋 8 层的办公楼。每层至少有5个办公室,每层的员工人数不限。

            在第一个场景中,我们不需要多个电梯。在第二种情况下,一个电梯根本不够用。

            现在您已经了解了单次升降机的需求与多台升降机的需求。这为我们理解单例设计模式奠定了基础。

            单一设计模式

            就像前面例子中的电梯一样,Singleton 设计模式也用于创建和使用一个对象的唯一实例。

            为什么?因为应用程序不需要多个对象。

            如果您见过专业办公室,您会发现一层楼的所有员工共用一台打印机。所有的学生都不会在家里备有打印机,他们宁愿去他们殖民地的网吧付款,并使用他的打印机来满足他们的临时需求。

            所有这一切都是因为这里讨论的对象很昂贵。从软件的角度来看,当一个对象利用大量资源来执行其操作时,它就是“昂贵的”。

            所以现在我们需要单例设计模式:

            1) 当应用程序在其整个生命周期中不会多次使用该对象时。

            2) 当应用程序频繁需要一个对象,但对象本身的计算量非常大时。

            单例设计模式的实现

            单例设计模式指出: 允许在整个应用程序中只创建一个类的实例并提供对其的全局访问点

            所以第一部分是指“限制”构造。

            从我们早期在 Java 中实现面向对象的过程中,我们知道构造函数是在程序执行期间调用的特殊函数,它们可以设为私有。当构造函数被设为私有时,类外的任何代码都不能创建它的对象。因此,为了实现单例设计模式,我们将需要一个“私有构造函数”,并且我们将使用该构造函数在类中创建一个对象。

            我们定义的第二部分讨论了给这个单一对象一个全局句柄。我们将使用公共方法来做到这一点。

            【讨论】: