WDH PERL - Класове и обекти
След кратко въведение в модулите, сега можем да преминем към подробно описание на тяхното използване при създаване на обектно-ориентирани програми. Обектното програмиране се третира по различен начин: някои като единствения истински начин за програмиране, други като мода. Авторът вярва, че обектният подход ви позволява бързо и ясно да формализирате постановката на проблема в езиците за програмиране, така че по-нататъшното описание на създаването на модули и работата с тях е изградено на основата на този подход.
Както знаете, обектно-ориентираното програмиране се основава на две основни концепции: класове и обекти. Класът е абстракция, която описва определени свойства (данни за класа и методи за работа с тях). Обектът е конкретен екземпляр на клас, който реализира неговите свойства.
PERL не съдържа нито класове, нито обекти в традиционния смисъл. Независимо от това, обектното програмиране на него е напълно възможно, ако следвате трите правила, формулирани от създателите на този език:
Нека разгледаме тези правила по-подробно.
6.8.2. Създаване на класове
В модула Info::Browser няма да използваме модула Explorer, защото той не експортира никакви знаци. На първо място, трябва да дефинираме структурата на данните на нашия клас. Най-често структурата на класа се реализира като ненаименувани асоциативни масиви, в които полетата с данни са индексирани по техните имена. За модула Info::Browser използваме асоциативен масив с три полета с данни: низът NAME (име на браузъра), номерът VERSION (номерът на версията) и масивът OPTIONS от две булеви стойности (първата стойност показва дали са разрешени Java аплети, втората показва дали са разрешени бисквитки).
След това се нуждаем от метод, който създава обекти от този клас. Такива методи се наричатконструктори и може да има произволно име. Повечето програмисти на PERL обаче ги наричат или с името new(), или с името на класа. Нашият конструктор изглежда така:
Тази функция казва на съдържанието на ref, че е обект на клас. Ако класът е пропуснат, тогава се използва текущият пакет. Функцията bless връща реф, така че операторътreturn в нашия конструктор не е задължителен и е въведен само за яснота.
Вече сме готови да напишем пълния текст на модула Info::Browser. В допълнение към конструктора, той съдържа три метода name(), version() и options(), които осигуряват достъп до полетата с данни на нашите обекти. Тези методи следват тази конвенция: ако се извикат без аргументи, те връщат текущата стойност на полето, а ако са с аргумент, тогава я поставят в съответното поле за данни.
Следващият пример показва как да създадете обект от класа Info::Browser и да инициализирате неговите полета:
Имайте предвид, че потребителят на нашия модул не се нуждае от подробности за неговото изпълнение. Той просто извиква конструктора и след това извиква неговите методи.
Създаденият от нас модул е доста функционален, но конструкторът му има два недостатъка. Първо, той се извиква само като метод на клас, а не като метод на обект. С други думи, не можем да пишем
Второ, функцията му за благословение е твърдо свързана с класа Info::Browser, защото го извикваме без втори аргумент. Ако по-късно искаме да създадем класове, които са наследници на Info::Browser, трябва да извикаме bless с два аргумента, като помним, че този клас се предава на всеки метод на клас като първи аргумент. Имайки предвид тези забележки, нашият конструктор приема формата:
Всички методи по-горе работеха върху полетата с данни на обекта. Разбира се, възможностите на методите не саограничен. Можем например да добавим следния метод към нашия клас:
6.8.3. Деструктори на обекти
Повечето езици за програмиране, които поддържат създаване на обекти чрез конструктори, поддържат и деструктори на обекти. Деструкторът е метод, който автоматично се извиква от средата за изпълнение, когато даден обект бъде унищожен. За разлика от конструктора, той трябва да има едно възможно име, а именно DESTROY. Причината за това е именно, че най-често се извиква автоматично от системата за събиране на боклук, която изисква всички деструктори да са с еднакви имена.
Въпреки че в PERL са предоставени деструктори на обекти, писането им е изключително рядко, отново поради наличието на система за събиране на отпадъци. Всъщност единствената ситуация, в която е необходимо да се напише деструктор, е за обекти, които съдържат пряка или косвена препратка към себе си. Такъв обект никога няма да бъде изтрит автоматично (поне докато програмата не приключи) и ако се интересуваме от оптимизиране на паметта, трябва изрично да извикаме деструктора на обекта.
6.8.4. Данни за класа
Досега всички данни в нашия клас са обектни данни, т.е. препращат към конкретен екземпляр на класа. В много случаи обаче се нуждаете от променливи, които се отнасят до класа като цяло. В нашия пример такава променлива може да бъде променливата $Count, съдържаща броя на съществуващите екземпляри на класа. Използвайки го, началото на модула изглежда така:
Както виждаме, добавен е методът total(), който връща стойността на $Count и обектният деструктор, който е необходим за правилното преброяване на съществуващите обекти.
Данните и методите, които се отнасят за класа като цяло, а не за неговите екземпляри, се наричат статични.
6.8.5. Наследство
Всички обекти-системите за ориентирано програмиране включват концепцията за наследяване. Наследяването означава, че създаваният клас може да бъде деклариран като наследник на вече съществуващ клас. Дете на клас наследява всички негови свойства, но по избор може да има свои собствени свойства или да променя наследените свойства.
PERL не поддържа наследяване на класове. Вместо това всеки пакет съдържа променлива @ISA, която контролира наследяването на метода. Ако извикаме метод на клас или обект, който не е в пакета на класа, тогава PERL търси в пакетите, изброени в @ISA, за метод с това име. Помислете за този пакет:
Този кратък модул създава нов клас Info::System, който наследява всички методи на класа Info::Browser. Например, можем да напишем програма като тази:
Нека да видим какво се случва, когато извикаме метода Info::System->new(). Тъй като няма такъв метод в пакета Info::System, PERL го търси в масива @ISA и го намира в пакета Info::Browser. След това извиква този метод, като му предава името на класа Info::System като негов първи аргумент, т.е. този оператор се заменя с Info::Browser::new("Info::System"). След това се задейства функцията bless, чийто втори аргумент е името на преминалия клас, както е описано по-горе. В резултат на това получаваме препратка към новосъздадения обект на класа Info::System, което ни трябва.
Сега можем да добавим наши собствени методи към класа Info::System, например:
Освен това можем да предефинираме част от методите на класа Info::Browser в Info::System, например:
Тъй като @ISA е масив, нищо не ни забранява да реализираме множествено наследяване на класове в PERL, тоест да създадем клас, който е потомък на няколко класа едновременно. Множественото наследяване еголяма тема, която изисква отделно разглеждане, така че няма да я засягаме тук.
6.8.6. УНИВЕРСАЛЕН клас
Истинските познавачи на PERL вярват, че целият чар на този език е в неговите настройки по подразбиране и затова те пишат в него по такъв начин, че непосветените да не разберат нищо. Дори и без да споделяме този подход, трябва да се признае, че познаването на настройките по подразбиране на PERL често е полезно. Едно полезно поведение по подразбиране е следното: PERL винаги имплицитно добавя името на модула UNIVERSAL към края на масива @ISA. С други думи, всички класове на PERL наследяват методите на класа UNIVERSAL. Има три такива метода: can, isa и VERSION.
ВЕРСИЯ Метод
Обикновено този метод не се извиква изрично; извиква се от функцията use() при проверка на версията. За да сте сигурни, че версията на вашия модул е проверена, просто трябва да добавите ред като
6.8.7. Неназовани подпрограми като обекти
Реализацията на обекти под формата на ненаименувани асоциативни масиви, която използвахме досега, разбира се, не е единствената възможна. Ето алтернативна реализация на класа Info::Browser, базирана на неименувани подпрограми. Той е малко по-сложен и по-бавен, но има едно значително предимство: данните за обекта стават напълно скрити от външния свят. Новата версия на нашия клас изглежда така:
6.8.8. Свързващи променливи
Нашият пример OutFile скрива запис в текстов файл зад обвързана променлива. Когато променлива е обвързана с класа OutFile, се извиква нейният конструктор, който създава файл с даденото име. Когато връзката бъде изтрита, този файл се затваря. Всеки път, когато на обвързана променлива се присвои нова стойност, тази стойност се извежда във файла като нов ред. Съответният пакет OutFile.pm ще бъде:
Сега можем да напишем програмаизползвайки класа OutFile. За да направим това, се нуждаем от вградените PERL функции, които създават и премахват асоциирането на променливи с обекти. Има три функции за работа с обвързани променливи: tie(), tied() и untie().
Функцията за свързване свързва променлива с клас. Тя прилича на
(Ако необвързана променлива се предаде на tied, тя връща недефинирана стойност.) За да развържете променлива от обект, използвайте функцията
Когато връзката е прекъсната, имплицитно извиква деструктора на обекта, свързан с променливата.
Сега можем да напишем пример за използване на променлива, свързана с класа OutFile:
След като изпълним тази програма, ще намерим в текущата директория нов файл MYFILE.TXT, съдържащ следните редове:
Първи Втори Трети
Най-честата употреба на обвързване на променливи е за имплицитно свързване на асоциативни масиви към бази данни, но тази тема е извън обхвата на това ръководство.