Рефакторирање во Eclipse

45
Универзитет „Св. Кирил и Методиј“ Факултет за информатички науки и компјутерско инженерство Рефакторирање во Eclipse Вградени опции во Eclipse IDE за рефакторирање на Java код Изработил: Даница Ѓорѓевска 129108 Ментор: Ивица Димитровски Скопје Март, 2014

Transcript of Рефакторирање во Eclipse

Универзитет „Св. Кирил и Методиј“ Факултет за информатички науки и

компјутерско инженерство

Рефакторирање во

Eclipse Вградени опции во Eclipse IDE за

рефакторирање на Java код

Изработил:

Даница Ѓорѓевска

129108

Ментор:

Ивица Димитровски

Скопје

Март, 2014

1

Содржина Вовед ........................................................................................................................................................ 2

Eclipse IDE ............................................................................................................................................. 2

Што е рефакторирање? ...................................................................................................................... 2

Поддршка за рефакторирање во Eclipse ............................................................................................... 4

Имплементација на процесот на рефакторирање во Eclipse .............................................................. 5

Eclipse Refactoring API.......................................................................................................................... 5

JDT Refactoring Implementation ........................................................................................................ 10

Имплементациски стил ..................................................................................................................... 12

Exeption Handling ............................................................................................................................... 13

Претставување на рефакторирањето на корисникот ........................................................................ 15

Рефакторирачки акции ..................................................................................................................... 15

Волшебници за рефакторирање ...................................................................................................... 21

Примери ............................................................................................................................................. 24

Грешки при Extract Method .............................................................................................................. 42

Иднина ................................................................................................................................................... 43

Референци ............................................................................................................................................. 44

2

Вовед

Eclipse IDE Eclpise е интегрирана развојна околина (Integrated Development Environment – IDE) која

иницијално била проект на IBM Канада. Во Ноември 2001 бил формиран конзорциум за

понатамошно развивање на Eclipse, кој кулминирал со создадвање на Eclipse фондацијата

во јануари 2004. Во јуни 2004 било пуштено првото официјално издание – Eclipse 3.0, a

почнувајќи од 2006та година фондацијата има координирани симултани годишни

изданија. Последната верзија е Eclipse Kepler 4.3.2 oд 28.02.2014.

Eclipse се состои од основен работен простор и екстензибилни додатоци (plug-ins) за

прилагодување на околината. Eclipse во најголем дел е напишана во Java и примарно се

користи за развивање на Java апликации, но додатоците поддржуваат и развивој со некои

други програмски јазици како што се Ada, C, C++. Fortran, Haskell, JavaScript, Perl, PHP,

Python, Ruby и тн.

Eclipse Software Development Kit (SDK) кој вклучува Java развојни алатки е наменет за

Java програмерите. Корисниците можат да ги прошират неговите способности преку

инсталирање на додатоци напишани за Eclipse платформата и исто така можат да

придонесат со нивни сопствени модули за додатоци.

Eclipse има богата клиентска платформа (Rich Client Platform RCP) за развивање на

сенаменски апликации за генерални цели. Компонентата од RCP која ги содржи

погледите, волшебниците, едиторите, перспективите и слично (честопати користени во

преостанатиот дел од текстот) се нарекува Eclipse Workbench.

Eclipse поддржува развој на апликации за Tomcat, GlassFish и многу други сервери и

честопати е возможно потребниот сервер да се инсталира директно преку IDE. Дозволува

оддалечено дебагирање, и му овозможува на корисникот да ги набљудува варијаблите и да

го разгледува кодот на апликација која работи на оддалечениот сервер.

Eclipse Web Tools Platform (WTP) проектот е екстензија на Eclipse плаформата со алатки за

развивање на веб и Java EE апликации.

Modeling проектот – платформата за моделирање ги содржи сите официјални проекти на

Eclipse фондацијата кои се фокусираат на модел-базирани развојни технологии. Сите тие

се компатибилни со Eclipse Modeling Framework креирана од IBM. Овие проети се

поделени во неколку подкатегории.

Eclipse е објавена под условите на Eclipse Public License, Eclipse SDK е слободен и

софтвер со отворен код (open source) иако е некомпактибилен со GNU General Public

License.

Што е рефакторирање? Постојат повеќе дефиниции за објаснување на терминот „Рефакторирање“. Наједноставно

е да се каже дека рефакторирање е процесот на изменување на софтверскиот систем на

3

начин што не влијае на неговото надворешно однесување, но ја подобрува неговата

внатрешна структура. При процесот на рефакторирање се зголемуваат нефункционалните

карактеристики на системот како што се едноставноста, флексибилноста, разбирливоста и

слично, но притоа системот не го променува своето однесување.

Има две генерални категории на бенефит од активноста рефакторирање:

1. Одржливост – полесно е да се поправаат грешки во кодот бидејќи истиот е полесен

за читање и разбирање

2. Екстензибилност – полесно е да се прошират способностите на апликацијата

доколку истата користи препознатливи дизајн шаблони и обезбедува одредена

флексибилност која можеби не постоела претходно

Процесот на рефакторирање всушност претставува збир од повеќе микро техники од кои

секоја претставува мала промена во изворниот код. Некои од нив се следните:

Техники што овозможуваат поголема апстракција

o Енкапсулирање на поле – го принудува кодот да го пристапи полето преку

гет и сет методи

o Генерализирање на тип – креирање на погенерални типови за да се

овозможи делење на повеќе код

o Замена на тип

o Замена на услов со полиморфизам

Техники за разделување на кодот на пологични делови

o Компонетизација го разделува кодот на реупотребливи семантички единици

што претставуваат јасен, добро дефиниран интерфејс лесен за употреба

o Екстрахирање на класа преместува дел од кодот од постоечка во нова класа

o Екстрахирање на метод за преместување на дел од голем метод во нов

Техники за подобрување на имиња и локации на кодот

o Преместување на метод или поле - преместување во посоодветна класа или

изворен фајл

o Преименување на метод или поле со име кое подобро ја открива неговата

цел

o Pull up – преместување во суперкласа

o Pull down – преместување во подкласа

Eclipse, како и голем број на други развојни околини овозможува автоматизирана

поддршка за извршување на механичките аспекти на рефакторирањето.

Истите тие се достапни преку посебно мени од лентата со менија. Исто така тие се

достапни од контексните менија на неколку Java погледи (пример Package Explorer,

Outline) и едитори. Многу навидум едноставни наредби, како што се Move (Премести) и

Rename (Преименувај) се всушност операции на рефакторирање, бидејќи преместувањето

и преименувањето на Java елементи честопати побарува промени во зависните фајлови.

4

Поддршка за рефакторирање во Eclipse Целта на рефакторирање на Java програми е да се направат промени во кодот во рамките

на целиот систем без притоа да се влијае на однесувањето на програмата. Java алатките

овозможуваат помош во лесно рефакторирање на кодот.

Кога се извршува операција за рефакторирање, може опционално да се прегледаат

промените кои резултираат од рефакторирачка акција пред да се одбере истите да се

извршат. Кога се прегледува операција на рефакторирање, ќе бидете известени за

потенцијалните проблеми и ќе Ви биде презентирана листа на промени кои ќе бидаат

извршени со акцијата. Доколку не ја прегледате операцијата на рефакторирање промената

ќе биде целосно извршена и ќе бидат прикажани резултантните проблеми. Доколку е

детектиран проблем кој не му дозволува на рефакторирањето да продолжи, операцијата ќе

биде прекината и ќе биде прикажана листа со проблеми.

5

Во текстот што следува ќе бидат разгледани сите алатки за рефакторирање кои ги нуди

Eclipse IDE. Истите тие се достапни од контексните менија на неколку Java погледи

(пример Package Explorer, Outline) и едитори. Многу навидум едноставни наредби, како

што се Move (Премести) и Rename (Реименувај) се всушност операции на рефакторирање,

бидејќи преместувањето и преименувањето на Java елементи честопати побарува промени

во зависните фајлови.

Рефакторирањето не може да се извршува само интерактивно, туку исто така и со

рефакторирачки скрипти. Повеќето рефакторирања кои се достапни во Refactor менито се

зачуваат во историјата на рефакторирање на работната површина со цел истите да бидат

понатаму искористени во рефакторирачки скрипти. Алатките за рефакторирање го

поддржуваат креирањето на скрипти за рефакторирање базирано на историјата на

рефакторирање на работниот простор. Истите понатака можат да се користат во

произволни работни простори. Користење на рефакторирачки скрипти стартува

рефакторирачки волшебник кој е способен да ги реизврши рефакторирањата на начин

како што биле иницирани од корисникот кој иницијално ги креирал.

Поврзано со скриптите за рефакторирање, алатките за рефакторирање нудат

рефакторирањето да мигрира JAR фајл кон нова верзија, користејќи информација од

рефакторирање со цел да се избегнат фатални промени во работниот простор после

миграцијата.

Имплементација на процесот на рефакторирање во Eclipse

Eclipse Refactoring API Eclipse Refactoring API е дел од Language Toolkit (LTK) и е имплементиран во

org.eclipse.ltk.core.refactoring и org.eclipse.ltk.ui.refactoring додатоците почнувајќи од R3.0.

Порано додатокот обезбедувал класи кои го апстрахираат процесот на рефакторирање и

дефинираат интерфејси на високо ниво користени од објектите кои учествуваат.

Подоцнежната верзија дефинира голема низа од UI елементи – волшебници, кои се

заеднички за повеќето рефакторирања и кои даваат конзистентен изглед и чувство. Додека

UI елементите се важни за собирање на повеќето влезови и му овозможуваат на

корисникот преглед и повратна врска, API компонентите пак се од голем интерес бидејќи

токму тие ја овозможуваат манипулацијата со кодот.

Рефакторирачки модел

API-то за рефакторирање овоможува апстракција на ниво на процес врз кој можат да

бидат изградени специфични рефакторирања. Сликата која следува ги прикажува

елементите на оваа апстракција на многу високо ниво. Овде, стрелките помеѓу елементите

прикажуваат зависности. На пример, Refactoring Implementer побарува знаење од (на

пример референца до) некој Select Element, со цел да разбере кон што ќе биде аплицирано

6

рефакторирањето. Слично, Refactoring Dialog и Implementer мора да разменат

информација за да ги прикажат соодветните UI елементи – волшебници.

Од кога е иницирано рефакторирањето, имплементер од тоа рефакторирање се користи за

да ги искоординира проверката на условности, собирањето на детали за рефакторирањето

и на крај продуцирањето на серија на промени кои можат да бидат применети на

работниот простор во Eclipse со цел да се изврши рефакторирање. Овој имплементатор

мора да ја прошири апстрактната класа org.eclipse.ltk.core.refactoring.Refactoring.

Животниот циклус на процесот рефакторирање е прикажан на следната слика:

Процесот на рефакторирање започнува преку иницијализација на рефакторирачка

инстанца. Ова вклучува пренесување на контекстуална информација, како што е кои

елементи од работниот простор се селектирани и понекогаш дури бихевориална

информација, како што е специјализиран процесор за дадениот контекст. Тогаш

рефакторирањето мора да провери дали дадениот контекст е соодветен за типот на

рефакторирањето. Оваа проверка е дефинирана во презапишаната имплементација на

7

Refactoring instance моетодот - checkInitialConditions. Може да се случи да не биде

достапна целата потребна информација за да започне извршувањето. Повеќе информации

можат да бидат побарани од корисникот и да бидат собрани преку различни UI

компоненти – волшебници. Овој процес продолжува се дури не бидат собрани сите

потребни податоци. Во тој момент, рефакторирањето станува одговорно за осигурувањето

дека неговото последователно извршување ќе произведе код кој е семантички

еквивалентен. Ова се случува при checkFinalConditions, каде што рефакторирањето може

да итерира преку најразлични репрезентации на елементите кои ќе бидат рефакторирани,

вклучувајќи AST репрезентации на кодот. Целиот процес на проверка на почетните и

крајните услови е наведен како предусловно проверување.

Класите кои произлегуваат од рефакторирањето се финално одговорни за продуцирање на

инстанца од org.eclipse.ltk.core.refactoring.Change која го опишува целиот ефект од

рефакторирањето. Промените можат да влијаат на било кој елемент од работниот простор

кој е потребен за да се изврши рефакторирањето а исто така самите тие можат да бидат

составени од голем број на помали промени.

Рефакторирањето може да користи било кој метод кој е соодветен за продуцирање на

соодветните промени во работниот простор. Сепак, доколку е потребно, може исто така да

искористи дополнителни процесни апстракции овозможени од страна на LTK со цел да се

потпомогне создавањето на овие промени. Процесор / учесник модел е обезбеден за да

помогне да се координира рефакторирачки процесор и нула или повеќе рефакторирачки

учесници. Рефакторирањата кои ја користат оваа апстракција мора да бидат изведени од

org.eclipse.ltk.core.refactoring.participants.ProcessorBasedRefactoring. Процесор / учесник

моделот побарува од рефакторирањето да вчита RefactoringProcessor и било кои

RefactoringParticipants кои се соодветни за моменталниот контекст. Точка на проширување

може да биде обезбедена овде така што дополнителните процесори или учесници можат

да бидат динамички регистрирани и да учествуваат во рефакторирањето.

Секој учесник може да имплементира делови од иницијалното или финалното

проверување на условите и/или генерирањето на промена, додека пак самиот

RefactoringProcessor е одговорен за агрегирање на придонесите на учесниците.

Опишување на промените во работниот простор

На крај рефакторирањето продуцира единствена апстрактна класа Change која ги опишува

промените во работниот простор кои се потребни со цел да се постигне рефакторирањето.

Имплементаторите на рефакторирањето мора да ги реискористат или да имплементираат

8

нивни сопствени класи изведени од Change со цел да го специфицираат однесувањето на

промената. Промените мора да поддржуваат модифицирање и на зачувани и на незачувани

ресурси.

На оваа слика е претставена хиерархија од апстрактни промени. Наследувањето и

композицијата на ваквата промена на типови им овозможува на програмерите

инкрементало да дефинираат комплексни промени, поедноставувајќи го кодот на било кое

дадено делче. Единствена промена, типично композитна промена, е продуцирана преку

инвоцирање на Refactoring instance методот createChange.

Кога се применува промена во работниот простор, извршувањето најчесто е раководено

од класата PerformChangeOperation. Извршувањето на промена продуцира нови инстанци

од Change кои можат да се искористат за да се отстрани (врати) операцијата.

Перформанси

Перформансите на рефакторирањето можат да бидат важно прашање кога се земе во

предвид прашањето на рефакторирање на големи работни простори. Типично

рефакторирање може да има потреба да вчита и да манипулира со AST репрезентација на

секое тело на код кое треба да се модифицира. API документацијата предлага

рефакторирањето да треба само да се справува само со една инстанца од такви големи

објекти во меморијата во било кој момент, со цел да се намали комплексноста на

меморијата.

За продорни рефакторирања на код, изгледа дека дури поголема меморија може да биде

потребна за претставувањето на финалната хиерархија на промени. Секоја Change

инстанца може сама по себе да биде мала, но рефакторирањата најчесто се состојат од

многу мали промени, можеби долу на нивото на модификации на текстуални ресурси.

Понатака, бидејќи е потребно елементите на кодот да бидат енумерирани уште во текот на

предусловната проверка, во пракса поголемиот дел или целата таа хиерархија на промени

е генерирана рано во животниот циклус на рефакторирање и истата опстојува. Дури и

9

доколку ваква голема хиерархија на промени може да се смести во меморијата, еднаш од

кога е применета, комплементарна хиерархија од приближно иста големина мора да биде

генерирана со цел да го поддржи моделот за враќање на промените во Eclipse и истата

постои за времетраењето на целата историја на враќање (Undo).

Историја на имплементација

Пред Eclipse R3.0, рефакторирачкото API не постоело внатре во LTK и наместо тоа било

JDT специфична имплементација почнувајќи од R2.0. За да се обезбеди API за

имплементирање на рефакторирање кај другите јазици, сет од интерфејси и апстрактни

класи биле користени внатре во JDT рефакторирачки пакети и истите тие биле на некој

начин проширени и преместени во LTK. Иако неколку имиња на методи биле изменети во

текот на оваа преселба, основната архитектура за имплементирање на рефакторирањето

останала конзистентна почнувајќи од R2.0. Еден исклучок од ова е рефакторирачкиот

процесор / учесник модел. Оваа процесна апстракција била претставена така што јазично

неутрални рефакторирања кои користат јазични или контексно специфични процесори

можат да бидат имплементирани. Класите присутни во LTK и соодветните класи кои

постоеле во R2.0 JDT UI додатокот се прикажани во табелата:

Други надворешни ефекти, како што е транзицијата од Java 2 кон Java 5 со инклузија на

генерики, немале ефект врз дизајнот. Иако девелоперите изгледаат свесни за генериките

(бидејќи тие вклучуваат линиски коментари за повеќето колекции кои го опишуваат

нивниот тип во генеричка нотација) , тие не ги користат истите. Претставувањето на

10

генериките сепак, довело до имплементација на Infer-Type-Arguments рефакторирањето.

Исто така изгледа дека други напредни карактеристики на Java 5 јазикот како што се

енумерирани типови не се користат моментално дури и при главните ревизии на

рефакторирачкото API.

Една веројатна причина за креирање на рефакторирачкото LTK било тоа што на тој начин

Eclipse IDE може да се справи со рефакторирањето за сите јазици преку заеднички

супертипови. Ова, секако, работи добро на начин што заедничките методи за инвокација

(кои исто така се содржат во LTK) можат да се искористат за било кое рефакторирање.

Ова е многу поефективно и робустно од обидите да се инвоцира рефакторирање базирано

на некои, под претпоставка, заеднички потписи на методите. Наместо тоа, преку

пристапот земен од страна на LTK, компајлерот ќе верифицира дека рефакторирањата го

имплементираат заедничкиот Refactoring class interface и дека се’ што е потребно во време

на извршување е успешен кастинг на тип.

JDT Refactoring Implementation Додатокот org.eclipse.jdt.ui содржи неколку имплементации на рефакторирањето во Java

разделени во над десет пакети.

11

Се’ уште не е разјаснето зошто токму org.eclipse.jdt.ui ги содржи имплементациите за

рефакторирање. Eclipse API-то за рефакторирање е доста јасно разделено во рамките на

LTK во „јадро“ (“core”) и „кориснички интерфејс“ (“ui”) пакети, а од друга страна JDT

имплементацијата ги сместува обете компоненти во JDT UI додатокот. Пристап кој би бил

12

конзистентен со LTK би бил да се креираат два нови JDT додатоци (plug-in):

org.eclipse.jdt.core.refactoring и org.eclipse.jdt.ui.refactoring.

Сепак постојат потенцијални причини зошто бил искористен само org.eclipse.jdt.ui

додатокот. Прво, би можело да претставува потешкотија да се изнајде решение за сите

оние додатоци на кои се потпира имплементацијата за рефакторирање бидејќи постои

висок степен на референтна зависност. Притоа, имплементацијата за рефакторирање исто

така содржи компоненти за кориснички интерфејси, за кои на JDT јадрениот додаток би

му недостасувале потребните UI зависности. JDT UI додатокот, со додаток на LTK

додатоци би поседувал доволен сет на зависности за да поддржува рефакторирање. Друга

причина може да биде тоа што преместувањето на имплементацијата на рефакторирањето

во два посебни додатоци може да предизвика циклични зависности помеѓу нив меѓусебно

или со останатите JDT додатоци. Повторно, поради степенот на зависност, можеби би

било наједноставно да се избегнат овие проблеми наеднаш, и да се вклучи

имплементацијата за рефакторирање само во еден JDT UI додаток.

Кодот кој е наменет за имплементација на рефакторирањето е исто така значајно поголем

од останатите делови на LTK. Бидејќи LTK е јазик и е рефакторирачки неутрален, многу

од неговите класи имаат апстрактни методи кои мораат да бидат имплементирани од

имплементациите на рефакторирање за да дефинираат специфични однесувања.

Убедливото мнозинство од кодот во JDT ги имплементира рефакторирачките методи за

checkFinalConditions и createChange (или директно или преку RefactoringProcessor наменет

за специфични типови на елементи) и исто така за правењето на подтип од Change класата

за да дефинира нови однесувања на промена.

Имплементациски стил Иако JDT рефакторирањата се многу ефективни гледани од перспектива на корисникот,

нивната кодна база е тешка за дешифрирање.Основното API за рефакторирање во Eclipse е

доста едноставно и добро документирано преку Javadocs. Сепак, долги недокументирани и

некоментирани рутини постојат во изобилство во JDT специфичната имплементација.

Доколку не би постоела документацијата за LTK класите кои JDK рефакторирањата ги

прошируваат, најверојатно ќе беше невозможно воопшто да се разбере кодот.

Како пример може да се земе рефакторирачката класа SelfEncapsulateFieldRefactoring.

Оваа класа дефинира рефакторирање каде сите read или write референцираат до поле кое

може да биде проксирано преку соодветен акцесор или мутатор метод. Повеќето од

рефакторирачките пресметки се извршуваат за време на checkFinalConditions, метод

дефиниран од LTK за осигурување дека сите предуслови за рефакторирањето (семантички

или други) се задоволени пред истото да биде применето. Овде, checkFinalConditions е 70

линии долго без ниту еден единствен линиски, блок или Javadoc коментар. Тоа ги

изоставува проверките на предусловите, генерирањето на хиерархија на промени и

манипулација со прогрес барот бидејќи референцира десетици недокументирани и

13

неискоментирани методи - помошници. Единствената

помош која би ја имал програмер кој работи на

одржувањето на функционалностите овозможени од

секоја од овие методи би биле нивното име и типовите

на аргументите. За да се добие осет колку ова би можело

да биде тешко, листа од методи за

SelfEncapsulateFieldRefactoring е прикажана во табелата.

Една интересна опсервација на рефакторирањата

овозможени за JDT е тоа што неколку од нив го користат

процесор / учесник моделот обезбеден од LTK. Ова може

да објасни зошто овие класи беа изоставени од оние кои

постоеа во R2.0 како што е прикажано во табелата.

Онаму каде што се користи процесор / учесник моделот,

специјализиран процесор е вчитан за да ги изврши

рефакторирањата базирани на типот на Java елементот

кој е рефакториран. Сепак, нема логика во тоа

рефакторирањето само да одредува кој процесор мора да

се користи. На пример, JavaMoveRefactoring класата и

нејзиниот супертип MoveRefactoring се ослонуваат на

MoveProcessor параметарот на конструкторот за да го дефинира целиот процес на

рефакторирање за одреден тип на Java елемент. Соодветниот MoveProcessor е селектиран

и инстанциран од помошната класа RefactoringExecutionStarter, која пак е референцирана

од страна на неколкуJDT рефакторирачки UI пакети. Други рефакторирања се

имплементирани на слични начини, вклучувајќи ги различните рефакторирања за

преименување. На овој начин, делови од основната логика на рефакторирањето се

релоцирани надвор од нивните основни рефакторирачки класи и однесени поблизу до UI

компонентите.

Exeption Handling Со оглед на тоа што рефакторирањето може да се примени на големи кодни системи,

постојат многу можности нешто да тргне наопаку, односно да се појави исклучителна

состојба (exceptional state). Сепак, LTK не дефинира некои исклучоци (exceptions)

специфични за рефакторирањето, ниту пак некои конвенции за справување со

неочекувани услови. Бидејќи секое рефакторирање е одговорно за проверката на неговите

предуслови (кое може да врати статус код за фатален испад), рефакторирачкото API

претпоставува дека успешна проверка на предусловите е доволна за успешна

рефакторирачка апликација.

Единствениот исклучок фрлен од било кое од рефакторирачките LTK и JDT класи за

рефакторирање е org.eclipse.core.runtime.CoreException. Јадрените исклучоци можат да

содржат информации кои ја опишуваат природата на грешката. Сепак, ниту еден од

14

справувачите со грешки внатре во JDT не ја референцира оваа информација во нивните

справувачки рутини со цел да направи пресметки за исклучителната состојба. Наместо

тоа, внатре во JDT имплементацијата, справувањето со грешки се стреми или да ја логира

грешката и да и дозволи на рутината тивко да падне или да генерира нов исклучок кој ја

содржи статусната информација на оригиналот. Исклучоци кои можат да се појават можат

да го остават рефакторирањето во неконзистентна состојба која понатака би генерирала

уште повеќе исклучоци. На пример, многу исклучоци оставаат полиња сетирани на null и

не е возможно точно да се продолжи со понатамошно процесирање. Тогаш овие

исклучоци ќе пропагираат низ повикувачкиот стек, можеби до методи внатре во LTK, или

полошо, до методи внатре во UI модулите каде што несоодветно справување би било

тешко. Земете ја во предвид SelfEncapsulateFieldRefactoring класата. Таа го референцира

проверениот исклучок JavaModelException седум пати:

checkArgName го фаќа, игнорирај и продолжи;

createChange го фаќа, логирај го и продолжи;

конструкторот, createFieldAccess, createModifiers и initialize(lField) методите го

фрлаат (неисправено)

initialize(RefactoringArguments) го фаќа и враќа генерички статус за испад

Истите класи исто така го референцираат CoreException седум пати. Во сите од овие

случаи тоа едноставно го игнорира CoreException-от кој би можел да биде генериран и го

препраќа повикувачкиот стек. Овој пример е репрезентативен за Java рефакторирањата и

демонстрира недостаток од соодветно справување со грешки. Не постои напор да се

истражи причината за исклучокот блиску до неговото генерирање и последователно да се

справи со истиот. Можеби може да се земе во предвид за случајот дека нема акција која

може да се превземе за исклучокот кој може да биде генериран. Сепак, во повеќето случаи

би било корисно бар да се шпекулира (преку коментари или можеби преку информативни

статус кодови) зошто се појавил исклучокот за дадениот контекст на рефакторирањето и

зошто не може Eclipse да се справи со истиот.

Друг проблем би можел да се појави кога исклучоците не се генерирани или справени

локално. Рефакторирањата се одговорни за проверка на предусловите, вклучувајќи

верификација на точноста и зачувување на семантиката на кодот, пред примена на

рефакторирањето. Сепак, доколку се земе во предвид моделот на животниот циклус на

класите изведени од Refactoring, изгледа дека може да се појават привремени проблеми.

На пример, после комплетирањето на проверката на предусловите, но пред да се примени

рефакторирањето, промените на афектираните ресурси можат да предизвикат

рефакторирањето да падне или полошо, да биде комплетно неточно. Вакви сценарија

можат да вклучат случаи кога ресурс е модифициран надвор од Eclipse и станува заклучен

од страна на другата апликација, или пристап до ресурсот е изгубен поради минливи

хардверски услови (како што е мрежната конективност). Друга можносе е дека

рефакторирањето се ослонува на код кој едноставно има грешки. Во било кој од овие

15

случаи, соодветните исклучоци можат да пропагираат низ границите на модулите и

компонентите, и во тој случај е изгубена секоја надеж за грациозно справување со истите.

Претставување на рефакторирањето на корисникот Во текстот што следува ќе бидат разгледани сите алатки за рефакторирање кои ги нуди

Eclipse IDE. Истите тие се достапни од контексните менија на неколку Java погледи

(пример Package Explorer, Outline) и едитори. Многу навидум едноставни наредби, како

што се Move (Премести) и Rename (Реименувај) се всушност операции на рефакторирање,

бидејќи преместувањето и преименувањето на Java елементи честопати побарува промени

во зависните фајлови.

Рефакторирачки акции Следуваат наредби кои се содржат во менито за рефакторирање на Eclipse заедно со

нивниот опис

Име Опис

Rename Ги преименува одбраните елементи и (ако е дозволено) ги поправа сите

референци до елементите (исто така и во други фајлови)

Достапно: Методи, параметри на методи, полиња, локални

варијабли, типови, параметри на типови, enum константи,

компилациски единици, пакети, изворни фолдери, проекти

и на текст селекции кои разрешуваат некои од

горенаведените типови на елементи

Shortcut: Alt + Shift + R

Опции: Преименување на тип дозволува да се преименуваат

слично именувани варијабли и методи. Притоа треба да се

дозволи 'Update similarly named variables and methods' во

Rename Type дијалогот. Одберете ‘Configure...’ со цел да

ја конфигурирате стратегијата за поврзување на имиња на

типови.

Преименување на пакет дозволува да се преименуваат

неговите подпакети. Дозволете ‘Rename subpackages’ во

Rename Package дијалогот.

Овозможете ‘Keep original method as delegate to changed

method’ со цел да го задржите оригиналниот метод.

Опционално може да deprecate стариот метод.

16

Move Ги преместува одбраните елементи и (доколк у е дозволено) ги поправа

сите референци до елементите (исто така во други фајлови)

Достапно: Инстанцирани методи (кои можат да се преместат до

компонента), еден или повеќе статички методи, статички

полиња, типови, компилациски единици, пакето, изворни

фолдери и проекти и на текст селекции кои разрешуваат

еден од овие типови на елементи

Shortcut: Alt + Shift + V

Опции: Може да користите Drag & Drop во Package Explorer-от за

да го започнете ова рефакторирање.

Change

Method

Signature

Ги менува имињата на параметрите, типовите на параметрите,

редоследот на параметрите и ги надградува сите референци до

соодветниот метод. Дополнително, параметрите и фрлените исклучоци

можат да бидат отстранети или додадени и типот кој го враќа методот и

неговата видливост можат да бидат променети.

Достапно: Methods or on text selection resolving to a method

Shortcut: Alt + Shift + C

Опции: Дозволете 'Keep original method as delegate to changed

method' во Change Method Signature со цел да го задржите

оригиналниот метод.

Extract Method Креира нов метод кој ги содржи изразите (statements, expressions)

моментално селектирани и ја заменува селекцијата со референца до

новиот метод. Оваа функционалност е корисна за расчистување на

долги и прекомплицирани методи.

Достапно: Може да користите Expand Selection to од Edit менито за

да добиета валиден ранг на секцијата.

Ова рефакторирање исто така е достапно како quick assist

на изрази селектирани во едиторот.

Shortcut: Alt + Shift + M

Extract Local

Variable

Креира нова варијабла доделена на изразот кој е моментално

селектиран и ја заменува селекцијата со референца до новата варијабла.

17

Достапно: Текст селекции што се разрешуваат до локални варијабли.

Може да користите Expand Selection to од Edit менито со

цел да добиете валиден селекциски ранг. Ова

рефакторирање исто така е достапно како quick assist на

изрази селектирани во едиторот.

Shortcut: Alt + Shift + L

Extract

Constant

Креира статично финално поле од селектираниот израз и ги заменува

референците на полето, и опционално ги презапишува другите места на

кои се појавува тој израз.

Достапно: Константни изрази или текст селекции кои разрешуваат

константни изрази.

Ова рефакторирање исто така е достапно како quick assist

на изрази селектирани во едиторот.

Inline Вметнува локални варијабли, методи или константи.

Достапно: Методи, статички финални полиња и текст селекции кои

се разрешуваат до методи, статички финални полиња или

локални варијабли. Ова рефакторирање исто така е

достапно како quick assist за локални варијабли

селектирани во едиторот.

Shortcut: Alt + Shift + I

Convert

Anonymous

Class to Nested

Конвертира анонимна внатрешна класа до member класа

Converts an anonymous inner class to a member class.

Достапно: Анонимни внатрешни класи

Move Type to

New File

Креира нова Java компилациска единица за селектираните member

типови или селектираните втори типови, обновувајќи ги сите

референци онака како што е потребно. За нестатички member типови,

поле е додадено со цел да се овозможи пристап до претходните

затварачки инстанци, доколку тоа е потребно.

Creates a new Java compilation unit for the selected member type or the

selected secondary type, updating all references as needed. For non-static

member types, a field is added to allow access to the former enclosing

instance, if necessary.

Достапно: Member types, secondary types, или text resolving to a

member type or a secondary type.

Convert Local

Variable to

Field

Претвора локална варијабла во поле. Доколку варијаблата е

иницијализирана при креирањето, тогаш операцијата ја преместува

иницијализацијата во декларацијата на новото поле или пак во

конструкторот на класата.

18

Достпно: Текст селекции кои се разрешуваат до локални варијабли.

Ова рефакторирање исто така е достапно како quick assist

на локални варијабли селектирани во едиторот.

Extract

Superclass

Екстрахира заедничка суперкласа од сет на сродни типови.

Селектираните сродни типови постануваат директни подкласи од

екстрахираната суперкласа после извршување на рефакторирањето.

Достапно: Типови

Опции: Овозможете 'Use the extracted class where possible' со цел

да ги користите новокреираните класи сегде кај што е

возможно. Види

Use Supertype Where Possible.

Extract

Interface

Креира нов интерфејс со сет од методи и ги принудува селектираните

класи да го имплементираат тој интерфејс

Достапно: Типови

Опции: Овозможете 'Use the extracted interface type where possible'

со цел да го користите новокреираниот интерфејс сегде

каде што тоа е возможно. Види Use Supertype Where

Possible.

Use Supertype

Where Possible

Ги заменува појавувањата на тип со еден од неговите супертипови

после идентификувањето на сите места каде што оваа замена е

возможна.

Достапно: Типови

Push Down Преместува сет од методи и полиња од класа во нејзината подкласа.

Достапно: Еден или повеќе методи и полиња декларирани во истиот

тип или на текст селекција внатре во метод или поле.

Pull Up Преместува поле или метод во суперкласата на неговаата декларирачка

класа или (во случај на метод) го декларира методот како апстрактен во

суперкласата.

Достапно: Еден или повеќе методи, полиња и member типови

декласирани во истиот тип или во текст селекција внатре

во поле, метод или member тип.

Extract Class Заменува сет од полиња со нов контејнер објект. Сите референци до

полињата се обновени за да достапуваат до новиот контејнер објект.

Достапно: Сет од полиња или тип кој содржи полиња

19

Опции: Овозможете 'Create Getter and Setters' за да додадете

акцесор методи на новиот тип

Introduce

Parameter

Object

Заменува сет од параметри со нова класа и ги обновува сите повикувачи

на методот за да препратат/предадат инстанца од новата класа како

вредност на introduce параметарот.

Достапно: Методи или тест селекции кои се разрешуваат до метод

Опции: Овозможете 'Keep original method as delegate to changed

method' во Introduce Parameter Object дијалогот за да го

зачувате оригиналниот метод.

Introduce

Indirection

Креира статички околен (indirection) метод кој делегира до одбраниот

метод

Creates a static indirection method delegating to the selected method.

Достапно: Методи или тест селекции кои се разрешуваат до метод

Опции: Овозможете 'Redirect all method invocations' со цел да се

заменат сите повици до оригиналниот метод со повици до

околниот метод.

Introduce

Factory

Креира нов фабрички метод, кој ќе повика одреден конструктор и ќе го

врати креираниот објект. Сите референци до конструкторот ќе бидат

заменети со повици до новиот фабрички метод.

Достапно: Декларации на конструктори

Introduce

Parameter

Заменува израз со референца до нов параметар на метод, и ги обновува

сите повикувачи на методот да го пренесат изразот како вредност на тој

параметар.

Достапно: Текст селекции кои се разрешуваат до изрази

Encapsulate

Field

Ги заменува сите референци до поле со getter и setter методи.

Достапно: Поле или текст селекција која се разрешува до поле

Ова рефакторирање е исто така достапно како quick assist

на декларација на полиња и референци селектирани во

едиторот.

Generalize

Declared Type

Му дозволува на корисникот да одбере супертип на моменталниот тип

на референцата. Ако референцата може безбедно да се промени во

новиот тип, тогаш се менува.

Достапно: Референци на типови и декларации на полиња, локални

варијабли и параметри со референцни типови.

20

Infer Generic

Type

Arguments

Заменува појавување на сирови (raw) типови на генеричките типови

преку параметризирани типови притоа идентификувајќи ги сите места

каде што замената е возможна.

Достапно: Проекти, пакети и типови

Options: 'Assume clone() returns an instance of the receiver type'.

Класи кои се однесуваат исправно генерално го

почитуваат ова правило, но доколку знаете дека Вашиот

код го нарушува, одштиклирајте го полето.

'Leave unconstrained type arguments raw (rather than inferring

<?>)'. Доколку нема ограничувања на елементите од ,на

пример ArrayList, одштиклирање на ова поле ќе

предизвика Eclipse се’ уште да обезбедува wildcard

параметар, заменувајќи ја референцата со ArrayList<?>.

Migrate JAR

File

Мигрира JAR фајл на build патеката на проект во Вашиот работен

простор кон понова верзија, веројатно со користење на информација за

рефакторирање зачувана во новиот JAR фајл со цел да се избегнат

штетни промени.

Достапно: JAR Files на build патеката

Create Script Креира скрипта од рефакторирањата кои биле применети во работниот

простор. Рефакторирачките скрипти можат или да бидат зачувани во

фајл или копирани на клипбордот. Види Apply Script.

Достапно: Секогаш

Apply Script Применува рефакторирачка скрипта врз проект во Вашиот работен

простор. Рефакторирачката скрипта може да биде вчитана од фајл или

од клипбордот. Види Create Script.

Достапно: Секогаш

History Ја пребарува рефакторирачката историја на работниот простор и ја нуди

опцијата да се избришат рефакторирања од рефакторирачката историја.

Достапно: Секогаш

Наредбите за рефакторирање исто така се достапни од контексните менија на многу

погледи и преку Java едиторот.

21

Волшебници за рефакторирање Дијалог базирани кориснички интерфејси ве водат преку потребните чекори за

извршување на одбраните рефакторирања. Во зависност од комплексноста на

рефакторирањето, се користи волшебник или едноставен дијалог прозорец за прибирање

на сите информации кои се потребни за рефакторирањето.

Input pages

Страниците за внес собираат информации кои се потребни за рефакторирањето. Од кога

ги обезбедите сите потребни влезови може да кликнете OK или Finish со цел да

продолжите со рефакторирањето без пред тоа да ги прегледате резултатите. Доколку пак

сакате да ги прегледате, кликнете на Preview или Next.

22

Preview page

JDT Ви овозможува да го прегледате резултатот од рефакторирачката акција пред да ја

извршите истата.

Страницата за преглед се состои од два дела:

Дрво кое на врвот ги содржи сите Java елементи кои се засегнати од

рефакторирањето. Секој јазол на највисокото ниво на дрвото репрезентира една

компилациска единица.

Некои рефакторирања овозможуваат да се филтрира дрвото преку различни видови

на промени направени со рефакторирањето. Користете го Filter Changes паѓачката

листа за да го промените филтрирањето.

Поглед за споредба на дното на екранот. Левата страна од погледот за споредба го

прикажува оригиналот, а десната страна го прикажува рефакторираниот код

23

Problem page

Страницата за проблеми при рефакторирањето индицира доколку постојат сомнителни,

потенцијални или определени проблеми со акцијата за рефакторирање која се обидувате

да ја извршите. Постојат четири можни проблеми:

Информација

Проблем опишан како Информација нема да влијае на рефакторирањето на било

кој начин, ниту пак негативно ќе му влијае на кодот во workbench-от. Најчесто

може да ги игнорирате повеќето вакви проблеми.

Предупредување

Предупредувањата – Warnings – се обидуваат да предвидат компајлирачки

предупредувања. Овој тип на проблем најчесто нема да влијае негативно на кодот

во Вашиот workbench.

Грешки

Проблем опишан како Грешка – Error – е многу веројатно да предизвика

компајлирачка грешка или семантички да го промени Вашиот workbench код.

Може да одберете да продолжите со рефакторирањето и покрај тие грешки, иако

тоа не е препорачливо.

Стоп проблеми

Овој тип на проблем го спречува извршувањето на рефакторирањето. На пример,

доколку одберете коментар и ја изберете Extract Method командата од него,

24

workbench-от ќе предизвика стоп проблем на обидот за рефакторирање бидејќи

коментар не може да се екстрахира.

Доколку не постојат некои стоп проблеми, тогаш рефакторирањето може да се изведе

преку притискање на Finish копчето. За да го прегледате резултатот од рефакторирањето,

притиснете го Finish копчето.

Рефакторирање без дијалог

Исто така е возможно да се преименува Java елемент без притоа да се прикаже дијалог.

Ова може да биде овозможено и неовозможено во Java reference page. Доколку е

овозможено, тогаш новото име на Java елементот може да биде впишан во едиторот кога

рефакторирањето за преименување е инвоцирано.

Примери

Преименување на локална варијабла

За да го активирате Rename рефакторирањето може да идете со десен клик врз варијаблата која

сакате да ја преименувате и да го одберете Rename Method од Edit менито, или пак истото да го

одберете од мени лентата додека сте позиционирани врз варијаблата.

Преименувањето на локална варијабла (Rename) може да се изврши со или без користење на

волшебник за рефакторирање.

25

Доколку користите волшебник, можете со притискање на Preview копчето да ја видите промената

пред истата да биде извршена. Доколку пак имате доверба во Eclipse потребно е само да

притиснете на OK копчето. При едноставни рефакторирања како што се Rename и Move Method

прегледување на рефакторирањето пред неговото извршување најчесто не е потребно.

Преместување на класа од еден пакет во друг

Move работи многу слично како Rename. Го одбирате Java елементот, најчесто класа, ја

специфицирате неговата нова локација и исто така бирате дали да бидат променети и

референците. Повторно можете да изберете дали сакате промената веднаш да биде извршена

или сакате да ја прегледате претходно. Кај некои платформи, особено Windows, ова

рефакторирање кај класи може да биде извршено со едноставен drag and drop во Package Explorer

погледот, при што сите референци ќе бидат автоматски променети.

Промовирање на анонимни и вгенздени класи

Две рефакторирања, Convert Anonymous Class to Nested и Convert Nested Type to Top Level

се слични во тоа што обете преместуваат класа надвор од моменталниот кон приближниот

опсег.

Анонимна класа е вид на синтаксичка кратенка која дозволува да биде инстанцирана класа

која имплементира апстрактна класа или интерфејс без притоа да треба експлицитно да

биде наведено името на класата. Ова често се користи кога се креирани ослушнувачи

(listeners) во корисничките интерфејси. Во следниот пример претпоставете дека Bag е

интерфејс дефиниран на некое друго место кој декларира два методи, get() и set().

26

public class BagExample { void processMessage(String msg) { Bag bag = new Bag() { Object o; public Object get() { return o; } public void set(Object o) { this.o = o; } }; bag.set(msg); MessagePipe pipe = new MessagePipe(); pipe.send(bag); } }

Кога анонимна класа станува голема и кодот станува тежок за читање, треба да ја земете

во предвид можноста анонимната класа да биде претворена во вистинска класа; За да се

зачува енкапсулацијата (со други зборови, да се сокрие од надворешните класи кои не

треба да знаат за неа) треба оваа класа да биде вгнездена класа. Ова може да биде

направено со кликање внатре во анонимната класа и бирање на Refactor > Convert

Anonymous Class to Nested. Внесете име на класата, на пример BagImpl и одберете

Preview или OK по што ќе го добиете следниот резултат:

27

public class BagExample { private final class BagImpl implements Bag { Object o; public Object get() { return o; } public void set(Object o) { this.o = o; } } void processMessage(String msg) { Bag bag = new BagImpl(); bag.set(msg); MessagePipe pipe = new MessagePipe(); pipe.send(bag); } }

Конвертирањето на вгнезден тип во Top Level е корисно тогаш кога сакате вгнездената

класа да ја направите достапна за други класи. На пример, може да користите некој објект

кој подоцна ќе имате потреба да биде споделен помеѓу класите. Ова рефакторирање ќе

креира нова класа од вгнездената класа. Ова се прави на начин што се селектира името на

28

класата во изворниот фајл (или со кликање на името на класата во Outilne погледот) и

селектирање на Refactor > Convert Nested Type to Top Level.

Ова рефакторирање ќе побара од Вас да внесете име за приложената инстанца. Може да ви

даде и сугестија, која можете да ја прифатите.

По притискање на OK, кодот за приложената BagExample класа би изгледал вака: public class BagExample { void processMessage(String msg) { Bag bag = new BagImpl(this); bag.set(msg); MessagePipe pipe = new MessagePipe(); pipe.send(bag); } }

Приметете дека кога класа е вгнездена, има пристап до надворешните членови на класата.

За да се зажува оваа функционалност, рефакторирањето ќе додаде инстанца од класата

BagExample во претходно вгнездената класа. Ова е инстанцираната варијабла за која

претходно Ви беше побарано да внесете име. Исто така креира конструктор кој ја сетира

оваа инстанцирана варијабла. Новата BagImpl класа која е креирана со рефакторирањето

изгледа вака:

final class BagImpl implements Bag { private final BagExample example; /** * @paramBagExample */ BagImpl(BagExample example) { this.example = example; // TODO Auto-generated constructor stub } Object o; public Object get() { return o; } public void set(Object o) { this.o = o; } }

29

Доколку немате потреба да го зачувате пристапот до BagExample класата, како што е во

овој случај, можете безбедно да ја отстраните инстанцираната варијабла и конструкторот,

и да го смените кодот во BagExample со стандардниот конструктор без аргументи.

Преместување на членови во класната хиерархија

Две други рефакторирања, Push Down и Pull Up, ги преместуваат методите во класите или

полињата од една класа во нејзината подкласа или надкласа соодветно. Претпоставете

дека имате апстрактна класа Vehicle дефинирана на следниот начин:

public abstract class Vehicle { protected int passengers; protected String motor; public int getPassengers() { return passengers; } public void setPassengers(int i) { passengers = i; } public String getMotor() { return motor; } public void setMotor(String string) { motor = string; } }

Исто така имате подкласа од Vehicle наречена Automobile

public class Automobile extends Vehicle { private String make; private String model; public String getMake() { return make; } public String getModel() { return model; } public void setMake(String string) { make = string; }

30

public void setModel(String string) { model = string; } }

Забележете дека еден од атрибутите на Vehicle е motor. Ова е во ред доколку знаете дека

ќе се справувате само со моторни возила, но доколку сакате да овозможите велосипеди на

пример, би сакале да го преместите motor атрибутот од Vehicle во Automobile класата. За

да го направите ова, одберете го motor во Outline погледот и изберете Refactor > Push

Down.

Eclipse е доволно паметен за да сфати дека не можете секогаш да преместите поле само по

себе и го овозможува копчето Add Required, за додавање на методи кои зависат од ова

поле во процесот на рефакторирање, како што се во овој случај get и set методите.

По притискање на OK, motor полето и getMotor() и setMotor() методите ќе бидат

преместени во Automobile класата која по рефакторирањето изгледа вака:

public class Automobile extends Vehicle { private String make; private String model; protected String motor; public String getMake() { return make; }

31

public String getModel() { return model; } public void setMake(String string) { make = string; } public void setModel(String string) { model = string; } public String getMotor() { return motor; } public void setMotor(String string) { motor = string; } }

Pull UP рефакторирањето е речиси идентично со Push down, освен , се разбира, тоа што ги

преместува членовите на класите во суперкласата наместо подкласата. На пример, може

да го искористите ова во случај подоцна да се премислите и да го вратите motor во Vehicle

класата. Се прикажува истото предупредување за да се осигурате дека сите потребни

членови се селектирани.

Extract Interface

Кога motor се наоѓа во Automobile класата, тоа значи дека доколку креирате други

подкласи, на пример Bus, треба да го додадете motor и неговите методи во Bus класата.

Еден начин на репрезентирање на врски од ваков тип е да се креира интерфејс, Motorized,

кој ќе го имплементираат Automobile и Bus, а Bike или RowBoat нема да го

имплементираат.

Најлесен начин да се креира Motorized интерфејсот е со користење на Extract Interface

рефакторирањето на Automobile класата. За да го направите тоа, селектирајте ја

Automobile класата во Outline погледот и одберете Refactor > Extract Interface од менито.

Дијалогот ќе Ви овозможи да изберете кои методи сакате да бидат вклучени во

интерфејсот на начин како што е прикажан на следната слика:

32

По кликање на OK, креиран е интерфејсот:

public interface Motorized { public abstract String getMotor(); public abstract void setMotor(String string); }

А класната декларација на Automobile изгледа вака:

public class Automobile extends Vehicle implements Motorized

Користење на супертип

Да разгледаме апликација која управува со инвентар од автомобили. Притоа користи

објекти од типот Automobile. Доколку сакате да бидете во можност да работите со сте

типови на возила, можете да го користите овој тип на рефакторирање со цел да ја смените

референцата до Automobile со референца до Vehicle. Доколку извршите било каква

проверка на тип (type-checking) во Вашиот код со користење на instanceof операторот, ќе

треба да одлучите дали е соодветно да се користи специфичниот тип или супертипот и да

ја маркирате првата опција, Use the selected supertype in ‘instanceof’ expressions, соодветно.

33

Потребата за користење на супертип се јавува често во Java програмскиот јазик, особено

кога се користи Фабрички Метод шаблонот. Типично ова е имплементирано со користење

на апстрактна класа која има статички create() метод кој враќа конкретен објект кој ја

имплементира апстрактната класа. Ова може да биде корисно доколку типот на

конкретниот објект кој мора да биде креиран зависи од имплементациски детали кои не се

во интерес на клиентските класи.

Екстрахирање и вметнување

Постојат неколку рефакторирања кои започнуваат со зборот Extract: Extract Method,

Extract Local Variable и Extract Constant. Првиот, како што може да очекувате, ќе креира

нов метод од кодот кој сте го селектирале. На пример земете го main() методот во

примерот што следува. Тој ги евалуира опциите од командната линија и доколку најде

некоја која започнува со –D, ги зачувува како име-вредност парови во Properties објектот.

import java.util.Properties; import java.util.StringTokenizer; public class StartApp { public static void main(String[] args) { Properties props = new Properties(); for (int i= 0; i < args.length; i++) { if(args[i].startsWith("-D")) { String s = args[i].substring(2); StringTokenizer st = new StringTokenizer(s, "="); if(st.countTokens() == 2) { props.setProperty(st.nextToken(), st.nextToken()); } } }

34

//continue... } }

Има два најважни случаи кога би можеле да сакате да извадите некој код од одреден

метод и да го сместите во друг. Првиот случај е кога методот е премногу долг и извршува

две или повеќе логички различни операции. (Не знаеме што друго извршува овој main()

метод, но од тоа што можеме да го видиме овде, тоа не е причина за екстрахирање на

методот.)

Вториот случај е кога постои логички различна секција од код која може да биде

реупотребена од страна на други методи. Понекогаш, на пример, може да се затекнете

себеси како повторувате неколку линии код во неколку различни методи. Ова е можно во

овој случај, но најверојатно вакво рефакторирање нема да го извршите се додека

навистина не се јави потреба да го реупотребите овој код.

Под претпоставка дека постои друго место каде што треба да парсирате име-вредност

парови и да ги додадете на Properties објект, може да ја извадите оваа секција код која ја

вклучува StringTokenizer декларацијата и следната if клауза. За да го направите тоа,

селектирајте го овој код и одберете Refactor > Extract Method од менито. Од Вас ќе биде

побарано да внесете име на методот; внесете addProperty, и тогаш верифицрајте дека

методот има два параметри, Properties prop и Strings. Подолу е прикажана класата од како

Eclipse го екстрахирала методот addProp().

import java.util.Properties; import java.util.StringTokenizer;

35

public class Extract { public static void main(String[] args) { Properties props = new Properties(); for (int i = 0; i < args.length; i++) { if (args[i].startsWith("-D")) { String s = args[i].substring(2); addProp(props, s); } } } private static void addProp(Properties props, String s) { StringTokenizer st = new StringTokenizer(s, "="); if (st.countTokens() == 2) { props.setProperty(st.nextToken(), st.nextToken()); } } }

Extract Local Variable рефакторирањето зема израз кој се користи директно и првично му

доделува локална варијабла. Понатака оваа варијабла се користи на места каде што

претходно бил изразот. На пример, во addProp() методот можете да го означите првиот

повик до st.nextToken() и да одберете Refactor > Extract Local Variable. Од Вас ќе биде

побарано да ја обезбедите варијаблата, внесете key. Забележете дека постои опција да се

заменат сите појавувања на селектираниот израз со референца до новата варијабла. Ова

честопати е соодветно, но не во случајот на nextToken() методот, кој (очигледно) враќа

различна вредност при секое негово повикување.

36

Наредно, повторете го ова рефакторирање за вториот повик до st.nextToken(), овој пат

повикувајќи ја новата локална варијабла value. Кодот после овие две рефакторирања

изгледа вака:

private static void addProp(Properties props, String s) { StringTokenizer st = new StringTokenizer(s, "="); if(st.countTokens() == 2) { String key = st.nextToken(); String value = st.nextToken(); props.setProperty(key, value); }

}

Претставување на варијабли на овој начин обезбедува неколку предности. Прво, при

обезбедување на имиња со значење за изразите, го прави експлицитно тоа што го прави

кодот. Второ, го олеснува дебагирањето на кодот бидејќи лесно можеме да ги провериме

вредностите кои ги враќаат изразите. На крај, во случаи кога повеќе инстанци од израз

можат да бидат заменети со единствена варијабла, ова може да биде поефикасно.

Extract Constant е слично со Extract Local Variable, но мора да одберете статичен,

константен израз кој рефакторирањето ќе го конвертира во статичка финална константа.

Ова е корисно за отстранување на хард-кодирани броеви и стрингови од Вашиот код. На

пример, во горниот код беше користено –D” за опција од командната линија која

дефинира име-вредност пар. Обележете –D” во кодот, одберете Refactor > Extract

Constant и внесете DEFINE како име за константата. Ова рефакторирање ќе го смени

кодот на следниот начин:

37

public class Extract { private static final String DEFINE = "-D"; public static void main(String[] args) { Properties props = new Properties(); for (int i = 0; i < args.length; i++) { if (args[i].startsWith(DEFINE)) { String s = args[i].substring(2); addProp(props, s); } } }

// ..

За секое Extract… рефакторирање постои соодветно Inline... рефакторирање кое ја

извршува обратната операција. На пример, ако ја обележите варијаблата s во горниот код

и одберете Refactor > Inline… и притиснете ОК, Eclipse ќе го искористи изразот

args[i].substring(2) директно во повикот до addProp(): if (args[i].startsWith(DEFINE)) { addProp(props, args[i].substring(2));

}

Ова може да биде маргинално поефикасно со користење на привремена варијабла и преку

правење на кодот поконцизен го прави или полесен за читање или покриптиран, зависно

од Вашиот поглед. Генерално, вметнување како ова не е многу препорачано. На ист начин

може да се замени варијабла со вметнат израз, може да обележите име на метод или

статичка финална константа. Одберете Refactor > Inline… од менито и Eclipse ќе ги

замени повиците на методите со кодот за методот или референци до константата со

нејзината вредност, соодветно.

Енкапсулирање на полиња

Генерално, не се смета за добра практика да се разоткрива внатрешната структура на

Вашиот објект. Поради тоа класата Vehicle и нејзините подкласи имаат private или

protected полиња и public setter и getter методи за да обезбедат пристап. Овие методи

можат да бидат генерирани автоматски на два начини. Еден начин е со користење на

Source > Generate Getter and Setter. Ова ќе прикаже дијалог кутија со предложените

гетери и сетери за секое поле кое не ги поседува истите. Сепак, ова не е рефакторирање

бидејќи не ги изменува референците до полињата да ги користат новите методи, тоа би

требало да го направите рачно доколку е потребно. Оваа опција заштедува на време, но

најдобро е да се користи при иницијално креирање на класата, бидејќи тогаш се’ уште

38

нема да има друг код кој би ги референцирал тие полиња и тоа ќе ја отстрани потребата од

дополнителни промени во кодот.

Вториот начин за генерирање на гетери и сетери е да се селектира полето и тогаш да се

одбере Refactor > Encapsulate Field од менито. Овој метод поединечно генерира гетери и

сетери за секое поле посебно, но споредено со Source > Generate Getter and Setter,

рефакторирањето исто така ги менува референците до полето во повици до новите методи.

На пример ако започнеме од почеток со нова, едноставна верзија на Automobile класата,

како што е прикажано подолу:

public class Automobile extends Vehicle { public String make; public String model; }

Следно, креирајте класа која инстанцира Automobile и го пристапува make полето

директно:

public class AutomobileTest { public void race() { Automobile car1 = new Automobile(); car1.make= "Austin Healy"; car1.model= "Sprite"; // ... } }

Сега енкапсулирајте го make полето преку обележување на името на полето и селектирање на

Refactor > Encapsulate Field. Во дијалогот, внесете ги имињата на гетер и сетер методите –

како што очекувате ти стандардно се наречени getMake() и setMake() .

Исто така може да одберете дали методите кои се во иста класа како полето ќе продолжат

директно да пристапуваат до него или и овие референци ќе бидат променети да ги користат

методите за пристап како што е случај со другите класи.

39

По притискање на ОК, make полето во Automobile класата ќе биде private и ќе има

getMake() и setMake() методи како што е прикажано: public class Automobile extends Vehicle { private String make; public String model; public String getMake() { return make; } public void setMake(String make) { this.make = make; } }

AutomobileTest класата исто така ќе биде променета да ги користи новите методи за

пристап: public class AutomobileTest { public void race() { Automobile car1 = new Automobile(); car1.setMake("Austin Healy"); car1.model= "Sprite"; // ... } }

Change Method Signature

Последното рефакторирање кое е спомнато овде е најтешко да се користи: Change Method

Signature. Прилично е очигледно што прави истото: ги менува параметрите, видливста и

типот кој го враќа еден метод. Тоа што не е толку очигледно е ефектот кој овие промени

го имаат врз методот или врз кодот кој го повикува тој метод. Доколку промената

40

предизвикува проблеми кај методот кој е рефакториран, од причини како што се дека

рефакторирањето остава недефинирани варијабли или несовпаѓање на типови, самата

операција на рефакторирање ќе ги маркира истите. Во тој случај имате опција да го

прифатите рефакторирањето и покрај грешките и подоцна да ги поправите истите, или да

го откажете процесот. Доколку рефакторирањето предизвикува проблеми кај другите

методи тие се игнорираат и морате да ги поправите сами после рефакторирањето.

За да го разјасниме ова, ќе го разгледаме следниот пример:

public class MethodSigExample { public int test(String s, int i) { int x = i + s.length(); return x; } }

Методот test() во горната класа е повикан од метод во друга класа, како што е покажано

долу. public class callTestClass { public void callTest() { MethodSigExample eg = new MethodSigExample(); int r = eg.test("hello", 10); } }

Обележете го test во првата класа и одберете Refactor > Change Method Signature. Ќе Ви

се појави следниот дијалог:

41

Првата опција е да се промени видливоста на методот. Во овој пример промената во

protected или private ќе и’ оневозможи на callTest() методата во втората класа пристап до

методот. Eclipse нема да ја маркира оваа грешка додека го извршува рефакторирањето,

оставено е на Вас да ја одберете вистинската вредност.

Следната опција е да го смените типот кој го враќа методот. Промена на типот во float на

пример, не е означено како грешка бидејќи int во изразот за враќање на test() методот е

автоматски променет во float. Сепак, ова ќе предизвика проблем во callTest() методот во

втората класа бидејќи float не може да биде променет во int. Ќе треба или да ја кастирате

вредноста која ја враќа test() во int или пак да го промените типот на r во callTest() во float.

Слични аспекти се јавуваат доколку го промениме типот на првиот параметар од String во

int. Ова ќе биде маркирано за време на рефакторирањето бидејќи предизвикува проблем

внатре во методот кој се рефакторира: int не поседува length() метод. Промена пак во

StringBuffer, нема да биде обележано како проблем бидејќи поседува length() метод, но тоа

повторно би јавило проблем во callTest() методот поради тоа што истиот се’ уште

пренесува String() кога го повикува test().

Како што е спомнато претходно, во случаи каде што рефакторирањето резултира со

грешка, без разлика дали е маркирано или не, може да продолжите со едноставно

корегирање на грешките една по една. Друг начин е да ги присвоите грешките. Доколку

сакате да го отстраните параметарот i бидејќи истиот е непотребен, можете да започнете

42

со отстранување на референците до него внатре во методот кој се рефакторира. Понатаму

рефакторирањето ќе се одвива поедноставно.

Последно нешто кое треба да се објасни е Default Value опцијата. Оваа опција се користи

само кога параметар се додава на потписот на методот. Се користи за да обезбеди

вредност кога параметарот е додаден на повикувачите. На пример, ако додадеме

параметар од тип String со име n, и стандардна вредност world, повикот до test() во

callTest() методот ќе биде променет на следниот начин: public void callTest() { MethodSigExample eg = new MethodSigExample(); int r = eg.test("hello", 10, "world");

}

Заклучокот кој треба да се извлече од оваа навидум комплицирана дискусија за Change

Method Signature рефакторирањето не е дека станува збор за проблематично

рефакторирање, туку за моќно рефакторирање, кое заштедува на време но кое побарува

разумно планирање со цел да се користи правилно.

Грешки при Extract Method

Кога се обидувате да екстрахирате метод, може да добиете една од следните грешки:

Selected block references a local type declared outside the selection

Декларацијата на локален тип не е дел од селекцијата, но е референцирана од една

од изјавите селектирани за екстрахирање. Во овој случај е потребно или да се

прошири селекцијата така што ќе ја вклучува и декларацијата на локалниот тип или

да се намали истата така што референца до декларацијата на локалниот тип нема да

биде селектирана.

A local type declared in the selected block is referenced outside the selection

Селекцијата покрива дефиниција на локален тип но типот е исто така

референциран надвор од селектираните изрази. Потребно е или да се прошири

селекцијата така што ќе ги вклучи сите референци до локалниот тип или да се

намали селекцијата со цел да не ја опфаќа декларацијата на локалниот тип.

Ambiguous return value: selected block contains more than one assignment to local

variable Повеќе од едно доделување на локална варијабла било пронајдено внатре

во селектираниот блок. Или треба да се намали селекцијата така што само едно

доделување е селектирано или да се прошири селекцијата така што барем сите

референци освен една до локалните варијабли се исто така покриени од

селекцијата.

Ambiguous return value: expression access to local and return statement

selected Селектираните изрази генерираат повеќе од една return вредност. Ова е на

пример случај доколку е селектиран израз а исто така се модифифицирани и

43

аргументи на изразот. За да се реши овој проблем потребно е да се прошири

селекцијата на начин што исто така ќе го покрива read пристапот на

модифицираниот аргумент.

Selection contains a break statement but the corresponding break target isn't selected За

да се реши овој проблем потребно е или да се прошири селекцијата за да го вклучи

break / continue таргетот или да се намали селекцијата со цел да не биде покриен

break / continue изразот.

Selection contains a continue statement but the corresponding continue target isn't

selected За да се реши овој проблем потребно е или да се прошири селекцијата за да

го вклучи break / continue таргетот или да се намали селекцијата со цел да не биде

покриен break / continue изразот.

Selection starts inside a comment Делови од коментар не можат да бидат

екстрахирани. Во овој случај треба или да се прошири селекцијата така што ќе го

покрива целиот коментар или да се намали истата со цел коментарот воопшто да не

биде опфатен.

Selection ends inside a comment Во овој случај треба или да се прошири селекцијата

така што ќе го покрива целиот коментар или да се намали истата со цел коментарот

воопшто да не биде опфатен.

Cannot extract selection that ends in the middle of a statement Треба да се прилагоди

селекцијата така што сет од изрази - statements или expressions ќе биде покриен во

целост. Корисникот може да ја прошири селекцијата до валиден ранг со користење

на Expand Selection to во Edit менито.

Иднина И покрај сите функционалности кои моментално постојат во Eclipse, се’ уште сме далеку

од комплетно покривање на рефакторирачкиот каталог. Згора на тоа, ова е само мал

почеток, можни се голем број на трансформации. Иднината ветува поголема нагласеност

на паралелизмот во форма на повеќејадрени платформи, кластери и масицни машини кои

се состојат од илјадници или дури милиони процесори. Конкурентно свесни и

конкурентно насочени рефакторирања ќе бидат важни алатки со цел да се забрза развојот

на ваков тип на софтвер. Дополнително, повеќето моментално присутни рефакторирања

ефектираат промени на релативно фино разгранети структури како што се методи,

полиња, изрази (expressions, statements) и индивидуални типови, рефакторирања кои

манипулираат со поостро разгранети структури (како што се пакети, цели хиерархии на

типови, компоненти и слично) можат да му овозможат на софтверот рафинирање блиску

до архитектурално ниво.

Што ни е потребно за да го исполниме ветувањето за ваков богат пакет на

трансформации? Во Eclipse, рефакторирањето се состои од неколку фази: проверка на

предусловите, детална анализа и презапишување на изворниот код. Следуваат три

препораки кои би го олесниле развивањето на рефакторирањата.

44

Прво, потребен ни е поедноставен механизам за презапишување на кодот со цел да се

избегне пишување на сложен императивен код кој ги креира AST јазлите еден по еден.

Ваков механизам би бил особено полезен доколку обезбедува осигурувања на точноста на

генерираните конструкции или барем да извршува проверки во време на извршување со

цел да се провери точноста. Имплементациите на рефакторирањето веќе се заштитени од

проблеми со форматирањето на ниско ниво, но API на повисоко ниво ќе придонесе до

поголема реупотребливост на рефакторирачките компоненти.

Второ, потребен е подобар начин на специфицирање на основната анализа, која се мапира

во ефикасна и скалабилна имплементација која дозволува рефакторирањето да се примени

на големи кодни бази.

Трето, потребно е големо истражување во разбирање на семантиката и манипулацијата со

распространетите мешавини на јазици.

Дополнителни понатамошни насоки кои во голема мера би го зголемиле опсегот на

алатките го вклучува Светиот Грал на програмерот – специфицирани рефакторирања, и

погенералните трансформации (кои не го зачувуваат однесувањето) специфицирани од

програмерите.

Понатамошните инструменти би можеле да ги заменат опасните, но доста застапени

јазично несвесни макропроцесори, што би им дало на програмерите статичка безбедност и

моќта во голема мера да ја намалат тежината во креирањето на регуларни кодни

структури. Со комбинација од овие мета алатки на располагање би имале доста побогат

простор на трансформации, но најверојатно ќе поминат години додека да се постигне тоа

ниво.

Референци

1. http://help.eclipse.org/juno/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Freference%2

Fref-wizard-refactorings.htm

2. Leif Frenzel: The Language Toolkit: An API for Automated Refactorings in Eclipsebased

IDEs, http://www.eclipse.org/articles/Article-LTK/ltk.html

3. Tobias Widmer: Unleashing the Power of Refactoring,

http://www.eclipse.org/articles/article.php?file=Article-Unleashing-the-Power-of-

Refactoring/index.html

4. Refactoring in the Eclipse JDT: Past, Present, and Future

Robert M. Fuhrer, Adam Kie˙zun, Markus Keller