繁体   English   中英

抽象类需要知道所有扩展类吗?

[英]Abstract Class needs to know all Extending Classes?

所以我有一个类,AbtractParent,以及扩展它的任意数量的子类。

每个子类都有一个与之关联的字符串。

最终,我希望能够编写代码

if(AbstractParent.doesStringRepresentSubClass("example String"))
   AbstractParent.getInstOfSubClassReppedBy("example String");

我目前的解决方案是将地图存储为AbstractParent的静态变量,并让每个子类将自身的实例及其字符串添加到此地图中。

问题是,通过这样做,AbstractParent现在知道它的每个子类,这似乎与OOP的想法相反。

我唯一的解决方案是

  1. 有一个配置文件,其中每个子类写入其类名和字符串表示
  2. 每次我创建一个子类时,都会在doesStringRepresentSubClass中的if-else语句中添加一行代码。

他们是一个更好的,更多OOP正确的设计方式吗?

谢谢大家!

编辑1:字符串表示形式与类名称不同,因此不能使用反射强制转换为类型。

编辑2:这里的最终目标是遵循Open Close原则,因此创建子类应该只需要编辑子类文件。 因此,工厂方法不能完全用于解决问题。

也就是说,将上面的代码分成工厂类和抽象父类绝对是我要实现的好设计。

这对我来说就像你在尝试实现工厂方法一样

您的工厂方法将了解每个子类以及如何在给定字符串的情况下实例化它。 请注意,此知识不在您正在创建的对象的基类中。

例如

public class Factory {
   public AbstractParent newInstance(String spec) {
      if ("example String".equals(spec)) {
         return new ExampleStringSubclass();
      }
      // etc.
   }
}

使用反射可以轻松解决您的问题。 这些方法很有用。 首先,为name实例化类,然后检查它是否是AbstractParent的子类。

在这种情况下,我不认为这是使用继承的正确方法。

因为基类不需要知道它的子类,否则,您的设计会在每次扩展时强制修改AbstractParent 另外, AbstractParent不能用子类代替,它是Liskov Substitution Principle。

它还违反了单一责任原则, AbstractParent既可以作为父级也可以作为工厂使用。

还有一个小问题:使用Enum作为键比使用String更好。

AbstractParent getInstOfSubClassReppedBy(string name) 
{

Type t = Type.GetType(name);
return (AbstractParent) Activator.CreateInstance(t);

}

bool doesStringRepresentSubClass(string name)
{

return Type.GetType(name).IsSubclassOf(typeof(AbstractParent))

}

然后你可以添加一些错误检查

编辑:好的,我更了解你想要的,试试这个。 (这是C#,但我相信你可以将它翻译成)

public abstract class Parent
{
    private static Dictionary<string, Parent> _dic;

    static Parent()
    {
        var subTypes =
        Assembly.GetExecutingAssembly().GetTypes().Where(type =>
            !type.IsAbstract
            && !type.IsInterface
            && type.GetConstructor(new Type[0]) != null  //Has empty constructor
            && typeof(Parent).IsAssignableFrom(type));

        foreach (var type in subTypes)
        {
            Parent obj = (Parent)Activator.CreateInstance(type);
            _dic.Add(obj.Identifier,  obj);
        }
    }

    public static bool IsSubClass(string s)
    {
        return _dic.ContainsKey(s);
    }

    public static Parent GetInstance(string s)
    {
        return _dic[s];
    }

    protected abstract string Identifier { get;}
}

public class Child : Parent
{
    protected override string Identifier
    {
        get { return "MyIdentifier"; }
    }
}

通常这样的东西不属于抽象类。 相反,它们被放在一个称为工厂类的单独类中。 它通常也是抽象的,可以有不同的实现,但这并不是绝对必要的。 不同之处在于,您的抽象父类现在对其子类一无所知,但工厂类却知道。 您如何实施工厂类取决于您。 但是,您不能在Java中使用switch / case作为字符串。 我通常把这种东西当作长链“if”来实现。 也可以使用地图,但是应该在其中放置Class对象或类名,而不是实例。 仅在调用getInstOfSubClassReppedBy()时创建实例。

PS但是,我确实在实践中使用了“抽象父母也是工厂”的方法。 当子类集是固定的并且它们都属于同一个项目时是有意义的,在其他地方定义子类没有任何意义。 例如,我有一些协议的实现,涉及一组固定的命令和以XML格式发送的答案。 当我得到XML答案时,我调用属于抽象父级的XMLAnswer.fromXML(xml)方法。 它从XML中找出要创建的子类。 现在,由于答案集是由协议定义的,因此我只能在发布新协议版本时添加子类,因此需要更改整个内容。 我也可以使用工厂类,但是这不会用于任何特定目的,因为整个事情都是包私有的,并且没有机制可以即时扩展协议。 因此基类有时可以用作工厂,但应该谨慎使用这种技术。

虽然,出于其他答案中给出的所有原因,我不建议您在AbstractParent编写代码,但在某些Factory类中,您可以查看反射库,它提供了易于使用的内省工具。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM