【问题标题】:what is a stateless class?什么是无国籍阶级?
【发布时间】:2011-12-01 07:15:27
【问题描述】:

我想知道无状态类(如果有的话)的缺点是什么? 有没有人见过真实世界的应用程序,其中某些用例强制创建无状态类(请不要问好世界)? 我认为无状态类是指没有任何字段的类。

【问题讨论】:

  • 这是一个奇怪的问题。你想解决什么问题?
  • 我知道这有点抽象,但我目前正在尝试了解是否应该考虑修改我当前工作的代码以合并无状态类(我对无状态类没有经验)......希望我我清楚一点..
  • 完全不清楚你所说的“无状态类”是什么意思。您使用什么语言?您是指没有数据成员的类,还是其他东西(可能是不可变对象)?
  • 我被要求或者更确切地说是被告知将 class myclass {..} 更改为 internal static class myclass{..}

标签: class c#-4.0 stateless


【解决方案1】:

这是一个真实的例子: Writing plugins for Microsoft Dynamics CRM.

在该文档中,您会看到一条说明:“插件的 Execute 方法应该写成无状态的”。 基本上,无状态类的含义是不应该有全局变量(除了一些特殊情况,除非被问到,否则我不会进入)。

CRM 对插件开发人员强制这样做的原因是,CRM 试图通过缓存和重用插件类的实例来提高性能。 CRM 执行其插件的方式大致如下:

主线程:

YourCustomPlugin yourCustomPluginCached = new YourCustomPlugin();

然后: 线程1:

yourCustomPluginCached.Execute(context1);

和线程2:

yourCustomPluginCached.Execute(context2);

一些开发者的错误在于他们会创建一个全局变量来存储上下文,他们将其设置为 Execute() 方法的第一行。这样他们就不必在所有方法之间传递它。但这实际上是一个巨大的错误。 因为如果他们这样做,并且在上面的场景中,如果 thread2 中的执行在 thread1 执行完成之前开始。这意味着 thread1 中的 context1 将被 context2 覆盖。现在这个插件会有一些意想不到的结果。在 99% 的情况下,即使以这种方式开发不正确,也没有问题,或者没有明显的问题。但在 1% 的情况下,它会导致出现问题,并且开发人员很难找出问题所在,并且在测试/调试时可能永远不会发生。所以它可能会在很长一段时间内不修复。

【讨论】:

  • 这个问题要求一个真实的例子。这是第一个提供答案的答案。
  • 几年后我问你几个特殊情况你介意吗?
【解决方案2】:

我从未听说过“无状态类”,但我认为您的意思是不可变对象(非常有用的概念!)。 或者可能是一个没有任何字段的类,所以通常它看起来只是一堆纯函数。

【讨论】:

  • 是的,我的字段被移动到一个方法中,所以这个类只是一堆方法放在一起
  • 你的意思是“静态类”吗?我知道 C# 有它,不确定它是否有很多意义,但是是的,这是一种声明一堆静态方法(通常是无状态的)的方式。
  • 我的猜测是......我的问题仍然是为什么我会选择一个而不是另一个或者更简单的说法是什么会迫使我重新思考我的设计?这是一个关键因素我改成无状态类(没有任何字段的类)?
  • 如果你需要一堆en.wikipedia.org/wiki/Pure_functions。这不是很常见,但通常可能是 utils、helpers 等。
【解决方案3】:

如果stateless class 是指一类不可变对象,那么缺点是变异操作需要复制对象而不是就地更改对象。那可能很贵。

我经常使用这些东西,几乎每当一个对象的行为由一些可以一次处理的输入决定时。最近的一个例子是我实现的statistical language model:模型参数完全在构造函数中根据训练输入确定,然后可以查询模型以获取看不见的文本的概率估计,但不能修改。不变性不是严格要求的,但修改对象没有意义,因为以后不需要添加数据(在这种情况下,无论如何都必须重做大部分计算)。

【讨论】:

  • 嗯...我不确定我是否同意不可变对象是无状态的。但我想这可能是 ashutosh 要求的。
  • 我不知道还有什么是无状态类,除非 OP 使用其中一种语言,其中普通函数需要 class {} 块和两个标识符来命名它。
  • 你能给我举个例子,你开始时没有无状态类,然后有一个给你带来好处?
  • @ashutoshrina:不,因为函数式编程已经感染了我的大脑,以至于我倾向于设计不可变对象,然后在绝对必要时添加突变器。这通常是出于性能原因。
【解决方案4】:

我也不确定你所说的这个术语是什么意思,但我认为它是指没有字段的类,因为对象的状态实际上是其字段的内容。

现在,通常您会将此类类用作相关函数的集合——例如,某个Utils 类。使用这种类的常用方法是将其方法设为静态,因此您实际上不必创建该类的实例。

我能想到实际创建这样一个无状态对象的唯一原因是您是否希望在运行时确定实际功能。所以,如果你有一个 UtilsBase 类,它提供了一堆虚拟方法和一个 UtilsDerived 覆盖了一些方法,你可以传递给需要使用 utils 的任何人对 UtilsBase 的引用,并创建实际的 utils对象在运行时,根据具体需要而定。

【讨论】:

    【解决方案5】:

    在无状态类中,所有字段成员都应该是只读类型。虽然 c# 没有任何这样的功能可以在编译时检查无状态,例如:

    public readonly class MyReadonlyClass
    {
        public readonly double X {get;set;}
        public readonly double Y {get;set;}
        public readonly double Z {get;set;}
    
        public MyReadonlyClass(double x_,double y_,double z_)
        {
            X=x_;Y=y_;Z=z_;
        }
    }
    
    public readonly static class MyStaticReadonlyClass
    {
        public readonly static double X {get;set;}
        public readonly static double Y {get;set;}
        public readonly static double Z {get;set;}
    
        static MyStaticReadonlyClass(double x_,double y_,double z_)
        {
            X=x_;Y=y_;Z=z_;
        }
    
    }
    

    【讨论】:

      【解决方案6】:

      无状态是不应该保留其状态的东西,或者换句话说,我们可以说每次我们使用该类的任何功能或成员时,以前使用/设置的变量不应影响该类/功能的下一次使用。

      考虑以下代码 sn-p(忽略标准)-

      class Maths {
      int radius;
      
      public void setRadius(int r) {
          this.radius = r;
      }
      
      public float getCircleArea() {
          return (float) (3.14 * radius * radius);
      }
      }
      
      public class Geometry {
      public static void main(String[] args) {
          Maths m = new Maths();
          m.setRadius(14);
          System.out.println(m.getCircleArea());
      }
      }
      

      如果其他一些正在运行的线程改变了 Maths 类中的半径值,那么 getCircleArea() 将给我们不同的结果,因为变量“radius”的状态可以改变,因为它是一个全局变量。这个问题主要发生在我们使用 bean 容器的 web 应用程序中。大多数bean都是Singleton并且只有一个副本。如果我们使用全局变量,那么全局变量的值可能会发生变化。

      要使无状态类尝试使用局部变量,以限制变量的范围。 上面getCircleArea()的例子就是。

      public float getCircleArea(int radius) {
          return (float) (3.14 * radius * radius);
      }
      

      【讨论】:

        【解决方案7】:

        有点晚了,但这是我的分享。 我确信海报的意思是stateless object。拜托,人们不要让它变得如此戏剧化。

        让我们开始吧。一些评论者询问了static。当在一个类中使用 static 时,应该谨慎使用,所有那些持有 static 关键字的成员现在都属于该类,而不是不再属于该对象。他们是班级的一部分。 例如:

        用名为Namestatic 属性声明Person 类没有意义

        public class Person
        {
            public **static** string Name { get; set; }
        }
        
        • 顺便说一下,静态的另一个词是“共享”,它在 VB.NET 中使用。

        如果我们曾经在 Person 上设置了名称,那么该名称将在任何地方共享,因为它不属于对象,而是属于 类本身。在现实世界中,您不会称所有人为“Jhon”,也不会称所有客户为“Customer1”。每个人都有不同的名字、姓氏、电话、地址等。一旦我们设置了这些属性,这个对象就变成了有状态的,而不是无状态的。它具有名称、地址等。在类中定义的对象的属性使其具有状态。

        这里的Name属性属于类,不属于对象:

        Person.Name="Fili" //You would not call every person Fili in your application.
        

        让我们通过删除 static 关键字来重新声明 Person 类:

        public class Person 
        {
            public string Name { get; set; }
        }
        

        在下面的示例中,对象是有状态的。它有一个状态,它被命名为“Jhon”,以后可以更改。例如,一个人可以将他的名字更改为 Jimmy。完成后,该对象的状态已从 Jhon 更改为 Jimmy。

        //From
        Person person1 = new Person {
            Name = "Jhon";
        }
        
        //to
        person1.Name = "Jimmy"
        

        对象“person1”仍然是同一个对象,但它的状态已经改变。现在已经不一样了。它有一个不同的名称。 因此,person1 在某个时间点之前一直处于状态,但随后其状态发生了变化。现在,它处于不同的状态。

        现在,创建一个新人是与 person1 无关的新事物。 Person2 有自己的状态。

        Person person2 = new Person {
            Name = "Imir";
        }
        

        使用静态类:Person.Name="Fili"它有一个状态,但它属于该类,它是到处共享的。 因此,无状态对象将是一个无需更改的对象,不具有值。你会创造一个没有名字的人吗?你会创建一个不存在的客户对象吗?不,当然不。当你为一个人或一个客户命名的那一刻,他们就有了一个状态。它们存在并且可以改变它们的状态。

        以飞机为例。例如,当您删除它时,您会添加一个 Status 属性。

        public class Airplane {
          public string Status {get;set;}
        }
        
        var plane1 = new Airplane{ Status="Flying"};
        

        那么,问题来了:此时飞机的状态(status)是什么? 有人会回答:它是“飞行”。如果将状态更改为“taxing”,那么它的状态是“taxing”,或者它是“stationary”,等等。

        【讨论】:

          猜你喜欢
          • 2011-05-12
          • 2019-06-25
          • 2018-07-06
          • 2017-02-11
          • 2012-07-12
          • 1970-01-01
          • 1970-01-01
          • 2014-01-04
          • 2016-10-19
          相关资源
          最近更新 更多