How to handle static fields that vary by implementing class我一直都遇到这个问题。假设我正在创建命令行界面(Java或C#,问题与我想的相同,我将在此处显示C#)。 现在-假设接口指定所有命令都实现Name属性和Execute方法... 对于Name,我的每个实例类都必须返回一个字符串,该字符串是该命令的名称。该字符串(" HELP"," PRINT"等)对于相关类是静态的。我希望能够做的是定义: 公共抽象静态常量字符串名称; 但是(很遗憾)您不能在接口中定义静态成员。 我多年来一直在为这个问题而苦苦挣扎(几乎在我拥有类似课程的家庭中任何地方),因此下面将发表我自己的3种可能的解决方案,供您投票。但是,由于它们都不是理想的,我希望有人能发布一个更优雅的解决方案。 更新: 我使用的示例很简单。在现实生活中,有时会有数十个实现类和这种半静态类型的多个字段(即实现类为静态)。 我忘了提-理想情况下,我希望能够静态查询此信息: 字符串名称= CommandHelp.Name; 在我提出的3个解决方案中,有2个要求实例化该类,然后才能发现此静态信息,这很丑陋。 正如您提到的,没有办法从接口级别强制执行此操作。但是,由于您使用的是抽象类,因此您可以在基类中将属性声明为abstract,这将强制继承类覆盖它。在C#中,它将如下所示:
(请注意,受保护的集可防止外部代码更改名称。) 这可能不完全是您要找的东西,但它与我想的差不多。根据定义,静态字段不变。对于给定的类,您根本无法拥有既静态又可重写的成员。 您可能会考虑使用属性而不是字段。
就那么简单。为什么要麻烦一个静态场? 观察:不要在抽象类中使用应在子类中初始化的字段(如David B建议)。如果有人扩展了抽象类而忘记了初始化字段怎么办? 像这样的事情呢:
现在,您可以静态访问信息:
从您的基类:
从设计的角度来看,我认为需要一个静态的实现成员是错误的。在性能和内存使用之间的相对差异是静态的,而不是示例字符串。除此之外,我知道在实施中,所涉及的对象可能具有明显更大的占地面积... 根本的问题是,通过尝试建立模型以支持在C#的基础或接口级别可用的静态实现成员,这是我们的选项受到限制的。在接口级别仅提供属性和方法。 下一个设计难题是代码??是基础代码还是特定于实现的代码。通过实现,您的模型将在编译时得到验证,该代码必须在所有实现中都包含类似的逻辑。使用base时,您的检定将在运行时发生,但是逻辑将集中在一个地方。不幸的是,由于没有逻辑与数据相关联,因此给出的示例是针对特定执行代码的完美展示。 因此,出于示例的考虑,假设存在与数据相关联的某些实际逻辑,并且该逻辑足够广泛/复杂,足以为基础分类提供展示。撇开基类逻辑是否使用任何实现细节,我们要确保实现静态初始化。我建议在基类中使用受保护的抽象,以强制所有实现创建所需的静态数据,这些数据将在编译时进行评估。我使用的所有IDE都可以使此操作变得非常容易。对于Visual Studio,只需单击几下鼠标,然后实质上更改返回值。 回到问题的非常具体的性质,而忽略许多其他设计问题……如果您真的必须将整个问题保持在静态数据的本质上,并且仍然通过问题的本质加以实施,那么……一定要去通过属性的方法,因为有很多副作用可以利用属性。在基类上使用静态成员,在实现上使用静态构造函数来设置名称。现在请记住,您必须在运行时而不是编译时验证名称。基本上,基类上的GetName方法需要处理实现未设置其名称时发生的情况。它可能会引发异常,从而使人们很残酷地意识到,某种实现会由于测试/ QA而不是用户的原因而感到厌倦。或者,您可以使用反射来获取实现名称并尝试生成一个名称...反射的问题在于,它可能会影响子类并设置代码环境,这对于初级开发人员而言很难理解和维护。 .. 为此,您始终可以通过反射从类名生成名称...尽管从长远来看,这可能是一个噩梦,但是它会减少实现上所需的代码量,这似乎更重要比其他任何问题都重要。您也可以在此处使用属性,但是随后您将在时间/工作量上等同于静态构造函数的代码添加到实现中,并且在实现不包含该信息时该怎么办仍然存在问题。 [建议的答案#3 of 3] 我还没有尝试过,并且在Java中还不是很好(我认为呢?),但是我可以用Attributes标记类: [CammandAttribute(名称="HELP")] 然后,我可以使用反射来获取该静态信息。需要一些简单的帮助程序方法,以使信息可以轻松地供类的客户端使用,但这可以在基类中使用。 我的答案将与Java有关,因为这就是我所知道的。接口描述行为,而不是实现。此外,静态字段与类(而不是实例)相关。如果您声明以下内容:
然后此代码如何知道要链接到哪个NAME:
我建议如何实现这一点,是以下方法之一:
但是,一个更好的解决方案可能是使用枚举:
这更加干净,并且允许您使用switch语句,并且很容易获得NAME。但是,您不能扩展选项运行时的数量,但是可以根据您的方案描述来扩展甚至不需要。 [建议的解决方案#2,共3]
好处:
缺点
我通常做的事(伪):
当然,如果这是其他开发人员将使用的库,则在属性中添加一些反射以验证当前实例是否确实实现了覆盖或抛出异常" Not Implemented"也不会受到损害。 只需将name属性添加到基类中,然后将其传递给基类的构造函数,并将派生类的构造函数传递给它的命令名称 [建议的解决方案#1,共3] 在这样的实现中实现:
其中name是该类中定义的常量。 好处:
缺点:
|