вторник, 25 марта 2014 г.

Вброс для программистов

Так как я пишу на RenPy игру которая выходит за пределы простой визуальной новеллы, то можно считать что я пишу её на Phyton-e. А питон как известно поддерживает объектно-ориентированную парадигму. Я долго не понимал что это такое, но в итоге вроде бы разобрался в общих чертах. Но возникает второй вопрос. А нужна ли она?
Вопрос вполне практический, так как сейчас я взялся намётывать боевую систему. С точки зрения ООП надо все сущности загнать в классы: классы для существ, классы для оружия, классы для боевых приёмов и т.п.
С другой стороны все необходимые атрибуты можно прописать в виде вложенных словарей, как я делал это в предыдущем модуле. И такой подход работает вполне неплохо, даже экономит объём кода. Методы классов вполне можно заменить отдельными функциями.
Возникает вопрос. Нужно ли мне заморачиваться с ООП подходом и что это даст?
Зачем писать класс как класс, а не как словарь и функции?

36 комментариев:

  1. Как гласит теория (подойдет для твоего случая):

    1. Классы позволяют проводить конструирование из полезных компонент, обладающих простыми инструментами, что дает возможность абстрагироваться от деталей реализации.

    2. Данные и операции вместе образуют определенную сущность и они не «размазываются» по всей программе, как это нередко бывает в случае процедурного программирования.

    Использование классов позволит тебе не загромождать код большим числом функций (процедур). Ты можешь создать компактный класс с необходимыми методами.

    На практике:

    Тебе надо создать 30 вражеских юнитов.

    1й тип врагов состоит из 15 юнитов, которые ты описываешь в классе 'ВРАГ1".
    у этих юнитов будет всего 2 способности (т.е. 2 метода) : удар рукой и удар ногой.

    2й тип врагов состоит из 15 юнитов, которые ты описываешь в классе 'ВРАГ2", которые наследует класс 'ВРАГ1".
    у этих юнитов будет уже 3 способности (т.е. 3 метода) : удар рукой, удар ногой и удар хлыстом.
    Так вот тебе во 2м классе достаточно описать 1 метод 'УДАР ХЛЫСТОМ', а остальные 2 метода будут унаследованы от предыдущего класса.

    И кстати программы написанные с использованием ООП работают быстрее, чем программы использующие процедурное программирование.

    Если будут еще вопросы - пиши.

    ОтветитьУдалить
    Ответы
    1. 1-2. Дядя это вы с кем сейчас говорили? Ничего не понял.
      Классы не компактнее ни разу. Класс надо объявлять и инициализировать, но при этом методы в нем занимают столько же места сколько и функции вне класса. То же самое с переменными. Не важно где они в списке или в классе. В классе даже больше места займут: self.variable против 'variable'
      Мне надо создать 30 юнитов. Я описываю все способности в трех функциях. Тип юнита1 прописываю как словарь. Тип юнита2 прописываю юнит1 + словарь с изменениями.
      По объёму кода и простоте решения - экономия.

      И кстати я не крузис пишу, мои программы и на калькуляторе должны быстро работать.

      Так что пока не понятно в чем цимес...

      Удалить
    2. Обычно ООП программы работают медленнее), но на питоне не должно быть заметно.

      Удалить
    3. Этот комментарий был удален автором.

      Удалить
  2. Заморачиваться точно стоит :)
    Я в своё время тоже не мог понять нахрена это нужно. Потом в голове щелкнула идея, что это просто для удобства объединяем код и данные в одну кучку. Как раз в это время я начал писать сложный (на то время) проект с библиотеками и классы легко и просто вписались в идею разделения всего проекта на несколько файлов.
    На первых этапах можно писать точно так же как и в процедурном программировании, только функции и процедуры будут как бы привязаны к классу.
    Про скорость работы несогласен с предыдущим оратором особой разницы быть не должно.

    ОтветитьУдалить
  3. Стоит.
    Вы сами очень быстро почувствуете, насколько вам удобнее составлять систему из частей. И код писать проще, и ошибки находить.
    А если к кодингу подключится кто-то помимо вас, то ООП - это вообще маст хэв, без которого два-три человека будут только мешать друг другу.

    ОтветитьУдалить
    Ответы
    1. >> Почему?

      Грубо говоря, классы - это как торговый автомат: нажали на одну кнопку - выпала шоколадка; на другую - сухарики. И вам не нужно знать, конструкцию аппарата, чтобы им пользоваться. Только уметь дать входящие данные (кнопки нажать) и забрать итоговый результат (получить хавчик).

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

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

      Удалить
  4. Класс - эта такая очень большая абстракция.
    Т.е. допустим. Собака - это класс. Шарпей - унаследованный от собаки класс. Конкретный тузик у тети Вали - экземпляр класса Шарпей.
    Что дает такой подход? То что один раз определив что собаки умеют лаять (метод класса), нам не нужно каждый раз писать как именно это делает каждая порода и каждая конкретная собака в частности.
    Естественно, не всегда стоит заморачиваться с классами. В том плане, что не всегда стоит городить отдельный класс когда можно ограничиться методом у уже существующего.

    ОтветитьУдалить
    Ответы
    1. А чем это отличается от функции лая которая прописана вне класса но всё равно одна и та же у всех собак?

      Удалить
    2. Тем, что допустим, собаки умеют лаять. Если эта функция просто выводит в чатик лай, то никаких проблем. А если эта же функция должна еще отнимать у той собаки, которая лает, стамину? Ей нужно будет передавать параметр, где хранится значение стамины. А если она еще, допустим увеличивает голод? Ей нужно будет передать параметр где хранится голод для этой собаки.
      При работе с классом, после создания объекта класс может хранить переменные для значений стамины и голода для данной собаки.
      Т.е. мы получаем следущие плюсы.
      1) Все данные про собаку храняться в одном месте.
      2) Другие объекты ничего не знают про состояние этой собаки, что ведет к уменьшению вероятности ошибки..
      3) Значительно проще искать ошибки и сопровождать такой код. Засчет того, что вместо хранения разных переменных в разных местах (про которые нужно помнить в один и тот же момент времени) мы получаем аккуратный объект.
      4) В случае необходимости мы можем переопределить функцию лая в потомках класса. Т.е. все собаки лаят "гав", а Доге лает "вов". При этом у нас останется единый интерфейс для лая: bark(), вместо двух функций. Можно использовать и одну функцию, но это приведет к переусложнению кода.

      Удалить
    3. 1) Ну есть же словарь собаки. В нём и хранятся все её переменные касающиеся её.
      2) Других объектов вообще нет, если не ООП )
      3) Вот это интересный момент. Да тут уже писали про структурирование, наверное это действительно плюс.
      4) модификация единой функции имеет свои преимущества, особенно в объёме кода

      Собственно что меня напрягает это появление микроклассов которые имеют один метод и полторы переменных, но при этом отдельно инициализируются и захламляют всё. Плюс проблемы с наследованием, особенно множественным и т.п. Чтобы писать объектами, особенно с иерархией наследования, надо заранее представлять себе какие они будут нужны в конце, а у меня такого понимания нет,я пишу программу на ощупь. Учусь прямо в процессе написания.

      Но так или иначе я попробую сделать боёвку в рамках ООП и посмотрю как оно пойдёт на практике.

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

      Удалить
    5. Могу дать общий совет не ударяться в крайности.
      Не обязательно делать выбор между полным отсутвием ООП и классами для каждой мелочи объединенными в жуткую иерархию которая все равно 10 раз изменится до релиза. Баланс и золотая середина наше всё.

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

      А в чем проблема с инициализацией, я не очень понимаю если честно. Можно ведь просто не инициализировать, если так уж не хочется)

      Удалить
    6. >микрокласс
      Я уже писал, что доводить до абсурда не надо. Класс на 1 функцию это уж слишком
      >надо заранее представлять
      Попробуй начать с ТЗ. Заранее определи что ты хочешь получить в итоге, при этом сразу же в ТЗ оговори что возможно (но не факт) может измениться. А уже по этому делай архитектуру приложения/модуля.
      Понятно, что за раз все не получится, еще придется вернуться к ТЗ и переписать интерфейсы.
      Часть смысла ООП в модульности. Есть некий модуль (класс/набор классов), который предоставляет некий API/функционал, который скрыт от пользователя этого API/функционала. При этом внутренняя реализация этого функционала может меняться, не затрагивая другие модули. Таким образом достигается взаимодействие разработчиков - они работают над разными модулями согласовав взаимодействие между ними, например. Таким образом достигается удобство траблшутинга ошибка будет всегда внутри модуля. Так достигается раширямость - при изменении/добавлении модулей другие модули не меняются или меняются незначительно.

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

      Удалить
    7. Ну и вообще про программирование с помощью ООП есть шикарнейшкая книга: "Совершенный код" Стива Макконелла. Там не столько про само программирование, сколько про подход.

      Удалить
  5. Если коротко: стоит)

    Почему стоит?
    Если совсем просто то ООП дает два основных приемущества
    - нет дублирования кода(при грамотной архитектуре)
    - программа разбивается на классы.

    Зачем разбивать на классы?
    - Улучшается читаемость
    - Можно разрабатывать классы отдельно(разными людьми)
    - Возможность изменить реализацию класса не меняя остальную программу

    Пример
    В случае модели реакций можно создать класс Характер
    в котором будут поля отвечающие за ауру/мораль
    и методы: получить_реакцию итп
    В этом случае весь код отвечающий за поведение будет компактно собран в одном месте и при сохранении имен методов мжно полностью изменить его логику работы незаметно для остальной программы(к примеру добавить усталость от однотипных действий).

    В общем код станет во многих отношениях проще, но не сразу. Нужен некоторый навык, но он в любом случае рано или поздно понадобиться, когда программа станет слишком сложной без ООП. Лучше начинать тренироваться заранее)

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

      Удалить
    2. Ага именно структурирование. Объединение данных и кода который с ними работает в одном месте.

      Пардигма все-таки модульная. Функциональная это вообще из другой оперы.

      Про дублирование:
      У меня вообще были C и C++ в голове когда я это писал. В питоне это не так заметно.
      Вообще при желании любой класс можно переписать через словари, тем более, что сам питон в итоге сам преобразует класс к паре словарей)

      Удалить
  6. Отвечая на вопрос "Нужно ли мне заморачиваться с ООП подходом и что это даст?", то ответ "ДА". Но лучше сначала заморочиться с процедурным подходом. Для начала откажитесь от глобальных переменных и "goto" / "jump".

    Когда я начинал меня поставили в жесткие рамки стандартом кодирования ( https://www.dropbox.com/s/vhrh6d2j57ul80j/GooglePythonStyleGuide%28RUS%29.zip ). Примерно через 3 года пришло понимание почему так нужно было делать. В нашем стандарте (для C++) было 2 решающих правила: 1. НИКАКИХ глобальных объектов 2. Процедура должена помещаться на экран. Это сразу приводит к тому, что все входные и выходные параметры нужно передовать, плюс количество процедур растет быстро. Дальше чувство прекрасного (common sence) решает и ООП органично встраивается в ваш мозг.

    ОтветитьУдалить
    Ответы
    1. А что плохого в джампах? Для питоновских кусков они в целом мне пока и не были ни разу нужны, а вот RenPy на них полагается изрядно так как построен из локаций как и QSP.

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

      Удалить
    2. А что за глобальные объекты?

      Удалить
  7. Возьмем, например, 00pythonearly.rpy. Те переменные, что там объявлены вначале доступны из ЛЮБОГО места в коде. Например функция
    def interactionSucess(pattern) выглядит так, как будто она принимает на вход pattern и возвращает что-то. Но на самом деле она меняет все состояние программы. В точке вызова этого не видно. Если передать все необходимые параметры, то в точке вызова это будет выглядеть так

    action_sucess = interactionSucess(interaction_pattern, my_girl, public, shame, disgust, fear, anger, guilt, suffer, insult, pleasure, tender, attraction, gratitude, confidence ...и еще много )

    Это конечно "доставляет", но зато в точке вызова примерно видно что функция делает и что её для этого нужно. И если вся программа написана таким образом, то это делает код немного понятнее. НО глобольные переменные все портят. Они могут быть изменены из любого места в коде. Обычно это происходит внезапно (особенно если в проекте задействовано много людей).

    ОтветитьУдалить
    Ответы
    1. А вообще не парьтесь :) Пишите в своё удовольствие и не нам вас учить

      Удалить
  8. Охотник сори не знаю как твоё имя.у меня не вопрос а совет(посмотри если будет время)самая популярная книга игра конца 1990 или начало 2000 не столь важно называется Гохан или странник изгоняющий мрак игра шикарная там нет 18+но может чтото возьмёшь для себя игра эта рельно кульная
    http://quest-book.ru/forum/viewtopic.php?t=1617
    там и текст и персонажи и по мне можно чтото взять
    Костя.пс мне 24 года

    ОтветитьУдалить
    Ответы
    1. Я играл в школе в книги игры да. Но сейчас играть это в pdf-е я пожалуй не выдержу ))
      Проблема не в том что у меня нет идей что напихать в игру. Проблема как суметь реализовать всё что я придумал.

      Удалить
  9. Как резюме. ООП как правило(зависит от реализации) ведет к инкапсуляции(изоляции) части кода и данных. Собственно дальше все сводится к правильному конструированию классов и правильность имеется в виду концептуальная а не синтаксическая. Конечно все что можно описать классами, можно описать и без них.Можно писать вообще на ассемблере. Как было сказано в "Ученые шутят" Если бы строители строили так же, как программисты пишут, то первый же залетевший дятел разрушил бы мир. ООП снижает остроту этой проблемы. А вообще то я бы на вашем месте присмотрелся к RPG maker ace там в основе ruby. Но большую часть можно вообще не писать, там интерфейс WYSIWYG позволяющий решить большинство задач. И при этом можно переопределить или расширить почти все именно потому, что все написано в рамках ООП и все коды доступны. Есть русификатор и русифицированный код. Сейчас фирма делает возможность порта игр на андройд. В случае вопроса легальности она стоит в районе 20 баксов. К нему есть куча расшареного кода реализующего все виды боевок(от пошаговых, до 3D). Квестовых систем и т.д. тут ссылок не привожу но достать на трекерах 5 минут. А вообще хотел выразить свой респект) Вы умница. Очень интересная модель поведения. http://informaticslib.ru/books/item/f00/s00/z0000024/st007.shtml

    ОтветитьУдалить
    Ответы
    1. Я пробовал RPGmaker. Он для моих задач избыточен, там всё крутится вокруг модели с бродящим по карте спрайтом. Делать спрайты и карты это отдельный геморой, а вот польза для игрового процесса сомнительна. На мой взгляд один вред. Имхо именно реализация на RPGmaker-е запорола SimBrothel 2. Хотя первая часть была годная.

      Удалить
    2. Этот комментарий был удален автором.

      Удалить
  10. Знаешь,ты задал довольно интересный вопрос. Я несколько дней размышлял почему именно я пишу соблюдая ООП . Ответ получился одновременно простым и сложным. Если в двух словах : так проще.
    Сама парадигма ООП не появилась на пустом месте. Она не противостоит процедурному а расширяет его. С помощью классов, можно составлять сложные отношения между обьеектами и при этом соблюдать четкую структуру кода. Модификаторы доступа , overwriting и overloding методов позволят тебе быть уверенным что твои обекты будут вести себя так как ты этого хочешь.
    Это как сравнивать телегу с лошадью и автомобиль. Оба могут тебя доставить из пункта А в пункт Б. Но на авто это получится быстрее,комфортнее, никто тебе не будет ветрв под нос пускать и еще и песенку спеть может.
    В заключение скажу что мне очень знакомо жто чувство,когда надо переходить от чего то старого и знакомого на что то новое и не совсем понятное. Первое время будет некомфортно, зато потом ты сможешь роектировать целые куски приложения в голове.
    Кстати, я бы тебе сейчас посоветовал отложить реализацию в сторону, взять листок бумаги и ручку и написать все все идеи насчет твоего проекта. Тогда тебе яснее будет представляться функционал приложения, требуемые сущности и отношения между ними. Потом и релизация буддет проще. Плюс ты можешь поделиться планами с нами и получить тоннну советов.:)

    В любом случае,успехов!!
    ЗЫ: Пишу с телефона поэтому сорри за опечатки если что.

    ОтветитьУдалить
    Ответы
    1. У меня никогда не получалось заранее написать техзадание целиком. Очень много конкретики становится понятно только тогда когда начинаешь пытаться реализовать более общие вещи в коде.
      Всё-таки пропасть между хотелкой описаной человеческим языком и работающей программой слишком велика.

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

      Удалить
    3. Ну я естественно изначально пишу некоторую механику словами. Просто практика показывает что когда берешься за код всё сильно меняется + возникает много такого о чем заранее и не думал.

      В общем вероятно я через некоторое время создам проект на GitHub и постараюсь организовать совместную с энтузиастами работу над кодом. Но это когда настанет время реализации.

      Удалить
  11. Некоторое время назад я читал хорошую книжку по прграммированию. Там была такая фраза . Do not program in the language.Program into it. Перевод на русский несколько затруднителен,однако обшая идея такова : не меняй свои требования изза возможностей языка. Ищи новые возможности для реализации своих требований.
    ... Надо будет разработать единую конвенцию для подопытных чтобы потом мозги себе не ломали...

    ОтветитьУдалить
  12. >>"И такой подход работает вполне неплохо, даже экономит объём кода."

    Это ситуация из разряда "пот экономит кровь". Лишний абзац на инициализацию класса там-сям даёт возможность впоследствии писать очень приближённо к естественному языку, а не продираться сквозь конструкции из скобочек. Говорю как перешедший однажды в своих поделках с самопальных конструкций на ООП))
    Ну и кроме того ООП как-то... упорядоченней. Взять те же методы - молоток как-то логичнее смотрится неподалёку от коробки с гвоздями, чем в одном ящике с расчёской и столовой посудой)

    ОтветитьУдалить
    Ответы
    1. Ну кстати приближенность к естественному языку я уже оценил за время что пробую классы. Действительно читается проще.

      Удалить