|
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
Исправление ошибки
Вначале проанализируем ошибку.
Переменная hasRotate объявлена в методе с типом Boolean, а в строке кода, которая дает ошибку, ей присваивается значение int. Далее мы смотрим как используется эта переменная. Мы не будем доверять нашей внимательности, а пройдемся поиском по коду. Ищем вхождения слова "hasRotate" начиная со строки с ошибкой. Вхождение лишь одно и оно совсем рядом с ошибочной строкой. Это еще одна удача: изменения, которые сделаем будут локальны. Смотрим, как используется переменная: с ее помощью производится проверка на наличие вхождений слова rotate. В принципе, в таких случаях есть два пути: либо создание новой переменной и дальнейшая работа уже с ней, либо изменение кода таким образом, чтобы переменная использовалась правильно. К первому решению чаще прибегают в случае, если изменение логики дальнейшей проверки нежелательно или затруднительно. Поскольку способ решения выбирается исходя из критерия минимального внесения изменений и у нас нет показаний за первое решение, поэтому прибегнем ко второму. Продублируем две строки, начиная со строки с ошибкой, одну пару закомментируем и пока оставим на память. Вторую строку изменим. Вот, что у меня получилось: // hasRotate = getAttribute(node, "transform").indexOf("rotate"); // if (hasRotate > -1) { hasRotate = getAttribute(node, "transform").indexOf("rotate") > -1; if (hasRotate) { Обратите внимание на еще один момент: хотя логика вполне прозрачна, и мы могли бы вместо > -1 использовать, например != -1 или придумать что-нибудь еще, мы оставляем использованный автором способ, поскольку остальной код пока для нас - потемки и абсолютно быть уверенным в правоте своих действий невозможно. По этой же причине мы оставляем закомментированные строки с исходным кодом. На этом процедура исправления ошибки заканчивается, и мы можем вернуться к рефакторингу.
__________________
http://realaxy.com Последний раз редактировалось iNils; 20.12.2010 в 13:20. |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
Продолжаем рефакторинг класса PathToArray
Для того, чтобы решить что делать дальше, откройте в FDT панель Problems и отсортируйте по столбцу Resource.
Здесь мы видим два типа ошибок: Local Variable is never used (локальная переменная никогда не используется) Untyped member access (нетипизированный доступ к членам) Первая ошибка встречается лишь однажды: двойным кликом по соответствующей строке в панели Problems переходим на строку с ошибкой в коде и удаляем ее. В итоге у нас останутся только ошибки нетипизированного доступа. Просмотрев код видим, что ошибки вызваны нетипизированными обращениями к переменным объектов fill, stroke, firstP, lastP, lastC. Первые два объявлены как переменные класса, остальные как локальные переменные метода makeDrawCmds. Начнем с локальных переменных. Судя по их использованию в коде, они могут быть типизированы как Point. Попробуем заменить тип на Point в их объявлении. После замены типов и сохранения документа количество ошибок резко уменьшилось и изменился их тип на "You can not assign an 'Object' to a 'Point'". Заменим, вот пример замены: Было: Стало: Чтобы ускорить этот процесс я воспользовался поиском и заменой, шаг за шагом заменяя строку "{x:" на "new Point(", внимательно следя за тем, чтобы не заменить эту строку там, где это не следовало бы делать. А именно в вызовах Math2.getQuadBez_RP(...). Затем, кликая по образовавшимся у скроллера красным меткам ошибок, я переходил на очередную строку с ошибкой и заменял закрывающую фигурную скобку на обычную и удалял "y:". В итоге, количество ошибок типизации в классе снизилось до шести. Все они - обращения к переменным объектов fill и stroke. В отличие от предыдущего шага, у этих объектов отсутствует соответствие в родных классах Flash и нам потребуется создать собственные. Сразу за самой последней закрывающей фигурной скобкой документа создадим внутренние классы. Затем зададим им публичные поля, одноименные с теми, доступ к которым осуществляется из объектов fill и stroke. Добавим в конструкторы соответствующие аргументы. Вот, что у меня получилось: internal class Fill { public var color:uint; public var alpha:Number; public function Fill(color:uint, alpha:Number) { initInstance(color, alpha); } private function initInstance(color : uint, alpha : Number) : void { this.color = color; this.alpha = alpha; } } internal class Stroke { public var color:uint; public var alpha:Number; public var width:Number; public function Stroke(color:uint, alpha:Number, width:Number) { initInstance(color, alpha, width); } private function initInstance(color : uint, alpha : Number, width : Number) : void { this.color = color; this.alpha = alpha; this.width = width; } } После этого список в панели Problems изменяется и вместо ошибок отсутствия типа мы получаем "You can not assign an 'Object' to an 'Stroke'". Здесь способ ускорения процесса правки несколько иной. Перейдя на начало документа, используем сочетание клавиш CTRL+1. FDT - умница, он хоть и не может решить всех проблем, но он предлагает добавить кастинг соответствующего типа. Соглашаемся, после чего добавляем ключевое слово new и удаляем имена полей и фигурные скобки. было: стало после кастинга: мы исправили: Проделывая те же манипуляции с объектом stroke, обязательно нужно обратить внимание на то, что последовательность, в которой расположены переменные в старом объекте stroke, не соответствует последовательности аргументов и мы должны поменять местами два последних аргумента. Закончив, выделим двойным кликом Fill проверяем, везде ли расставлено ключевое слово new. То же самое проделываем со Stroke. В результате наших действий, в классе PathToArray не должно остаться ни одной подсвечивающейся ошибки. Тестируем, убеждаемся в том, что Flash-компилятор не ругается, а изображение рисуется. Итоговый класс аттачу к этому посту.
__________________
http://realaxy.com Последний раз редактировалось iNils; 20.12.2010 в 13:21. |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
Небольшое отступление
Возможно, читатель думает, что я портировал проект, отрефакторил и оптимизировал и теперь, зная все подводные камни, веду их по спланированному сюжету.
Это не так. Я не знаю, к чему мы придем. Я вначале делаю небольшой шаг, затем описываю что сделал. Всё по-честному. - Что же мне дает уверенность в том, что в итоге всё получится хорошо? - Ну, помимо природной наглости, я понимаю, что так или иначе удастся справиться с возникающими вопросами, а также я придерживаюсь простых правил, которые мне помогут:
__________________
http://realaxy.com Последний раз редактировалось Iv; 12.03.2008 в 18:09. |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
Приводим в порядок Math2
При поверхностной оценке здесь, в отличие от предыдущих классов, может возникнуть необходимость изменения типа данных аргументов.
Следовательно, этот шаг затронет не только текущий класс, но и другие, его использующие. Этот шаг мы оставим на самый последний момент. Сейчас мы нацелены на снижение количества подсвечиваемых ошибок и первое, что стоит сделать - заняться локальными переменными. Идем сверху вниз. ratioTo - пропускаем intersect2Lines - задаем всем аргументам тип Object - задаем всем локальным переменным тип Number rotation - задаем всем аргументам тип Number midPt - задаем аргументам тип Number - указываем возвращаемое значение Object getQuadBez_RP - здесь не указаны типы аргументов, для того, чтобы выяснить поиском по файлам (CTRL+H, File Search) ищем вызовы этого метода и, констатируя факт, указываем использованные типы: public static function getQuadBez_RP(p1:Object, c1:Object, c2:Object, p2:Object, k:Number, qcurves:Array) - задаем тип Number переменным dx и dy - остальным переменным задаем тип Object bezierSplit - задаем всем аргументам тип Number - задаем переменной m тип Function pointOnCurve - задаем всем аргументам тип Number - видим, что можем легко избавиться от подсвечивания ошибки нетипизированного доступа и заменяем процесс создания объекта на такой: public static function pointOnCurve(p1x:Number, p1y:Number, cx:Number, cy:Number, p2x:Number, p2y:Number, t:Number):Object { return {x:p1x + t*(2*(1-t)*(cx-p1x) + t*(p2x - p1x)), y:p1y + t*(2*(1-t)*(cy-p1y) + t*(p2y - p1y))}; } - задаем всем аргументам тип Number pointsOnLine - задаем всем аргументам тип Number - задаем переменной i тип int - задаем всем остальным переменным тип Number curveApproxLen - задаем всем аргументам тип Number; Первая часть на этом заканчивается, причем на мажорной ноте: если вы обратите внимание на панель Problems, то увидите, что число ошибок стало меньше 100 и это означает, что окончание процесса не за горами.
__________________
http://realaxy.com Последний раз редактировалось iNils; 20.12.2010 в 13:21. |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
удаляем Object
Следующим нашим шагом борьбы с нетипизированным доступом станет повсеместное удаление типа Object.
Изучив код, увидим, что очень часто Object используется там, где бы мог использоваться Point. Порядок замены в таких случаях:
__________________
http://realaxy.com |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
Взглянув на код, определяем какие методы нам проще всего изменить. Внешним признаком будет служить нам то, что в качестве аргументов и возвращаемого значения не применяется тип Object.
Единственный подходящий метод - curveApproxLen. Добавим в методе создание новой точки, назовем ее middle: и далее заменим обращения к mp на middle. Тут же видим: вызываемый метод pointOnCurve подходит для следующего шага, поскольку у него только возвращаемое значение имеет тип Object. Заменяем объект и возвращаемый тип на Point: public static function pointOnCurve(p1x:Number, p1y:Number, cx:Number, cy:Number, p2x:Number, p2y:Number, t:Number):Point { return new Point(p1x + t*(2*(1-t)*(cx-p1x) + t*(p2x - p1x)), p1y + t*(2*(1-t)*(cy-p1y) + t*(p2y - p1y))); } Для того, чтобы найти места в коде, где используется метод pointOnCurve применим хитрость: переименуем pointOnCurve в pointOnCurve1, сохраним классс и по списку ошибок увидим где это. Результат - два вызова в этом же классе. Проще всего исправить curveApproxLen. Мы просто ставим вызов метода pointOnCurve вместо new Point(...) и удаляем строку объявления объекта mp. Также можем удалить "Math2." в вызове. В итоге должны получить вот такой метод: public static function curveApproxLen(p1x:Number, p1y:Number, cx:Number, cy:Number, p2x:Number, p2y:Number):Number { var middle:Point = pointOnCurve(p1x, p1y, cx, cy, p2x, p2y, 0.5); var len1:Number = Math.sqrt((middle.x - p1x) * (middle.x - p1x) + (middle.y - p1y) * (middle.y - p1y)); var len2:Number = Math.sqrt((middle.x - p2x) * (middle.x - p2x) + (middle.y - p2y) * (middle.y - p2y)); return len1+len2; } Теперь можно разобраться со вторым методом, использующим pointOnCurve, это метод pointsOnCurve. Сейчас он возвращает массив точек и... драма! Неприятная неожиданность: мы обнаруживаем, что это не могут быть объекты класса Point, поскольку в этом методе задается точке загадочная переменная r. Ну, что-ж, это не критично. Cоздадим подкласс объекта Point. Пока мы не знаем как его называть, поскольку сущность переменной r нам неизвестна. Мы только видим, что это угол, но угол чего именно на этом этапе мы выяснять не имеем права. Второй вопрос - нам следует выяснить будут ли использованы эти точки вне класса Math2, чтобы определиться делать ли новый класс публичным или достаточно будет internal. Для этого переименуем pointsOnCurve в pointsOnCurve1, сохраним документ и пройдемся по полученым ошибкам - местам вызова этого метода. К моему удивлению - ни одной ошибки. Включаем занудство и используя поиск по файлам ищем вхождение строки "pointsOnCurve". Опять ничего кроме его самого. Делаем вывод: мы нашли неиспользуемый метод. Тревога отменяется, закомментируем этот метод полностью и идем дальше. Среди оставшихся четырех методов содержащих ошибки только bezierSplit в качестве параметров не требует Object, поэтому заглянем в него. Этот метод использует другой метод из этого же класса: midPt. Переходим на него и видим, что возвращаемый тип можно заменить на Point, что и делаем. По прежней схеме с переименованием проверим, а не используется ли он где-то еще. Но в этот раз заодно зададим имя без сокращений: midpoint. А поскольку снаружи текущего класса этот метод нигде не используется, то сделаем его приватным. В итоге, он должен выглядеть так: private static function midpoint(p1x : Number, p1y: Number, p2x: Number, p2y: Number):Point { return new Point((p1x + p2x)/2, (p1y + p2y)/2); } Ошибок нет, можно продолжить доработку метода bezierSplit. Заменим вызовы метода midpoint через локальную переменную m на обычный вызов и удалим строку объявления этой переменной. Это требуется для того, чтобы убедиться в правильности передаваемых аргументов. Далее, всем объектам, которым присваивается возвращаемое методом midpoint значение зададим тип Point. Но стоит ли на этом останавливаться? Если мы оставим тип Object переменным p1 и p2, то в возвращаемом объекте будет каша. Да, мы не знаем и пока не хотим знать логику приложения, но здравый смысл подсказывает, что p1 и p2 должны иметь такой-же как и у остальных тип - Point. Прислушаемся к голосу разума, исправим, и получим вот такой метод: public static function bezierSplit(p1x:Number, p1y:Number, c1x:Number, c1y:Number, c2x:Number, c2y:Number, p2x:Number, p2y:Number):Object { var p1:Point = new Point(p1x, p1y); var p2:Point = new Point(p2x, p2y); var p01:Point = midpoint (p1x, p1y, c1x, c1y); var p12:Point = midpoint (c1x, c1y, c2x, c2y); var p23:Point = midpoint (c2x, c2y, p2x, p2y); var p02:Point = midpoint (p01.x, p01.y, p12.x, p12.y); var p13:Point = midpoint (p12.x, p12.y, p23.x, p23.y); var p03:Point = midpoint (p02.x, p02.y, p13.x, p13.y); return { b0:{p1:p1, c1:p01, c2:p02, p2:p03}, b1:{p1:p03, c1:p13, c2:p23, p2:p2} }; } Ошибок нет, идем дальше. Я взялся за ratioTo, но переименовав его обнаружил, что он также нигде не используется. Проверил поиском - подтвердилось. Закомментируем и идем дальше.
__________________
http://realaxy.com Последний раз редактировалось iNils; 20.12.2010 в 13:22. |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
Выбирая из оставшихся двух переименованием, обнаруживаем, что метод intersect2Lines используется только в этом классе и лишь в одном месте. Делаем метод приватным.
Наиболее предсказуемые последствия возникнут если сейчас изменить возвращаемый тип. Логика подсказывает, что пересечение двух линий - точка. Заменяем возвращаемый тип на Point. Редактор тут же подсвечивает ошибки: возврат NaN и Object. В первом случае заменяем на null, во втором случае на new Point(...) - в трех местах. После этого двойным кликом выделяем имя метода и используя CTRL+R открываем панель результатов поиска и там переходим на метод getgetQuadBez_RP и заменяем тип переменной s на Point. (Можно заодно удалить Math2. в вызове метода intersect2Lines) Далее приведем в порядок аргументы метода intersect2Lines, задав им тип Point. После этого в панели Problems отсортируем список по полю Description так, чтобы вверху оказались строки "You can not assign an 'Object' to an 'Point'" и приступим к исправлению этих ошибок. Здесь нужно четко понимать: наша задача в данный момент - исправить только эти ошибки, а не весь проект. Наши исправления должны носить по возможности локальный характер. Поэтому поступим следующим образом: - переименуем первый аргумент в point1; - перед вызовом intersect2Lines объявляем переменную p1 с типом Point и присваиваем ей новый объект Point, которому в качестве параметров передаем point1.x и point1.y; - поступаем аналогично со следующим аргументом, задав ему имя control1. - аналогично изменяем оставшиеся два аргумента, имеющие тип Object. - сохраняем проект, тестируем. Вот что имеем на данный момент: public static function getQuadBez_RP(point1:Object, control1:Object, control2:Object, point2:Object, k:Number, qcurves:Array):void { // find intersection between bezier arms var p1:Point = new Point(point1.x, point1.y); var c1:Point = new Point(control1.x, control1.y); var c2:Point = new Point(control2.x, control2.y); var p2:Point = new Point(point2.x, point2.y); var s:Point = intersect2Lines (p1, c1, c2, p2); // find distance between the midpoints var dx:Number = (p1.x + p2.x + s.x * 4 - (c1.x + c2.x) * 3) * .125; var dy:Number = (p1.y + p2.y + s.y * 4 - (c1.y + c2.y) * 3) * .125; // split curve if the quadratic isn't close enough if (dx*dx + dy*dy > k) { var halves:Object = Math2.bezierSplit (p1.x, p1.y, c1.x, c1.y, c2.x, c2.y, p2.x, p2.y); var b0:Object = halves.b0; var b1:Object = halves.b1; // recursive call to subdivide curve getQuadBez_RP (p1, b0.c1, b0.c2, b0.p2, k, qcurves); getQuadBez_RP (b1.p1, b1.c1, b1.c2, p2, k, qcurves); } else { // end recursion by saving points qcurves.push({p1x:p1.x, p1y:p1.y, cx:s.x, cy:s.y, p2x:p2.x, p2y:p2.y}); } } Мы имеем возможность быстро протестировать изменения и, если придется откатиться, то совсем недалеко. Это огромное преимущество маленьких шагов. Итак, в настоящий момент все ошибки типизации сконцентрировались в одном методе: getQuadBez_RP и разбиты на две группы: ошибки вызванные неверной типизацией аргументов и ошибки вызванные объектом halves. Следуя нашей стратегии вначале исправим ошибки, затрагивающие только методы текущего класса. Объект halves - это возвращаемый методом bezierSplit объект, содержащий в себе два других объекта b0 и b1, каждый из которых содержит в себе по 4 объекта типа Point. У нас есть выбор: либо создать пользовательский класс для типизации объекта halves, либо, поскольку его структура жестко задана, использовать массив и последующую типизацию. В данном случае массив предпочтительнее, поскольку это более короткий и логичный путь. Название метода (bezierSplit - разделить безье) и использованное имя переменной, которой присваивается возвращаемое значение (halves - половинки), также говорят за использование массива. Ок, пойдем этим путем. Проверим вначале на какие другие методы окажут воздействие изменения в bezierSplit. Сделать это можно либо переименовав его, либо с помощью CTRL+R. Метод используется только в getQuadBez_RP. Это заодно нам позволяет сделать его приватным. Далее: - изменяем возвращаемый методом bezierSplit тип на Array; - изменяем строку return на: - в методе getQuadBez_RP изменяем тип объекта halves на Array; - изменяем присвоение переменным b0 и b1 на доступ к 0 и 1 элементам массива соответственно; - тестируем. Аналогичным образом поступаем с объектами в возвращаемом методом bezierSplit массиве - также делаем из них массивы: - в методе getQuadBez_RP изменяем тип переменных b0 и b1 на Array; - чтобы увидеть все вхождения b0 и b1, а также сделать код более читабельным, переименовываем эти переменные в bezier0 и bezier1 соответственно. - дублируем строки, в которых появились ошибки, одну пару комментируем, чтобы не забыть что было изначально; - в тех местах где подсветились ошибки заменяем имена переменных и доступ к их содержимому на оператор доступа к массиву. Замена производится соответственно: ".p1" на "[0]", ".c1" на "[1]", ".c2" на "[2]", ".p2" на "[3]". Наример, было: b0.c1 стало: bezier0[1]; Результат должен получиться таким: Тестируем. Всё в порядке? Тогда на этом первую половину процесса можно считать законченной.
__________________
http://realaxy.com Последний раз редактировалось iNils; 20.12.2010 в 13:23. |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
вторая часть рефакторинга getQuadBez_RP
Эту часть процесса мы будем делать исходя из очень простой логики действий: вначале вызываем ошибку, затем исправляем.
Изменяем первый тип аргумента метода getQuadBez_RP на тип Point. Сохраняем документ. В панели Properties появляется список ошибок вида: "You can not assign an 'Object' to an 'Point'" Двойным кликом переходим на первую ошибку и заменяем первый аргумент в вызове метода Math2.getQuadBez_RP с объекта на new Point(...). Сохраняем документ, чтобы обновилась панель Problems. Переходим к следующей ошибке этого типа. Исправляем таким образом все ошибки данного типа. Обязательно тестируем. Результатом этого процесса должно стать полное отсутствие ошибок в панели Problems. Это и было целью первого этапа рефакторинга. Мы ее добились и на этом этап можно считать законченным.
__________________
http://realaxy.com Последний раз редактировалось Iv; 13.03.2008 в 19:25. |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
В процессе вы познакомились с различными приемами и принципами решения проблем.
Я и дальше постараюсь применять разные подходы к исправлению схожих задач, чтобы максимально охватить их спектр, даже, если в каком-то конкретном случае решение будет не самым оптимальным. В дальнейшем вы сами владея этими инстурументами сможете выбирать подходящий. Подведем некоторые итоги. Мы добивались того, чтобы в FDT не показывались ошибки. Но мы не прятали их, а именно исправляли. Результатом этого стало то, что кроме коллекции цветов в проекте не осталось объектов типа Object, а это - один из критериев оценки. - Но устраивает ли нас код в текущем состоянии? - Ни в коем случае. Этот код нам непонятен, его повторное использование практически невозможно, внесение изменений в код крайне затруднительно. Собственно именно поэтому мы и говорим об окончании первой волны рефакторинга, но это - только начало.
__________________
http://realaxy.com Последний раз редактировалось Iv; 13.03.2008 в 16:48. |
|
|||||
Регистрация: Apr 2001
Адрес: Moscow
Сообщений: 1,475
|
Удаление ненужных методов
Перед началом следующего этапа почистим код: нужно отыскать и удалить неиспользуемые методы.
Раньше этого не стоило делать: мы пока не можем быть уверены в том, что в других частях проекта эти методы не использованы (имеется ввиду вторая часть - анимация отрисовки svg файла), а возвращаться в последствии к этапу приведения типов не хочется. Удалив лишнее нам проще будет сконцентрироваться на оставшихся методах и сэкономит время. При этом, разумеется, у нас должна остаться копия проекта в текущем состоянии для того, чтобы при необходимости мы могли восстановить любой удаленный метод. Неиспользуемые методы я рекомендую именно удалить, а не просто закомментировать, поскольку отстутствие лишнего кода, в том числе и закомментированного, ускорит процесс нашей работы. Мы уже удаляли методы, так что я не буду концентрироваться на самой процедуре, отмечу лишь тот момент, что удаление одного метода может повлечь за собой прекращение необходимости в другом. Также напоминаю, что удаление метода в незнакомом проекте требует обязательного последующего тестирования. В итоге ненужные методы оказались только в классе Math2 и там, после удаления осталось их всего 4: getQuadBez_RP - публичный, я его переместил в начало класса; intersect2Lines - приватный; midpoint - приватный; bezierSplit - приватный; Чтобы "сверить наши часы" я выкладываю файлы проекта в текущем состоянии.
__________________
http://realaxy.com |
Часовой пояс GMT +4, время: 07:10. |
|
« Предыдущая тема | Следующая тема » |
|
|