Вопрос по интерфейсам и фабрике обьектов.

hgjtfrdredreder

Путник
Участник
Сообщения
90
Розыгрыши
0
Репутация
-16
Реакции
32
Баллы
46
Хроники
  1. Chaotic Throne: High Five
Исходники
Присутствуют
Сборка
L2J
Возможно это не совсем адекватный вопрос)

Копаюсь в лыже. Хочу максимально уйти от импорта имплементаций и заменить всё что можно интерфейсами. Встает вопрос об инстанцировании обьектов. Есть мысль сделать синглтон-фабрики со статик методами, которые будут создавать обьекты. То есть вся имплементация по сути будет обьявлена лишь в этих фабриках, а весь остальной код будет оперировать только интерфейсами и обращаться за реализацией к ним. Насколько адекватен такой подход? Какие проблемы могут быть проблемы?

И еще глупый вопрос - насколько ресурсоемок каст интерфейса к имплементации? Еслть смысл на это обращать внимание или накладные расходы ничтожны. Чет не смог нагуглить.
 
Чтобы делать как ты хочешь - тебе придется знать в каждом конкретном методе какой у тебя будет объект и все это придется как-то менеджить. Поэтому все обычно и пихают в базовый класс который отвечает за функционал ( аля Creature / Character ), чтобы не ебаться с резолвингом кастов, и где какой объект должен был вернуться.
Но если тебя раздражает 10к строк - можешь вынести функциональный код в сервисы и компоненты. И тогда мувинг у тебя будет не в Creautre, а в MoveController (можно сделать его интерфейсом или абстрактным классом, если базовый мув контроллер один). И вот тут уже фабрика тебе поможет все дженерализировать (через дженерики или вайлдкард):
Описываешь интерфейс фабрики со всеми функциями-генераторами и фабрикой классов, которые будут наследовать / переопределять эти генераторы:
Java:
public interface ObjectFactory<T extends Creature> {
    static ObjectFactory<Creature> playerFactory() {
        return (ObjectFactory<Creature>) Factories.PLAYER_FACTORY;
    }

    static ObjectFactory<Creature> boatFactory() {
        return (ObjectFactory<Creature>) Factories.BOAT_FACTORY;
    }

    MoveController newMoveController(T creature);
}
Имплементируешь генераторы под нужные объекты(родителей-держателей этих сервисов), которые будут использовать разный функциональный код.
Java:
class PlayerFactory implements ObjectFactory<Player> {
    static final ObjectFactory<? extends Creature> FACTORY = new PlayerFactory();

    private PlayerFactory() {}
   
    @Override
    public MoveController newMoveController(Player creature) {
        return new PlayerMoveController(owner);
    }
}
Где то у тебя в базовом объекте все это инициализируется через фабрику один раз. Там же создается геттер под нужный сервис.
Java:
public abstract class Creature extends GameObject {
    private final MoveController moveController;
   
    protected Creature(int objectId) {
        moveController = getFactory().newMoveController(this);
    }
   
    protected abstract ObjectFactory<Creature> getFactory();

    public final MoveController getMove() {
        return moveController;
    }

}

В объекте наследнике фабрика вернет соответствующий этому объекту генератор. И потом когда к обращаешься за нужным сервисом, переопределяешь возвращаемый объект и получаешь нужный класс:
Java:
public class Player extends Creature {

    @Override
    protected ObjectFactory<Creature> getFactory() {
        return ObjectFactory.playerFactory();
    }

    @Override
    public final PlayerMoveController getMove() {
        return (PlayerMoveController) super.getMove();
    }
}

Что это все дает:
Можно разделить функционально код, очень гранулярно разделить его на дерево сервисов и контроллеров, которые будут наследовать и переопределять код друг друга (например, CreatureService с общей логикой, наследованные от него PlayableService и NpcService с собственной) и уже эти компоненты определить нужному родителю (PlayableService - для Player и всех его наследников типа Summon, NpcService - для Npc)
Ну а дальше просто исходя из уровня фантазии кусками любого размера код можно выносить в компоненты.
Насчет производительности - естественно тут будут накладные расходы, тк компилятор не сможет сам определять когда какой объект будет возвращен и все это будет происходить в рантайме. Поэтому все пихать как угарелый в отдельные компоненты не надо, стоит быть рациональным и делать компоненту, которая будет часто переиспользоваться. Тогда вместо постоянного обращения к переменным и функциям через Creature, вся работа будет внутри компоненты PlayerMoveController, который мы получим всего один раз за действие. Это должно быть недорого относительно, остальной нагрузки.
 
Круто, спасибо за развёрнутое пояснение.
Меня не столько даже раздражает код в 10к строк, хотя подобное и усложняет работу с классами, сколько я вижу в этом источник проблем, которые постепенно ведут к говну и палкам - это неизбежно в конечном итоге, когда у тебя в коде срач и не пойми что где валяется и как скомпоновано.
Я ушёл от мысли пихать всё в интерфейсы - слишком много лишних сущностей и гемора с ними. Пришёл примерно к той же мысли, что описана тобой, но гораздо примитивнее пока что - без генераторов) Я подумаю над прочитаным - мне нравится. Благодарю.