Программисты разделяются на две категории - те, кому нужно разработать программу, и те, кому хочется написать программу. Вторых принято называть хакерами. Именно они придумали язык Си, на котором можно написать тетрис в одной строке директив препроцессора и вообще заставить компьютер прыгать через пылающий обруч. А первые, прагматики, получают деньги за то, что с минимальными усилиями предоставляют клиенту максимум удобств. Именно для них и был придуман объектно-ориентированный подход к программированию, и то, что у истоков его стояли хакеры, нисколько не мешает прагматикам писать программы, которые просто отлаживать и модифицировать по требованию заказчика.
Исторически принципы ООП использовались при программировании на традиционных языках, иногда даже не очень структурных. Затем был разработан чисто объектный язык Smalltalk, в котором даже чихнуть нельзя было без того, чтобы не создать объект. Он был очень объектным, но не очень эффективным. Хакеры не любят, когда их в чем-то ограничивают, и потому они решили добавить объекты в свой любимый Си. Получившийся гибрид удовлетворил всех - он позволял писать просто и удобно, но в нем остались и возможности для любителей острых ощущений.
Через некоторое время команда хакеров (а они никогда не останавливаются на достигнутом) решила сделать что-нибудь действительно крутое, и они взяли и переписали все, что знали, начисто. Не добавляя к готовому, как это обычно делается, а на пустом месте. В частности, они сделали язык программирования, похожий на Си, но хороший. Так получилось, что в Java объекты родные, и без того, чтобы писать красиво и объектно, писать на Java нельзя.
Основополагающими в ООП считаются три понятия: инкапсуляция, наследование и полиморфизм. Инкапсуляцией называют объединение данных и алгоритмов их обработки в общий черный ящик, называемый далее классом. При использовании класса можно не думать о том, как он устроен - язык (если он, конечно, объектный) сам позаботится, чтобы вмешаться в его работу непредусмотренным при создании способом было невозможно. А когда думаешь только о том, что надо сделать - без того, что может из этого выйти, жизнь становится проще.
Наследование тоже упрощает жизнь - оно позволяет использовать уже созданные классы для своих целей, наделяя их новыми свойствами. Приятно бывает взять готовую программу, которая умеет прыгать через обруч, и научить ее делать сальто. Типичный пример - апплет Java, являющийся потомком класса Applet. В классе написано, как устроен апплет и как он взаимодействует с браузером, а в конкретном апплете остается определить, чем данный апплет отличается от выводящего пустой серый прямоугольник.
Полиморфизм представляет собой еще одно понятие с мудреным названием и очевидной сущностью. Гибкость - его второе имя. Перегрузка методов (overloading) означает создание нескольких реализаций одного метода для обработки разнотипных данных. Так, метод write класса window может выполнять различный код для строк, чисел или картинок, совершая одно действие - вывод в окно.
Еще более интересные возможности предоставляет полиморфизм вкупе с наследованием. Класс-потомок может перекрывать (override) методы суперкласса, то есть назначать свою реализацию метода вместо унаследованной. В результате класс-предок сможет вслепую работать с типами данных, о существовании которых во время его создания могло быть и неизвестно. Например, в Java класс Object, потомками которого являются все классы, содержит метод toString, преобразующий данные объекта в строку. В частности, он используется при автоматическом преобразовании к типу String. Перекрыв toString, вы можете определить строковое представление своего класса.
На деле это может выглядеть так, как показано в моем примере объектной программы. Я вволю порезвился, запихивая в маленькую программку большие возможности ООП, хотя бы поэтому она заслуживает внимательного изучения.
Класс Point инкапсулирует координаты точки в двухмерном пространстве. Одноименные классу методы Point - конструкторы, которые вызываются при создании экземпляра класса оператором new. В зависимости от типа (а в моем случае - наличия) параметров будет использован один из них. Это и есть перегрузка. Ключевое слово this обозначает объект, от имени которого был вызван метод класса. Без конкретного объекта могут использоватся только элементы класса с модификатором static - метод main, к примеру - в таком случае перед точкой ставится не имя объекта, а имя класса.
Метод toString делает то, что ему и положено - перекрывает преобразование к типу String. Тут заслуживает внимания лишь конкатенация строк оператором '+' - это единственный в Java случай перегрузки оператора. В остальных местах, дабы не вводить в искушение, позволено перегружать только методы.
Класс Pixel является расширением, то есть наследником, класса Point. Вдобавок к координатам здесь вводится цвет. Слово super аналогично this- оно обозначает обращение к классу-прародителю. Обратите внимание на основную программу - переменная p объявлена как Point, но инициализируется конструктором Pixel. В результате она и будет ссылаться на объект класса Pixel: переменной одного класса можно присвоить ссылку на объект класса-наследника. Язык сам, во время выполнения, будет определять, методы какого класса вызывать для данного объекта.
Много еще о чем можно было бы рассказать, но любой истории когда-нибудь приходит конец. Пытливый читатель, ведомый тягой к знаниям, может добраться до таких глубинных тайников Java, которых не видел и я. Надеюсь лишь, что по прочтении сего опуса настороженное отношение к этой технологии сменится у него здоровым любопытством. Познавайте, и откроется вам!
Дмитрий БОРОДАЕНКО
Пример объектной программы
class Point { int x,y; Point(int x, int y) { this.x=x; this.y=y; } Point() { this(0, 0); } public String toString() { return "Point("+x+","+y+")"; } } class Pixel extends Point { int color; Pixel(int x, int y, int color){ super(x, y); this.color=color; } Pixel() { super(); this.color=0; } public String toString() { return "Pixel at "+ super.toString()+ " with color "+color; } } class Example { public static void main(String args[]) { Point p=new Pixel(3, 15, 6); System.out.println("p="+p); } }
Горячие темы