Наследование с неизвестными потомками - PullRequest
0 голосов
/ 13 марта 2012

Допустим, у меня есть лабиринт с AI-символами, где пользователи определяют символы.Каждый пользователь предоставляет классы для своих отдельных персонажей.Все символы / классы расширяют некоторый класс / тип C, который имеет метод control ().

Я хочу вызывать метод control () каждого пользователя, но я не знаю, сколько пользователей будет или чтозанятия они будут проводить.Как мне решить эту проблему?

РЕДАКТИРОВАТЬ: Я хотел передать, что я не знаю, сколько подклассов, или как их имена.Поэтому я не могу поместить эти подклассы в код статически.

РЕДАКТИРОВАТЬ 2: Есть ли способ сделать это без использования отражения?Я знаю, что рефлексия решает проблему, но я надеялся, что была более чистая реализация.

РЕДАКТИРОВАТЬ 3: Абсолютно необходимо, чтобы пользователи создавали разные классы, так как целью программы является тестирование конкурирующих ИИ.

Кстати, я пишу это на Java.

Ответы [ 3 ]

2 голосов
/ 13 марта 2012

Суть наследования в том, что вам не нужно знать точный тип. Если у вас есть ссылка на объект типа C или подкласса C, вы можете вызвать для них ваш метод «control ()», и он вызовет правильный метод, т. Е. Тот, который реализован дочерним классом.

Не зная, сколько пользователей означает, что вам придется использовать список или что-то еще и перебирать его.

public class AIGame {
public static void main(String[] args) {
    List<AICharacter> characters = new ArrayList<AICharacter>();
    characters.add( new ReallySmartAICharacter() );
    characters.add( new ReallyDumbAICharacter() );
    for ( AICharacter c : characters ) {
        c.control();
    }
    }
}

interface AICharacter {
    public void control();
}

class ReallySmartAICharacter implements AICharacter {
    @Override
    public void control() {
        // TODO do something clever here        
    }   
}

class ReallyDumbAICharacter implements AICharacter {
    @Override
    public void control() {
        // TODO do something stupid here        
    }
}
2 голосов
/ 14 марта 2012

Прежде всего, вам необходимо решить, будет ли поведение разных персонажей действительно настолько дифференцированным, как необходимость в Java-коде для реализации конкретного поведения.Возможно, поведение может быть выражено одним классом и изменено только путем установки различных значений таких параметров, как скорость, здоровье, сила атаки и т. Д. В этом случае вы полностью избавитесь от проблемы наследования и будете использовать один класс, в то время как пользователи будут толькопредоставить различные конфигурации.

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

Первое - стандартное.Это использует только немного отражения.Вы определяете интерфейс, например:

public interface C {
    void control(); //Params skipped for brevity
}

Теперь ваши пользователи создают классы, которые реализуют этот интерфейс.Единственная проблема заключается в том, как создать экземпляр класса игрока.Получив его, вы вызываете его control() или другие методы через интерфейс.Во-первых, пользователи должны сделать этот класс загружаемым.Это можно сделать через сеть или другими сложными способами, но самое простое - они помещают свой файл .class или .jar в путь к классам при запуске приложения.Теперь все, что вам нужно, это создать экземпляр класса.Предполагая, что вы указываете требование, чтобы класс имел конструктор с нулевым аргументом (вы можете определить метод в вашем интерфейсе для загрузки некоторой конфигурации и выполнить инициализацию позже), вы будете делать что-то вроде:

C gameCharacter = (C)Class.forName("your.fully.qualified.ClassName").newInstance();

Помимо обработки ошибок, это все, что вам нужно.Теперь вы можете вызывать все методы интерфейса C для вашего gameCharacter объекта - не зная, кто или как его написал и что именно методы делают.

Другое решение будет использовать Groovy или другой подобный язык для компиляции и запуска кода на лету.В этом случае вам не нужен пользовательский JAR в пути к классам, и вы даже можете обойти необходимость знать имя класса для загрузки.Ваш пользователь может предоставить Java-код метода control() в виде текста, и вы можете иметь класс-заглушку, метод control() которого компилирует и выполняет только Groovy-код, предоставленный пользователем.Это может быть более удобным, но требует, чтобы пользовательский код символа был предоставлен вам как исходный код, а не скомпилированный JAR, что может быть проблемой для некоторых пользователей.Кроме того, это решение более удобно, если реализации будут короткими и автономными, в то время как отдельный JAR и загрузка через отражение лучше, если загруженный код более сложен, использует вспомогательные классы, кроме основного и т. Д.

0 голосов
/ 13 марта 2012

Если все символы расширяют некоторый общий класс, для удобства назовем его Character, тогда вы можете использовать полиморфизм для динамического вызова каждого из control() методов.

Другими словами, если каждый подклассиз Character переопределяет control(), тогда все, что вам нужно сделать, это вызвать его как обычно, и Java определит, какой control() метод вызвать.

например,

Character[] characters = new Character[2];
characters[0] = new Man(); // Man is a subclass of Character
characters[1] = new Woman(); // same with Woman

character[0].control(); // <- this will call the control() method as defined in Man

Механизмэто называется поздней (или динамической) привязкой, о которой вы можете прочитать подробнее здесь: http://en.wikipedia.org/wiki/Late_binding

Если подклассы неизвестны во время компиляции (то есть они указаны во время выполнения), тогдавам нужно будет использовать отражение для их загрузки.

Чтобы отслеживать каждого пользователя, используйте динамический размер List, например LinkedList или ArrayList.Таким образом, вам не нужно заранее знать, сколько пользователей.

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