2008-09-15 53 views
4

我一直都在遇到这个问题。假设我正在制作命令行界面(Java或C#,问题与我想的相同,我将在此处显示C#)。如何处理因实施类而异的静态字段

  1. 我定义一个接口的ICommand
  2. 我创建一个抽象基类CommandBase它实现ICommand的,以包含通用代码。
  3. 我创建了几个实现类,每个实现类都扩展了基类(以及扩展接口)。

现在 - 假设接口指定了所有的命令实现Name属性和Execute方法...

对于每名我的实例类必须返回一个字符串,该命令的名称。该字符串(“HELP”,“PRINT”等)对于相关类是静态的。我希望能够做的是定义:

public abstract static const String Name;

但是(可惜)你不能在接口中定义静态成员。

我现在这个问题奋斗多年(几乎任何地方,我有一个类似的家庭类)等将张贴下面您的投票我自己的3个可能的解决方案。然而,因为他们都不是理想的,我希望有人会发布更优雅的解决方案。


UPDATE:

  1. 我不能得到的代码格式才能正常工作(Safari浏览器/ Mac吗?)。道歉。
  2. 我正在使用的示例很简单。在现实生活中,有时会有几十个实现类和这个半静态类型的几个字段(即对于实现类是静态的)。

  3. 我忘了提 - 理想我希望能够查询这个信息静态:

    字符串名称= CommandHelp.Name;

我3级提出的解决方案的2需要的类实例化之前,你可以找到这个静态信息这是丑陋的。

回答

0

[建议的解决方案的#3 1]

  1. 在界面定义一个抽象属性Name迫使所有执行类来实现的名称属性。
  2. (在c#中)在基类中将此属性添加为摘要。
  3. 在实现都实现了这样的:

    public string Name 
    { 
        get {return COMMAND_NAME;} 
    } 
    

    name是在类中定义的常量。

    优点:

    • 名称本身定义为一个常数。
    • 接口要求创建属性。

    缺点:

    • 复制(我恨)。完全相同的属性访问器代码粘贴到我的每个实现中。为什么不能在基础课程中避免混乱?
2

您可以考虑使用属性,而不是字段。

[Command("HELP")] 
class HelpCommand : ICommand 
{ 
} 
0

只是名字属性添加到基础类,并通过它ITO基类的构造函数,并具有从派生类传球constuctor在它的命令名称

1
public interface ICommand { 
     String getName(); 
} 

public class RealCommand implements ICommand { 
    public String getName() { 
      return "name"; 
    } 
} 

这么简单。为什么打扰有一个静态领域?


实验值:不要在应该在子类(如David B建议)发起一个抽象类,使用的字段。如果某人扩展了抽象类并忘记发起这个领域呢?

+0

这不是在说原来的问题,但他/他想要一个字段,以便他/她可以绑定到它在WPF中。 – 2010-10-20 07:32:23

0

我最常做的(伪):

abstract class: 

private const string nameConstant = "ABSTRACT"; 

public string Name 
{ 
    get {return this.GetName();} 
} 

protected virtual string GetName() 
{ 
    return MyAbstractClass.nameConstant; 
} 

---- 

class ChildClass : MyAbstractClass 
{ 
    private const string nameConstant = "ChildClass"; 

    protected override string GetName() 
    { 
     return ChildClass.nameConstant; 
    } 
} 

当然,如果这是其他开发人员使用一个库,它不会,如果你在验证属性添加一些反射伤害实际上当前实例确实实现了覆盖或抛出了一个“未实现”的异常。

0

[建议的解决方案的#3 2]

  • 制作一个私有成员变量名。
  • 在界面中定义一个抽象属性名称。
  • 实现这样在基类的属性:

    public string Name 
    { 
        get {return Name;} 
    } 
    
  • 强制所有实现调用抽象基类的构造函数时传递的名称作为构造函数的参数:

    public abstract class CommandBase(string commandName) : ICommand 
    { 
        name = commandName; 
    } 
    
  • 现在所有我的实现在构造函数中设置名称:

    public class CommandHelp : CommandBase(COMMAND_NAME) {} 
    

优点:

  • 我的访问代码在基类集中。
  • 名称被定义为一个常数

缺点

  • 名称现在是一个实例变量 - 我指挥班的每个实例 使一个新的参考,而不是 共享一个静态变量。
0

我的答案将涉及到Java,因为这是我所知道的。接口描述行为,而不是实现。此外,静态字段绑定到类,而不是实例。如果您宣布:

interface A { abstract static NAME } 
class B { NAME = "HELP" } 
class C { NAME = "PRINT" } 

话,怎么可能这个代码知道链接哪个名字来:

void test(A a) { 
    a.NAME; 
} 

我怎么会建议实现这一点,是通过以下方式之一:

  • 类名约定,基类从类名派生名称。如果你想偏离这个,直接覆盖接口。
  • 基类有一个构造函数,其名称为
  • 使用注释并通过基类强制它们的存在。

然而,一个更好的解决方案是proabably使用枚举:

public enum Command { 
    HELP { execute() }, PRINT { execute() }; 
    abstract void execute(); 
} 

这是更清洁,并允许您使用switch语句,并名字将很容易得到。但是,您无法扩展选件运行时的数量,但是可能不需要您的场景描述。

+0

这不会在C#中工作,因为您无法在接口中声明抽象成员;否则它看起来会很好。 – 2008-09-15 20:32:45

0

[建议答案#3/3]我还没有尝试这样做,它不会在Java中是那么好(我想?),但我可能只是标记我的班,属性:

[CammandAttribute(Name =“HELP”)]

然后我可以使用反射来获取该静态信息。需要一些简单的辅助方法来使信息很容易提供给班级的客户,但这可能会在基类中。

2

正如你所提到的,没有办法从接口级强制执行此操作。但是,由于您使用的是抽象类,因此可以在中做的是将该属性声明为基类中的抽象类,这将强制继承类重写它。在C#中,这应该是这样的:

public abstract class MyBaseClass 
{ 
    public abstract string Name { get; protected set; } 
} 

public class MyClass : MyBaseClass 
{ 
    public override string Name 
    { 
    get { return "CommandName"; } 
    protected set { } 
    } 
} 

(注意保护设置防止外部的代码更改名称。)

这可能不是你在寻找什么,但它的尽可能接近我认为你可以得到的。根据定义,静态字段变化;您无法为给定的类设置静态和可重写的成员。

0

从设计的角度来看,我认为这是错误的,要求一个静态实现成员...静态而不是为例子字符串是最小的之间的性能和内存使用之间的相对尊重。除此之外,我知道在实现时,所讨论的对象可能会有更大的尺寸...

基本问题是,通过尝试设置模型来支持静态实现成员,这些静态实现成员可以在基础或接口与C#级别的是,我们的选项是有限的...只有属性和方法在接口级别可用。

下一个设计挑战是代码是否是基地或执行特定的。通过实现,您的模型在编译时必须在所有实现中包含类似逻辑的代码中获得一些值。基地你的报价将在运行时发生,但逻辑将集中在一个地方。不幸的是,给出的例子是实现特定代码的完美例子,因为没有与数据相关的逻辑。

因此,为了举例,我们假设有一些与数据相关的实际逻辑,并且它足够广泛或足够复杂,以便为基类提供展示。撇开基类的逻辑是否使用任何impelementation细节与否,我们都确保implemtation静态初始化的问题。我会建议在基类中使用受保护的抽象强制所有实现创建所需的静态数据,将在编译时valdated。所有IDE的工作都可以让这一切变得非常简单。对于Visual Studio,只需点击几下鼠标,然后根本改变返回值。

回到问题的非常具体的性质,忽略许多其他设计问题......如果你真的必须保持这整个静态数据的性质,并仍然通过问题的性质限制执行它。 ..肯定会用一种方法来处理属性,因为有很多副作用可以使用属性。在基类上使用静态成员,并在实现上使用静态构造函数来设置名称。现在请记住,您必须在运行时调用名称,而不是编译时间。基本上,基类中的GetName方法需要处理实现没有设置名称时发生的情况。它可以抛出一个异常使其惨遭明显的东西是与通过测试/质量保证,而不是用户hopefulyl导致执行拨错。或者您可以使用反射来获取实现名称并尝试生成一个名称......反射的问题是它可能会影响子类并设置代码情况,这对于初级开发人员来说很难理解和维护。 ..

对于这个问题,你总是可以通过反射从类名生成名称......虽然从长远来看,这可能是维护的噩梦......然而,这会减少所需的代码量实现,这似乎比任何其他问题更重要。你也可以在这里使用属性,但是你将代码添加到与静态构造函数相同的实现中,并且当实现不包含这些信息时仍然存在问题。

0

什么是这样的:

abstract class Command { 
    abstract CommandInfo getInfo(); 
} 

class CommandInfo { 
    string Name; 
    string Description; 
    Foo Bar; 
} 

class RunCommand { 
    static CommandInfo Info = new CommandInfo() { Name = "Run", Foo = new Foo(42) }; 

    override commandInfo getInfo() { return Info; } 
} 

现在您可以访问静态的信​​息:

RunCommand.Info.Name; 

而且从你的基类:

getInfo().Name;