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

时间:2011-02-01 08:35:08

标签: java design-patterns

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

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

最终,我希望能够按照

的方式编写代码
if(AbstractParent.doesStringRepresentSubClass("example String"))
   AbstractParent.getInstOfSubClassReppedBy("example String");

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

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

我唯一的其他解决方案想法是

  1. 有一个配置文件,其中每个子类都写入其类名和字符串表示
  2. 每次创建子类时,都会在doesStringRepresentSubClass中的if-else语句中添加一行代码。
  3. 他们是一个更好,更有效的设计方式吗?

    全部谢谢!

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

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

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

7 个答案:

答案 0 :(得分:5)

在我看来,您正在尝试实施factory method

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

e.g。

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

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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

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

它还违反单一责任原则,AbstractParent同时适用于家长和工厂。

还有一个小问题:使用Enum作为密钥优于String

答案 3 :(得分:1)

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))

}

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

答案 4 :(得分:1)

修改 好的,我更了解你想要什么,试试这个。 (这是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"; }
    }
}

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

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