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

hgjtfrdredreder

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

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

И еще глупый вопрос - насколько ресурсоемок каст интерфейса к имплементации? Еслть смысл на это обращать внимание или накладные расходы ничтожны. Чет не смог нагуглить.
 
Проблема немного наизнанку. Что-бы знать что должно быть в интерфейсе, нужнo найти код где все обьекты уже используються, ну и описать функции интерфейса. Ну а потом будет вопрос зачем там интерфейс.... Обычно он нужен где нужно прикрыть доступ к множеству функций и ограничить все через функции интерфейса. Почему? A что-бы гарантировать что-бы код мог работать только со специальными обьектами, построенными через такой вот интерфейс.

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

Идея заключается в том, чтобы с помощью интерфейсов как раз таки уменьшить зоопарк классов в основной части кода, подтянуть архитектуру. Зачем импортировать 10 родственных имплементаций, переопределяющих по своему методы родителя, там, где по хорошему должно хватать одного родительского интерфейса, например.
 
Ну по хорошему публичные переменные должны получаться через геттеры, а уж устанавливаться исключительно через сеттеры.

Идея заключается в том, чтобы с помощью интерфейсов как раз таки уменьшить зоопарк классов в основной части кода, подтянуть архитектуру. Зачем импортировать 10 родственных имплементаций, переопределяющих по своему методы родителя, там, где по хорошему должно хватать одного родительского интерфейса, например.
Переменные можно и без геттеров/сеттеров использовать. Вот так все просто. Но всем как-то хочется все спрятать в дополнительный уровень абстрации. Можно? Конечно, вперед...

Нужно сформулировать проблему. Ну например, вот так-то плохо, а будет намного удобнее если интерфейс там будет. А так просто без конкретного примера говорить можно без остановки. Вот в чем заключается зоопарк классов? В их количестве? Или же не нравится что один класс больше чем другой? Или же хочется как-то все это раздробить в более мелкие классы? И в чем будет лучше?
 
Я что-то не пойму как Вы собираетесь интерфейсами все заменить...

Условно в интерфейсе только финальные и статические классы есть. То-есть условно из текущего

Java:
private ClassId _classId;

public void setClassId(ClassId classId)
{
    _classId = classId;
}

вы собираетесь сделать

Java:
private final ClassId _classId;

public void setClassId(ClassId classId)
{
    _classId.setClassId(classId)
}

в таком случае нужно как минимум запрет на чтение дополнительно вешать, или реализовывать как

Java:
public abstract void setClassId(ClassId classId);

private ClassId _classId;

@Override
public void setClassId(ClassId classId)
{
    _classId = classId;
}

в таком случае вы только больше классов продуцируете :\
 
Переменные можно и без геттеров/сеттеров использовать. Вот так все просто. Но всем как-то хочется все спрятать в дополнительный уровень абстрации. Можно? Конечно, вперед...

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

Условно в интерфейсе только финальные и статические классы есть. То-есть условно из текущего
Не догнал пример. Зачем использовать внутри класса его же сеттер? Мож я туплю на ночь, сори)
 
Кривое наследование - методы находятся не там, где должны, одни классы перераздуты, другие наоборот тонкие. Сама архетектура классов выглядит как-то странно местами. Код монолитный шопиздец. Когда начинаешь отходить от реализаций и мыслить абстракциями - понимаешь, что архитектурно творится какая-то дич. Её можно решить и без интерфейсов, конечно, но с ними как-то попроще будет разложить всё по полочкам в голове и коде, мне кажется. Цель их использования не столько прикладная, сколько архитектурная.
Ну я когда начал портировать NASC к себе, столкнулся с тем, что гораздо проще какие-то вещи упаковать в интерфейсы и работать с ними. Я решал таким образом проблему отсутствия множественного наследования, которого нет в Java, но которое есть в C++.
Но все подряд пихать в интерфейсы - смысла нет на самом деле. На вашем месте, я бы начал перепиливание архитектуры L2j с внимательного изучения структуры слитого ПТС. Иначе просто перепилив лыжу в то, что вам кажется верным, на выходе получится ублюдок-мутант, который кроме вас не будет никому нужен. И когда вы столкнетесь с вопросом: "Вот я написал распиздатый движок, но почему-то, чтобы заставить его работать как нужно, мне придется опять перепилить его в фабрику костылей." - будет уже поздно)
 
Вот для ХФ хороший интерфейс, рекомендую: Интерфейс хф + исходники
Спойлерю: там скоро обнова будет.
Спасибо, теперь мои дела пойдут в гору!
Ну я когда начал портировать NASC к себе, столкнулся с тем, что гораздо проще какие-то вещи упаковать в интерфейсы и работать с ними. Я решал таким образом проблему отсутствия множественного наследования, которого нет в Java, но которое есть в C++.
Но все подряд пихать в интерфейсы - смысла нет на самом деле. На вашем месте, я бы начал перепиливание архитектуры L2j с внимательного изучения структуры слитого ПТС. Иначе просто перепилив лыжу в то, что вам кажется верным, на выходе получится ублюдок-мутант, который кроме вас не будет никому нужен. И когда вы столкнетесь с вопросом: "Вот я написал распиздатый движок, но почему-то, чтобы заставить его работать как нужно, мне придется опять перепилить его в фабрику костылей." - будет уже поздно)
Я решаю проблему исправления архитектуры в данном случае. Интерфейсы повысят уровень абстракции и обяжут искать более правильные и красивые решения, а не пилить из говна и палок нечто "главное что работает". Может не совсем уж всё - некоторые классы не нуждаются в интерфейсах, очевидно.
И когда вы столкнетесь с вопросом: "Вот я написал распиздатый движок, но почему-то, чтобы заставить его работать как нужно, мне придется опять перепилить его в фабрику костылей." - будет уже поздно)
Ну вот я и решил посоветоваться)
 
Я решаю проблему исправления архитектуры в данном случае
Вам правильно написали. Сформулируйте конкретную проблему, желательно с примером кода и опишите, как конкретно экстракция интерфейса поможет ее решить. Я не сильно чет могу представить кейс в котором бы это имело смысл в лыже, в контексте ее структуры. И избегайте использовать слово “архитектура”, чтобы не навлечь на себя гнев Rozhek
 
Вам правильно написали. Сформулируйте конкретную проблему, желательно с примером кода и опишите, как конкретно экстракция интерфейса поможет ее решить. Я не сильно чет могу представить кейс в котором бы это имело смысл в лыже, в контексте ее структуры. И избегайте использовать слово “архитектура”, чтобы не навлечь на себя гнев Rozhek
Screenshot_17092024_235513.png

Есть вот такое дерево классов. Оно не полное - там ниже еще инстансы и прочее. Что мы имеем:
1. Корабли наследуются от Character. Почему? Потому что логика движения реализована там, а не в корневом обьекте.
2. Сам Character 10к строк. При том, что часть его методов можно пихнуть в родительский обьект (и отнаследовать корабль от него), а часть вроде как вообще в потомков перенести.
3. Типичное использование в коде примерно такое:
Java:
private PCInstance player;

public void foo(PCInstance p, AVehicle v, Location loc) {
    p.moveTo(loc);
    v.moveTo(loc);
    player = p;
}

public PCInstance get() {
    return player;
}

Зачем тут имплементация, если код moveTo реализован будет в AObject вообще?
Почему не сделать так? Количество сущностей мгновенно сократилось, абстракция повысилась. Оккама одобряе.

Java:
private IObject player;

public void foo(IObject p, IObject v, ILocation loc) {
    p.moveTo(loc);
    v.moveTo(loc);
    this.player = p;
}


public IObject get() {
    return player;
}

// допустим это некий метод обработки игрока
// и мы знаем, что тут прилетает PCInstance
private void doWithPlayer() {
    IObject p = this.get();
    ((IPCInstance) p).doSmth()
}

Мне кажется такой подход поможет привести классы в порядок в процессе приведения их к минимальному количеству интерфейсов.

4. Ещё там есть класс L2Item. Финализированый. То есть он описывает вообще любой айтем. В нём полей 50 наверно. Этому конкретному предмету нужно только 2 поля из 50? [А по щам?] там - он будет хранить все 50.

Мне кажется, что повысив уровень абстракции в процессе пиления интерфейсов мне станет лучше понятно как распределить методы по родителям/наследникам/сиблингам, где может добавить сущностей, где наоборот убрать. А сейчас это дом из говна и палок - одну убираешь и за ней падает к хуям всё вообще по цепочке.
 
1. Корабли наследуются от Character. Почему?
Потому, что корабль это прежде всего сущность и кроме движения, он обладает другими признаками сущности, как-то параметры(скорость, колизии), ИИ и самое важное - способностью слать игрокам о себе информацию, используя сетевые пакеты. Вы будете удивлены, но кубики и агатионы это тоже сущности.

2. Сам Character 10к строк.
Вам смущает количество строк? Почему? Вы можете попробовать сделать грамотную декомпозицию классов Character и Player, но для этого нужно понимать причины, вызвавшие такой размер и я не уверен, что среди этих 10000 строк вы найдете дубликаты кода для более чем 5%(Более того, когда к вам придет осознание того, что APlayble не должно существовать в принципе, эти классы прирастут еще 2-3к строк каждый) А также, в какие-то моменты опять же удобнее обратиться напрямую к родительскому типу сущности, чем делать очередной каст к нужному типу наследника, что в очередной раз снижает когнитивную нагрузку. Да и 10к строк там на самом деле не потому, что разработчики лыжи неквалифицированные идиоты, которые не понимают как писать код.
4. Ещё там есть класс L2Item. Финализированый. То есть он описывает вообще любой айтем. В нём полей 50 наверно
Субъективщина. Мне например, более удобно когда основные проверки и часть логики предмета в одном классе, а не размазана на 20 слоев абстракций, т.к это снижает когнитивную нагрузку и прыгать в дебагере по десятку классов вместо одного метода - такое себе удовольствие. Кроме того, с постепенным узнаванием структуры лыжного ядра, вы поймете, что если в XML описано 2-3 поля итема, не означает, что у него 2-3 свойства, а означает, что остальные 50 полей имеют значение заданное по умолчанию.
 
Последнее редактирование:
Потому, что корабль это прежде всего сущность и кроме движения, он обладает другими признаками сущности, как-то параметры(скорость, колизии), ИИ и самое важное - способностью слать игрокам о себе информацию, используя сетевые пакеты.
Ну так всё это добро наследуется от L2Object - почему не реализовать все базовые для всех обьектов механики там? Это сразу разгрузит L2Character и позволит реализовать общий интерфейс IObject с общими для всех обьектов свойствами. А в L2Character оставить то, что характерно персонажам - скилы etc.
Отсылка пакетов вообще реализуется в потомках с третьего колена вроде - тех, которых нет на диаграмме как раз, и эти методы легко и непринуждённо становятся абстрактными, что превращает L2Object в AObject, что и верно, по сути.
Вам смущает количество строк? Почему? Вы можете попробовать сделать грамотную декомпозицию классов Character и Player, но для этого нужно понимать причины, вызвавшие такой размер и я не уверен, что среди этих 10000 строк вы найдете дубликаты кода для более чем 5%(Более того, когда к вам придет осознание того, что APlayble не должно существовать в принципе, эти классы прирастут еще 2-3к строк каждый) А также, в какие-то моменты опять же удобнее обратиться напрямую к родительскому типу сущности, чем делать очередной каст к нужному типу наследника, что в очередной раз снижает когнитивную нагрузку. Да и 10к строк там на самом деле не потому, что разработчики лыжи неквалифицированные идиоты, которые не понимают как писать код.
Разработчики не идиоты, но их много и коду дохерища лет - он прошел через кучу хроник. И постоянно там что-то пилили и пилят разные люди, что в принципе лишает возможности заняться рефакторингом. Там, например, хватает методов с пометкой Deprecated и TODO:delete this. Декомпозицию классов тоже начинаю делать. Например, кажется, в Character куча думми методов, что явно намекает на косяк в архитектуре в этой части кода - как минимум их можно безболезненно вынести в отдельный подкласс не нарушив структуры. Хотя правильнее будет вынести их в потомков, скорее всего.
Субъективщина. Мне например, более удобно когда основные проверки и часть логики предмета в одном классе, а не размазана на 20 слоев абстракций, т.к это снижает когнитивную нагрузку и прыгать в дебагере по десятку классов вместо одного метода - такое себе удовольствие. Кроме того, с постепенным узнаванием структуры лыжного ядра, вы поймете, что если в XML описано 2-3 поля итема, не означает, что у него 2-3 свойства, а означает, что остальные 50 полей имеют значение заданное по умолчанию.
В том то и дело, что имеют заданное по умолчанию. Но оно не используется, а тупо висит в памяти. А обьектов то много. Хотя я глянул, и наверно это был не Item, а что-то другое. Хотя его тоже можно разбить на отнаследованные подклассы, мне кажется.
Screenshot_18092024_011746.png
Вот нафига, допустим, квест итему большая часть этих полей? Хз. Они просто есть.
 
Так а конечная цель какая все же?
Но оно не используется, а тупо висит в памяти. А обьектов то много.
Снизить потребление памяти? На сколько? Вдвое? Втрое? Вероятно, счет будет идти в лучшем случае - на ДЕСЯТКИ(в лучшем случае ПАРУ СОТЕН) МЕГАбайт. Как вы думаете, два бакса в месяц переплаты за впс стоят пары месяцев вашей работы?


что явно намекает на косяк в архитектуре в этой части кода - как минимум их можно безболезненно вынести в отдельный подкласс не нарушив структуры
А сейчас это дом из говна и палок - одну убираешь и за ней падает к хуям всё вообще по цепочке.
Сделать все академически и эстетически красиво? Ну тогда вам проще писать сразу с нуля, т.к это займет намного меньше времени, чем стабилизировать и причесывать лыжу, т.к там очень много ГЛОБАЛЬНЫХ проебов, об которые вы будет раз за разом убиваться и перед вами будет вставать выбор - переписать вот этот ахуенно огромный глобальный механизм, который тянется через все ядро и прорастает БУКВАЛЬНО В КАЖДЫЙ другой крупный механизм, попутно сломав кучу всего, выбив из под этого костыли, либо сказать - да ну нах, я быстрее с нуля это напишу.
 
Так а конечная цель какая все же?
Сейчас хотя бы минимально структурировать код и подготовить его к более глубокой переработке. Сейчас это невозможно - тронул одно и будешь переписывать вообще всё. Для этого я и пытаюсь повысить уровень абстракции - уменьшить количество сущностей, структурировать, сделать логичным. Программирование как математика - правильно то, что красиво, и красиво то, что правильно. Глобально - хочу сделать правильно.
Снизить потребление памяти? На сколько? Вдвое? Втрое? Вероятно, счет будет идти в лучшем случае - на ДЕСЯТКИ(в лучшем случае ПАРУ СОТЕН) МЕГАбайт. Как вы думаете, два бакса в месяц переплаты за впс стоят пары месяцев вашей работы?
Моё самоуважение и гордость от хорошо проделанной работы этого стоят)
Сделать все академически и эстетически красиво? Ну тогда вам проще писать сразу с нуля, т.к это займет намного меньше времени, чем стабилизировать и причесывать лыжу, т.к там очень много ГЛОБАЛЬНЫХ проебов, об которые вы будет раз за разом убиваться и перед вами будет вставать выбор - переписать вот этот ахуенно огромный глобальный механизм, который тянется через все ядро и прорастает БУКВАЛЬНО В КАЖДЫЙ другой крупный механизм, попутно сломав кучу всего, выбив из под этого костыли, либо сказать - да ну нах, я быстрее с нуля это напишу.
Проще, но не мне, к сожалению. Я никогда не писал серверов и на яве тоже. Спасает только то, что она Си-подобная) Я не знаю как должно быть правильно, но жопой чую, когда не правильно - от этого и пляшу. Так бы конечно лучше сам написал, но это вообще первый проект с таким количеством кодовой базы для меня. С нуля я такое не вывезу - придётся шаг за шагом делать маленькие уборочки то тут, то там, и постепенно исправлять проёбы от меньшего к большему.

Собственно потому я и спрашивал насчет фабрики обьектов. С таким подходом я в один клик через глобальный поиск ИДЕшки мог бы найти все создания обьектов в коде и держать всю имплементацию на виду, а не размазаной тонким слоем по коду, а в коде работать с достаточно ограниченным количеством интерфейсов - это бы дало чуть больше контроля и в последствии бы пригодилось. Но я не уверен, как это будет работать в многопоточности - плохо знаю язык.
 
Весь этот перфекционизм в "вылизывании" кода к сожалению возможен только если у тебя просто дохренища свободного времени и более заняться нечем...
А если у тебя ситуация "это все должно было работать еще вчера", то увы на "сделать все красиво" зачастую просто нет никакого времени.
Я вон тоже периодически порываюсь многое у себя в сервере переделать в плане структуры кода, но где на это найти время? Игроки у меня уж точно не будут ждать еще несколько лет, когда я весь сервер "по фэншую" перепишу. Так что большей частью приходится обходиться относительно точечными переделками...
 
Последнее редактирование:
Весь этот перфекционизм в "вылизывании" кода к сожалению возможен только если у тебя просто дохренища свободного времени и более заняться нечем...
А если у тебя ситуация "это все должно было работать еще вчера", то увы на "сделать все красиво" зачастую просто нет никакого времени.
Я вон тоже периодически порываюсь многое у себя в сервере переделать в плане структуры кода, но где на это найти время? Игроки у меня уж точно не будут ждать еще несколько лет, когда я весь сервер "по фэншую" перепишу. Так что большей частью приходится обходиться относительно точечными переделками...
При этом игрокам глубоко "насрать" как написано код - "по фэншую", или в момент снюхивания кокса с соска проститутки, где потом сам разобраться не можешь, если оно работает - то какая разница как оно написано :)
 
Весь этот перфекционизм в "вылизывании" кода к сожалению возможен только если у тебя просто дохренища свободного времени и более заняться нечем...
А если у тебя ситуация "это все должно было работать еще вчера", то увы на "сделать все красиво" зачастую просто нет никакого времени.
Я вон тоже периодически порываюсь многое у себя в сервере переделать в плане структуры кода, но где на это найти время? Игроки у меня уж точно не будут ждать еще несколько лет, когда я весь сервер "по фэншую" перепишу. Так что большей частью приходится обходиться относительно точечными переделками...
Это да, но у меня есть некоторое количество времени на это. И я надеюсь в последствии отбить потраченное за счет более легкой и быстрой разработки) К тому же мне так и так необходимо перелопатить весь сервак чтобы понять как он работает - рефакторинг отличный способ это сделать, мне кажется)
 
В целом разделение ответственности хорошая практика, как минимум можно будет нормально покрыть тестами логику. Если нет других целей, помимо цели ”сделать красивее”, рекомендую посмотреть в сторону тестирования
 
Назад
Сверху Снизу