Абстрактный класс должен знать все классы расширения? - PullRequest
3 голосов
/ 01 февраля 2011

Итак, у меня есть класс AbtractParent и произвольное количество подклассов, расширяющих его.

Каждый подкласс имеет строку, связанную с ним.

В конце концов, я хочу иметь возможность писать код в соответствии с

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

Мое текущее решение - сохранить карту как статическую переменную AbstractParent, и каждый подкласс должен добавить экземпляр себя и свою строку к этой карте.

Проблема в том, что благодаря этому AbstractParent теперь получает знания о каждом из своих подклассов, что кажется противоречащим идеям ООП.

Мои единственные другие решения - это

  1. имеет файл конфигурации, в котором каждый подкласс записывает свое имя класса и строковое представление
  2. каждый раз, когда я создаю подкласс, добавляем строку кода в оператор if-else внутри didStringRepresentSubClass.

Является ли их лучший, более ООП-правильный способ сделать это?

Спасибо всем!

Редактировать 1: строковое представление НЕ будет совпадать с именем класса и, следовательно, не может быть приведено к типу с использованием отражения.

Редактировать 2: Конечная цель здесь - следовать принципу Open Close, и поэтому создание подкласса должно требовать редактирования только файла подкласса. Из-за этого фабричный метод не может быть использован для полного решения проблемы.

Тем не менее, разделение моего кода выше и на фабричный класс, и на абстрактный родительский класс, безусловно, хороший дизайн, который я буду реализовывать.

Ответы [ 7 ]

5 голосов
/ 01 февраля 2011

Мне кажется, что вы пытаетесь реализовать фабричный метод .

Ваш фабричный метод будет знать каждый подкласс и как его создать, используя строку. Обратите внимание, что эти знания не находятся в базовом классе создаваемых вами объектов.

, например

public class Factory {
   public AbstractParent newInstance(String spec) {
      if ("example String".equals(spec)) {
         return new ExampleStringSubclass();
      }
      // etc.
   }
}
1 голос
/ 01 февраля 2011

РЕДАКТИРОВАТЬ: Хорошо, я понимаю, что вы хотите, попробуйте это.(Это C #, но я уверен, что вы можете перевести его) в Java

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"; }
    }
}
1 голос
/ 01 февраля 2011
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))

}

И тогда вы можете добавить некоторые проверки ошибок

1 голос
/ 01 февраля 2011

Я не думаю, что в этом случае это правильный способ использования наследования.

Поскольку базовый класс не должен знать свои подклассы, в противном случае ваш дизайн заставляет AbstractParent изменяться каждый раз при расширении. Кроме того, AbstractParent нельзя заменить подклассами, это Подстановка Лискова Принцип.

Это также нарушает Единую ответственность Принцип, AbstractParent работает как родитель и фабрика.

И небольшая проблема: лучше использовать Enum в качестве ключа, чем String.

1 голос
/ 01 февраля 2011

Ваша проблема может быть легко решена с помощью отражения. Эти методы будут полезны.Сначала вы создаете экземпляр класса для name, а затем проверяете, является ли это подклассом вашего AbstractParent.

0 голосов
/ 01 февраля 2011

Хотя, по всем причинам, приведенным в других ответах, я бы не рекомендовал вам писать код в вашем AbstractParent, но в некотором классе Factory вы могли бы взглянуть на библиотеку отражений ,предоставьте простые в использовании утилиты для самоанализа.

0 голосов
/ 01 февраля 2011

Обычно такие вещи не относятся к абстрактному классу. Вместо этого они помещаются в отдельный класс, который называется фабричным классом. Часто он тоже абстрактный и может иметь разные реализации, но это не является строго обязательным. Разница в том, что ваш абстрактный родительский класс теперь ничего не знает о своих подклассах, а класс фабрики знает. Как вы реализуете фабричный класс, зависит от вас. Вы не можете использовать переключатель / регистр для строк в Java, хотя. Я обычно реализую такие вещи как длинную цепочку «если». Также можно использовать карту, но вы должны помещать в нее объекты класса или имена классов, а не экземпляры. Создавать экземпляры только при вызове getInstOfSubClassReppedBy ().

P. С. Однако на практике я использовал подход «абстрактный родитель - это тоже фабрика». Это имеет смысл, когда набор подклассов является фиксированным, и все они принадлежат одному и тому же проекту, и не имеет никакого смысла определять подклассы где-либо еще. Например, у меня есть реализация некоторого протокола, включающего фиксированный набор команд и ответов, которые отправляются в XML. Когда я получаю ответ XML, я вызываю метод XMLAnswer.fromXML (xml), который принадлежит абстрактному родителю. Он выясняет из XML, какой подкласс создать. Теперь, поскольку набор ответов определен протоколом, я могу добавить подкласс только при выпуске новой версии протокола, поэтому все это необходимо изменить. Я мог бы также использовать фабричный класс, но это не послужило бы какой-либо конкретной цели, так как все это в любом случае является пакетно-частным, и нет механизма для расширения протокола на лету. Поэтому базовый класс иногда можно использовать как фабрику, но эту технику следует использовать с осторожностью.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...