Что такое абстрактный класс? Чем интерфейс отличается от абстрактного класса. Примеры реализации абстрактного класса и интерфейса в perl. Использование Class::Interface
Абстрактный класс в ООП — это базовый класс, который не предполагает создания своих экземпляров. Абстрактный класс может содержать абстрактные методы и атрибуты.
Абстрактный класс является яркой демонстрацией одного из основных принципов ООП — полиморфизма.
В некоторых языках программирования использование абстрактных классов запрещено. В некоторых — разрешено. Perl, как всегда, посередине. В нем можно реализовать абстрактный класс стандартными возможностями языка. Но в нем нет специальных конструкций для этого, как, например, в PHP или Java.
Абстрактный класс в PHP:
1 2 3 4 5 6 7 8 9 10 |
<?php abstract class MyClass { abstract function aMethod( $an_arg ); public function pFunction() { ... } ... } ?> |
Абстрактный класс в Java:
1 2 3 |
public abstract class AbstractList { ... } |
Не смотря на то, что в Perl нет стандартного определения абстрактных классов, их использование — это интересный и полезный архитектурный прием.
Иногда абстрактные классы путают с интерфейсами. Это не совсем корректно. Хотя интерфейс в Perl часто является абстрактным классом.
Абстрактный класс отличается от интерфейса тем, что интерфейс — это только список методов, которые обязательно должны быть определены в подклассах. Абстрактный класс, во-первых, ни к чему не обязывает подкласс, во-вторых, может содержать полноценные функции и методы.
Обычно говорят, что абстрактный класс расширяется (extends), а интерфейс — реализуется (implements).
Интерфейс в perl, создаваемый с помощью Moose:
1 2 3 4 5 6 7 8 9 |
package InterfaceClass; use strict; use Moose::Role; # ничего лишнего, только список обязательных для определения в подклассе методов requires 'Imethod'; 1; |
Использование абстрактных классов удобно тем, что можно разместить в этом классе все функции и методы, которые будут потом нужны во множестве других классов. Абстрактный класс — это отличное решение, которое спасает от дублирования кода.
Зачем нужны абстрактные классы и интерфейсы
Допустим у вас есть программа, которая скачивает с удаленного сервера файлы и обрабатывает их.
Сначала вы закачиваете простые txt-файлы, читаете данные, сохраняете их в БД, потом переносите файл в директорию с обработанными файлами. Для обработки файлов вы используете специальный класс Document.
Потом приходит ваш клиент и говорит, что через месяц надо будет обрабатывать не только txt-файлы, но и xml. А еще через пару месяцев добавляется doc и pdf. Если txt и xml еще чем-то похожи, то обработка doc и pdf уже сильно выделяется из общего потока. Теперь вы создаете для обработки каждого документа свой класс: DocumentXML.pm , DocumentPDF.pm и т.п. Начинает дублироваться код. Появляется путаница в обработчиках. В конце концов вы вызываете метод для распечатки csv-файла и программа падает, т.к. вы забыли добавить в DocumentCSV.pm метод printer().
Самым разумным в данном случае, будет превратить Document.pm в интерфейс, который будет задавать список методов, обязательных к определению в каждом подклассе. При этом, класс-интерфейс будет являться абстрактным классом, и его экземпляры в процессе работы программы создаваться не будут.
Теперь, если мы забыли указать какой-то метод, это станет ясно при первой же попытке запуска программы.
Более того, если некоторые методы будут общими для всех подклассов, можно определить их в абстрактном классе. Например, методы копирования и переноса файлов по разным директориям, или пересылки по почте и т.п.
Реализация интерфейсов и абстрактных классов в Perl с помощью Class::Interface
Class::Interface — удобный модуль, который помогает сделать реализацию интерфесов и абстрактных классов в perl делом простым и приятным.
Moose, кстати, тоже позволяет упростить реализацию интерфейсов. Пример с Moose уже приводился выше. Но Class::Interface, в отличие от Moose, небольшой и простой модуль, а не сложная и тяжелая система.
Для создания абстрактных классов и интерфейсов, модуль предоставляет 4 подпрограммы:
- &interface() — превращает вызвавший ее класс в интерфейс.
- &implements() — загружает интерфейс, имя которого передается в качестве параметра, и проверяет вызвавший класс на наличие всех необходимых методов.
- &abstract() — превращает вызвавший класс в абстрактный.
- &extends() — загружает данные указанного абстрактного класса и проверяет вызывающий класс на наличие абстрактных методов.
Для создания интерфейса необходимо, чтобы:
- было указано use Class::Interface;
- была вызвана подпрограмма &interface()
- была объявлена хотя бы одна подпрограмма (англ. routine). Реализация подпрограммы в рамках интерфейса не является обязательной. Объявление подпрограммы допустимо двумя способами:
- sub routine;
- sub routine {}
Для создания абстрактного класса необходимо, чтобы:
- было указано use Class::Interface;
- была вызвана подпрограмма &abstract()
- была объявлена хотя бы одна подпрограмма
Реализация интерфейса с помощью Class::Interface
Интерфейс:
1 2 3 4 5 6 7 8 9 10 11 |
package InterfaceDocument; use strict; use Class::Interface; &interface; sub read; sub write; 1; |
Класс, реализующий интерфейс:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
package DocumentXML; use strict; use Class::Interface; &implements('InterfaceDocument'); sub new { my $type = shift; my $data = {}; my $obj = bless($data, $type); return $obj; } sub parse { # ... print "Parsing process...\n"; } sub read { # ... print "I can read\n"; } sub write { # ... print "I can write\n"; } 1; |
Скрипт:
1 2 3 4 5 6 |
#!/usr/bin/perl use DocumentXML; my $obj = DocumentXML->new; $obj->read; |
Если в классе, реализующем интерфейс, не все методы определены, при запуске программы мы получим сообщение об ошибке:
1 2 3 4 5 |
$ perl subclass.pl SubClass fails to implement read from AbstractClass, SubClass fails to implement write from AbstractClass at SubClass.pm line 7. Compilation failed in require at subclass.pl line 3. BEGIN failed--compilation aborted at subclass.pl line 3. |
Реализация абстрактного класса с помощью Class::Interface
Абстрактный класс:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package Document; use strict; use Class::Interface; &abstract; sub copy { my ( $self, $name ) = @_; # ... print $name."\n"; $self->delete; } sub delete; 1; |
Класс, расширяющий абстрактный класс:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package DocumentXML; use strict; use Class::Interface; &extends('Document'); sub new { my $type = shift; my $data = {}; my $obj = bless($data, $type); return $obj; } sub delete { # ... print "Delete XML\n"; } 1; |
Скрипт:
1 2 3 4 5 6 |
#!/usr/bin/perl use DocumentXML; my $obj = DocumentXML->new; $obj->copy("000999_stat_result.xml"); |
Полезные ссылки по теме «Абстрактный класс — Abstract Class — в Perl»
wikipedia.org: Абстрактный класс
Где тут регистрация не пойму