我想知道无状态类(如果有的话)的缺点是什么?有没有人见过一个真实世界的应用程序,其中一些用例要求创建一个无状态类(No hello world please)?我认为无状态类是指没有任何字段的类。
7 回答
这是一个真实的示例: 为 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% 的情况下,它会导致出现问题,并且开发人员很难找出问题所在,并且在测试/调试时可能永远不会发生。
我从未听说过“无状态类”,但我认为您的意思是不可变对象(非常有用的概念!)。或者可能是一个没有任何字段的类,所以通常它看起来只是一堆纯函数。
我也不确定这个术语是什么意思,但我认为它是指没有字段的类,因为对象的状态实际上是其字段的内容。
现在,通常你会使用这种类作为相关函数的集合——比如某个 Utils
类。使用这种类的常用方法是将其方法设为静态,因此您实际上不必创建该类的实例。
我能想到实际创建这样一个无状态对象的唯一原因是您是否希望在运行时确定实际功能。因此,根据UtilsBase
_具体需求。UtilsDerived
UtilsBase
在无状态类中,所有字段成员都应该是只读类型。尽管 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_;
}
}
无状态是不应该保留其状态的东西,换句话说,我们可以说每次我们使用该类的任何功能或成员时,先前使用/设置的变量不应影响该类/功能的下一次使用。
考虑以下代码片段(忽略标准)-
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);
}
有点晚了,但这是我的分享。我确定海报的意思是stateless object
. 拜托,人们不要让它如此戏剧化。
让我们开始吧。一些评论者询问了static
. 当在一个类中使用 static 时,应该谨慎使用,所有那些持有 static 关键字的成员现在都属于该类,而不再属于该对象。他们是班级的一部分。例如:
用名为的属性声明一个Person
类没有意义static
Name
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”,等等。