#flash #actionscript-3 #coding-style
#flash #actionscript-3 #стиль кодирования
Вопрос:
Все,
Я программист-самоучка, а не выпускник CS, поэтому существуют сотни рекомендаций по кодированию, которые я, вероятно, регулярно игнорирую.
В любом случае, вот общий вопрос…
При кодировании, лучше ли использовать больше кода или больше переменных (или массивов, хэшей и т.д.) Для реализации логики?
Это расплывчатый вопрос, но вот конкретное «например …»
Я создаю пользовательский интерфейс для RIA; одно из преимуществ — это серия маленьких точек в строке — каждая точка кликабельна и работает как навигатор, позволяющий пользователям выбирать другой экран.
(Подумайте о точках в нижней части ваших домашних экранов на вашем iPhone, которые позволяют переключать экраны, или о навигаторе переключения изображений на этой странице:http://www.apple.com/ipad /)
В любом случае, я реализовал этот «элемент управления точечной навигацией» как пользовательский подкласс Sprite. Когда пользователь нажимает на точку, класс отправляет слушателю пользовательское событие, содержащее значение индекса (uint), соответствующее выбранной точке (например, «0» — это первая точка, «n-1» — это n-я точка).
Теперь в прослушивателе мне нужно предпринять действие — переместить пользователя на соответствующую страницу. Итак, один очевидный вариант:
private function dotClicked(e:customDotEvent):void {
// e.target.index contains the index of the dot clicked
switch (e.target.index) {
case 0:
// navigate the user to the screen that corresponds to dot 0
loadScreen("home");
....
break;
case 1:
// navigate the user to the screen that corresponds to dot 1
loadScreen("about");
....
break;
....
case n:
// navigate the user to the screen that corresponds to dot n
loadScreen("etc");
....
break;
}
В этом примере у меня есть довольно подробная функция переключения, которая выполняет свою работу.
Менее подробный вариант:
private function dotClicked(e:customDotEvent):void {
var screens:Array = ["home","about","blog",...,"etc"];
// e.target.index contains the index of the dot clicked
loadScreen(screens[e.target.index]);
}
Во втором варианте — у меня, очевидно, меньше кода, но для хранения массива «screens» требуется дополнительная память (хотя и только на время работы функции).
(Очевидно, что, вероятно, есть и множество лучших альтернатив, но, надеюсь, это иллюстрирует мой вопрос …).
Итак, в целом, что лучше для повышения производительности, уменьшения общего объема памяти приложения и т.д.?
В этом простом примере разница, вероятно, тривиальна. Но может ли это использоваться в большом приложении?
Является ли Flash (или другие платформы) лучшим средством обработки большего количества кода или большего количества переменных?
Стоит ли об этом беспокоиться, или я должен просто беспокоиться о написании кода, который проще поддерживать и отлаживать?
Заранее большое спасибо!
Ответ №1:
Я думаю, вам следует в первую очередь позаботиться о создании кода, который проще поддерживать и отлаживать в первую очередь. Если ваше приложение работает медленно, выполните оптимизацию. Моя стратегия заключается только в оптимизации кода, который будет часто выполняться. Прослушиватель событий для кнопки может выполняться за 0,001 секунды или 0,01 секунды, это не имеет значения. Но если сложный код выполняется в каждом кадре, то оптимизируйте его.
В actionscript скорость выполнения кода даже имеет меньшее значение, потому что рендеринг использует гораздо больше циклов процессора, чем обычно алгоритмы (если вы не пишете какое-то приложение, которое выполняет только огромные математические вычисления). Например:
for (var i:int=0;i<500;i ){
Math.sin(Math.random());
}
менее ресурсоемкий, чем
graphics.lineTo(300,300);
Итак, если вы хотите оптимизировать, начните с графики.
Комментарии:
1. Совершенно верно. Переходя от разработки на C к AS3, я заметил, что профилирование кода для меня гораздо менее полезно в среде AS3. Чаще всего мои приложения проводят > 90% времени в prerender() и render (), так что оптимизация графики является единственным предписанием (и это часто выходит за рамки моей работы).
Ответ №2:
Мне пришлось немного подумать о том, к чему вы клоните — я понимаю, к чему вы клоните, но производительность в этом случае тривиальна.
(Примечание: «Производительность тривиальна» — это одно из тех заявлений ласки … но пока просто придерживайтесь его, поверьте мне. Мы можем беспокоиться о производительности и объемах памяти после того, как разберемся с большой задачей.)
В этом случае вам, вероятно, не стоит беспокоиться о памяти или скорости вычислений. Это не только потому, что вы имеете дело с небольшими данными, но и потому, что вам нужно больше беспокоиться о возможной необходимости расширения вашего кода. Если бы у вас были сотни страниц или даже тысячи, вы действительно хотели бы добавить оператор switch case для каждой из них? Нет. Но…вы действительно хотели бы добавлять элемент, строку всех элементов, в массив для каждой новой итерации? Нет.
В этом случае я бы предпочел удобство сопровождения кода производительности или объему памяти, потому что это тривиально. (Вот оно снова.) Это тривиально, потому что есть большие потери производительности, о которых вам захочется побеспокоиться, прежде чем жертвовать удобством сопровождения кода ради решения этой проблемы. Ваш второй пример лучше первого, потому что вы не используете код повторно, и это хорошо. Однако вы могли бы пойти еще дальше; вместо определения вашего местоположения («home» и т.д.) В массиве и использования индекса в вашем классе Dot для ссылки на ключ массива, который содержит местоположение, почему бы не определить местоположение как переменную в самом классе Dot? Это отделяет ваши объекты от вашей функции с точечным кликом, открывая больше опций и упрощая обслуживание вашего кода в долгосрочной перспективе. Вы бы назвали это использованием чего-то вроде этого:
private function dotClicked(e:customDotEvent):void {
// e.target.location contains the target location of the dot clicked, e.g. "home", "about", "etc"
loadScreen(e.target.location);
}
Используя этот метод, вы не создаете новый массив каждый раз при вызове своей функции (что должно спасти сборщик мусора от запуска), и вам также не нужно выполнять итерации по инструкции switch-case (которую вам приходится редактировать каждый раз при внесении изменений), и вам не нужно беспокоиться о переводе индексов в строки и т.д. и, в процессе, вы получаете лучший код.
Это было ужасно долго и затянуто, и если вы зашли так далеко, я аплодирую вам (и также приношу извинения). В двух словах, мой ответ на вопрос «лучше ли использовать больше кода или больше переменных (или массивов, хэшей и т.д.) для реализации логики» таков — если вы можете с этим справиться, вероятно, лучше использовать меньше того и другого.
Желаю удачи.
Комментарии:
1. Lunchmeat — спасибо за этот потрясающий ответ — ОЧЕНЬ четкий и полезный.
2. Но это хороший пример того, о чем я говорю. Насколько я понимаю ваше предложение, вы добавляете «свойство» к каждой точке — переменную, которая определяет местоположение, связанное с точкой. Это делает код более элегантным. Однако приложение теперь отвечает за хранение этой переменной на протяжении всего срока службы приложения. Итак, что требует меньше накладных расходов — дополнительный код переключателя (вариант 1), работа по созданию массива каждый раз (вариант 2) или память для хранения дополнительных переменных в виде точечных свойств (вариант 3)?
3. Как вы предположили — это, вероятно, тривиально по сравнению со значительным преимуществом, заключающимся в том, что мой код становится более понятным, элегантным и поддерживаемым
4. @matt Скользкий вопрос — скользкий ответ. Каждое решение обеспечивает различный тип служебных данных — блоки switch case используют константы времени компиляции, но должны выполнять итерации по логике. Массивы имеют более высокие затраты памяти, но обращаются непосредственно к ключу. Вызовы классов лучше экономят память, но создают накладные расходы на абстракцию. Основное внимание здесь уделяется не накладным расходам на каждый метод; основное внимание уделяется устранению этапа поиска — передавая значение напрямую, вместо того чтобы искать его с помощью ключа массива, вы ускоряете свой код. Вы переходите из точки A в B, вместо того чтобы переходить от A к C к B.
5. Что касается более общего вопроса — переменные против кода — честно говоря, это, вероятно, зависит как от языка, так и от компилятора. Насколько я понимаю, многие оптимизирующие компиляторы на самом деле создают больше кода (посредством развертывания цикла и т.д.), Но они создают код более низкого уровня. Я думаю, вам придется ознакомиться с рекомендациями по производительности для конкретных языков. Если вы все еще обеспокоены, вы можете начать профилирование и модульное тестирование вашего кода, что должно дать вам более конкретные результаты.
Ответ №3:
Менее подробная версия определенно предпочтительнее, потому что ее намного проще поддерживать и управлять. Беспокоиться о производительности кнопки отчасти глупо, но беспокоиться о простоте отладки и обслуживания важно.
Хотя это не всегда так, в большинстве случаев существует довольно хорошая корреляция между количеством строк кода и объемом работы, которую вам нужно выполнить, чтобы управлять им. Глупо быть настолько кратким, что становится трудно читать код, но это действительно вызывает беспокойство, только когда вы достигаете уровня сокращения строки с 14 символов до 13.
Как бы то ни было, в вашем примере вам не нужно каждый раз создавать новый массив «screens». Поскольку этот массив всегда один и тот же, просто определите его как константу в верхней части вашего кода.
Ответ №4:
В вашем примере вы правы в том, что это не повлияет на производительность с несколькими экранами. Если бы у вас было много-много возможных вариантов расположения экранов для отображения точек, имело бы смысл присвоить им постоянные целочисленные идентификаторы вместо строк для экономии памяти и использовать ваш второй подход (массив или вектор). Я полагаю, что компилятор AS3 превращает инструкции switch в большую кучу условных выражений (каскад if, else if), так что это не идеально с точки зрения производительности и также не обеспечивает значительной экономии размера.
Чтобы ответить на ваш вопрос в более общем плане, «размер против скорости» — это классический компромисс программной инженерии. Почти для каждой проблемы с программным обеспечением существует более медленный способ выполнения задач, который требует больше вычислений, но меньше памяти, и более быстрый способ, который использует больше памяти, но меньше вычислений. Большинство компиляторов для скомпилированных языков, таких как C , имеют предустановки оптимизации либо для скорости, либо для размера. Оптимизация скорости обычно позволяет вашему приложению занимать больше места на диске (поскольку она позволяет оптимизировать такие действия, как развертывание цикла, избыточное добавление функций), а также увеличить использование памяти во время выполнения (больше кэширования и предварительных вычислений в таблицах поиска). Оптимизация по размеру приводит к меньшему размеру кода, который выполняется медленнее (например, циклы и функции не расширяются и имеют свои обычные накладные расходы) и с меньшим количеством кэширования и предварительных вычислений. Правильного ответа нет, вы принимаете решение о соотношении размера и скорости, исходя из потребностей вашего приложения и ресурсов, которые будут ему доступны.
Ответ №5:
То, как вы это делаете, прекрасно. Единственное, что я бы добавил, это введение констант. Использование констант означает, что ваш код менее подвержен ошибкам и делает его более читаемым для вас.
Например, если у вас есть Locations
класс, подобный этому:
public class Locations
{
public static const HOME:int = 0;
public static const ABOUT:int = 1;
public static const ETC:int = 2;
...
}
итак, теперь ваш switch
становится:
switch (e.target.index)
{
case Locations.HOME:
// navigate the user to the screen that corresponds to dot 0
loadScreen("home");
....
break;
case Locations.ABOUT:
// navigate the user to the screen that corresponds to dot 1
loadScreen("about");
....
break;
....
}
Гораздо, намного проще для чтения, и если вы хотите что-то изменить, вы делаете все это в одной области (Locations.as) а не по всему вашему коду