Объектно-ориентированное программирование: различия между версиями

м
== Создание и уничтожение объектов ==
 
=== Конструкторы ===
 
Свойство: Вышеупомянутый класс ужасно прост. Первое, что мы могли бы к нему добавить, это конструктор, что является хорошей техникой для решения проблемы инициализации объекта.
OP: В Object Pascal вы используете специальное ключевое слово constructor и можете дать конструктору любое имя. Хотя Borland в Delphi 4 добавила поддержку перегрузки методов, программисты всё ещё дают разным конструкторам разные имена. В Object Pascal у каждого класса по умолчанию есть конструктор Create (наследуемый от TObject), если вы не перегрузите его конструктором с тем же именем и, возможно, другими параметрами. Этот конструктор, как мы увидим позднее, просто наследуется от общего базового класса ;)
 
=== Деструкторы и финализация ===
 
Свойство: Деструктор играет роль противоположную конструктору и обычно вызывается при уничтожении объекта. Если конструктор нужен большинству классов, только некоторые из них нуждаются в деструкторе. Деструктор в основном должен освободить ресурсы, зарезервированные конструктором (или другими методами во время жизни объекта). Эти ресурсы включают память, файлы, базы данных, ресурсы Windows и  т.  д.
 
C++: В C++ деструкторы автоматически вызываются, когда объект выходит из области определения или когда вы удаляете объект, заведенный динамически. У каждого класса есть только один деструктор.
 
OP: В Object Pascal деструкторы похожи на деструкторы C++. /Для деструкторов используется ключевое слово destructor (мое примечание - — В. К.)/ Object Pascal использует стандартный виртуальный деструктор, называемый Destroy. Этот деструктор вызывается стандартным методом Free. Все объекты динамические, поэтому предполагается, что вы вызовете Free для каждого объекта, созданного вами, если у того нет владельца, отвечающего за его уничтожение. Теоретически вы можете объявить несколько деструкторов, что имеет смысл, поскольку вы можете вызывать деструкторы в своем коде (это не делается автоматически).
 
Java: В Java нет деструкторов. Объекты, на которые нет ссылок, уничтожаются сборщиком мусора, который работает в виде фоновой задачи (как описывалось ранее). Прежде чем уничтожать объект, сборщик мусора должен вызвать метод finalize(). Однако нет никакой гарантии, что этот метод вызывается в каждой JVM. По этой причине, если вам нужно освободить ресурсы, вы должны добавить какой-нибудь метод для этого, и убедиться, что он вызывается (эти дополнительные усилия не нужны в других ОО языках).
Свойство: Общим элементом всех трех языков является присутствие трех спецификаторов доступа, указывающих на различные уровни инкапсуляции класса: public, protected, и private. Public означает: видимый любым другим классом, protected означает: видимый производными классами, private означает: отсутствие видимости извне. В деталях, однако, есть различия.
 
C++: В C++ вы можете использовать ключевое слово friend для обхода инкапсуляции. Видимость по умолчанию для класса - — private, для структур - — public.
 
OP: В Object Pascal private и protected относятся только к классам других юнитов. В терминах C++, класс является дружественным для любого другого класса, определенного в том же юните (или файле исходного кода). В Delphi есть еще один модификатор доступа - — published, который генерирует информацию времени выполнения (RTTI) об элементах.
 
Java: В Java отличие синтаксиса в том, что модификатор доступа повторяется для каждого элемента класса. А конкретнее, по умолчанию в Java используется friendly, это значит, что элемент видим для других классов этого же пакета (или файла исходного кода, как в OP). Подобным образом, protected означает видимость для подклассов, тогда как комбинация private protected соответствует protected в C++.
Свойство: Важное различие между тремя языками заключается в организации исходного кода в файлах. Все три языка используют файлы в качестве стандартного механизма для запоминания исходного кода классов (в отличие от других ОО языков, таких как Smalltalk), но компилятор C++, в отличие от OP или Java, не понимает файлов. Эти же два языка работают с идеей модулей, хотя называют их по-разному.
 
C++: В C++ программист обычно помещает определение класса в файл объявлений, а определение методов - — в отдельный файл кода. Обычно у этих двух файлов одинаковые имена и различные расширения. Компилируемый блок, как правило, ссылается (включает в себя) на свой файл объявлений и на файлы объявлений тех классов (или функций), на которые ссылается код. Все эти соглашения не утруждают компилятор. Это значит, что линкеру предстоит большая работа, потому что компилятор не может знать, в каком другом модуле может быть определен нужный метод.
 
OP: В Object Pascal каждый файл исходного кода называется unit, и он делится на две
части: интерфейс и исполнение, отмечаемые соответственно ключевыми словами interface и implementation. Секция интерфейса включает в себя определения классов (с объявлениями методов), а секция исполнения должна включать в себя определения методов, объявленных в интерфейсе. Писать фактический код в секции интерфейса нельзя. Вы можете сослаться на объявления другого файла, используя предложение uses. Этим включается в компиляцию интерфейс того файла:
uses
== Классы и наследование ==
 
Свойство: Наследование у классов - — одно из оснований ООП. Оно может быть использовано для выражения генерализации или специализации. Основная идея в том, что вы определяете новый тип, расширяя или модифицируя существующий, другими словами, производный класс обладает всеми данными и методами базового класса, новыми данными и методами и, возможно, модифицирует некоторые из существующих методов. Различные ОО языки используют различные жаргоны для описания этого механизма (derivation, inheritance, sub-classing), для класса, от которого вы наследуете (базовый класс, родительский класс, суперкласс) и для нового класса (производный класс, дочерний класс, подкласс).
 
C++: C++ использует слова public, protected, и private для определения типа наследования и чтобы спрятать наследуемые методы или данные, делая их приватными или защищёнными. Хотя публичное наследование наиболее часто используется, по умолчанию берётся приватное. Как мы увидим далее, C++ - — единственный из этих трех языков, поддерживающий множественное наследование. Вот пример синтаксиса наследования:
class Dog: public Animal {
...
Свойство: В некоторых ОО языках каждый класс происходит по крайней мере от некоторого базового класса по умолчанию. Этот класс, часто называемый Object, или подобно этому, обладает некоторыми основными способностями, доступными всем классам. Фактически, все другие классы в обязательном порядке его наследуют. Этот подход является общим ещё и потому, что так первоначально делалось в Smalltalk.
 
C++: Хотя язык C++ и не поддерживает такое свойство, многие структуры приложений базируются на нём, вводя идею общего базового класса. Пример тому - — MFC с его классом COobject. Фактически это имело большой смысл вначале, когда языку не хватало шаблонов.
 
OP: Каждый класс автоматически наследует класс TObject. Так как язык не поддерживает множественное наследование, все классы формируют гигантское иерархическое дерево. Класс TObject поддерживает RTTI и обладает некоторыми другими возможностями. Общей практикой является использование этого класса, когда вам нужно передать объект неизвестного типа.
OP: В Object Pascal позднее связывание вводится с помощью ключевых слов virtual и dynamic (разница между ними только в оптимизации). В производных классах переопределённые методы должны быть отмечены словом override (это заставляет компилятор проверять описание метода). Рациональное объяснение этой особенности OP состоит в том, что разрешается больше изменений в базовом классе и предоставляет некоторый дополнительный контроль во время компиляции.
 
Java: В Java все методы используют позднее связывание, если вы не отметите их явно как final. Финальные методы не могут быть переопределены и вызываются быстрее. В Java написание методов с нужной сигнатурой жизненно важно для обеспечения полиморфизма. Тот факт, что в Java по умолчанию используется позднее связывание, тогда как в C++ стандартом является раннее связывание, - — явный признак разного подхода этих двух языков: C++ временами жертвует ОО моделью в пользу эффективности, тогда как Java - — наоборот
 
Примечание: Позднее связывание для конструкторов и деструкторов. Object Pascal, в отличие от других двух языков, позволяет определять виртуальные конструкторы. Все три языка поддерживают виртуальные деструкторы.
== Абстрактные методы и классы ==
 
Свойство: При построении сложной иерархии, для обеспечения полиморфизма программисты часто вынуждены вводить методы в классы верхнего уровня, даже если эти методы ещё не определены для этой специфической абстракции. Здесь можно было бы оставить пустые методы, но многие ОО языки предлагают такой специфический механизм, как определение абстрактных методов, то есть методов без реализации. Классы, имеющие хотя бы один абстрактный метод, часто называется абстрактным классом.
 
C++: В C++ абстрактные методы или чисто виртуальные функции получаются добавлением так называемого чистого описателя (=0) в определение метода. Абстрактные классы являются просто классами с одним или более абстрактным методом (или наследующие их). Вы не можете создать объект абстрактного класса.
OP: Object Pascal для выделения этих методов использует ключевое слово abstract. Кроме того, абстрактными классами являются классы, имеющие или наследующие абстрактные методы. Вы можете создать объект абстрактного класса (хотя компилятор выдаст предупреждающее сообщение). Это подвергает программу риску вызвать абстрактный метод, что приведёт к генерации ошибки времени выполнения и завершению программы.
 
Java: В Java и абстрактные методы, и абстрактные классы отмечаются ключевым словом abstract (действительно, в Java обязательно определять как абстрактный класс, имеющий абстрактные методы, - — хотя это кажется некоторым излишеством). Производные классы, которые не переопределяют все абстрактные методы, должны быть отмечены как абстрактные. Как и в C++, нельзя создавать объекты абстрактных классов.
 
== Множественное наследование и интерфейсы ==
Свойство: Некоторые ОО языки допускают наследование более чем одному базовому классу. Другие языки позволяют вам наследовать только от одного класса, но дополнительно позволять вам наследовать также от многочисленных интерфейсов или чисто абстрактных классов, то есть классов, состоящих только из виртуальных функций.
 
C++: C++ - — единственный из трех языков, поддерживающий множественное наследование. Некоторые программисты считают положительным фактом, другие - — отрицательным, и я не буду вмешиваться сейчас в эту дискуссию. Определенно, что множественное и повторяющееся наследование влечет за собой такие понятия, как виртуальные базовые классы, которые не легко освоить, хотя они предоставляют мощную технику. C++ не поддерживает понятия интерфейсов, хотя их можно заменить множественным наследованием чисто абстрактным классам (интерфейсы можно рассматривать как подмножество множественного наследования).
 
Java: Java, как и Object Pascal, не поддерживает множественное наследование, но полностью поддерживает интерфейсы. Методы интерфейсов допускают полиморфизм, и Вы можете использовать объект, осуществляющий интерфейс, когда ожидается интерфейсный объект. Класс может наследовать или расширить один базовый класс, но может осуществить (это - — ключевое слово) многочисленные интерфейсы. Хотя это не было спланировано заранее, интерфейсы Java очень хорошо укладываются в модель COM. Вот пример интерфейса:
public interface CanFly {
public void Fly();
C++: Язык C++ первоначально не поддерживал RTTI. Это было добавлено позже для динамического преобразования типа (dynamic_cast) и сделало доступной некоторую информацию о типе для классов. Вы можете запросить идентификацию типа для объекта, и проверить, принадлежат ли два объекта одному классу.
 
OP: Object Pascal и визуальная среда поддерживает и требует много RTTI. Доступен не только контроль соответствия и динамическое преобразование типов (с помощью операторов is и as). Классы генерируют расширенную RTTI для своих published свойств, методов и полей. Фактически это ключевое слово управляет частью генерации RTTI. Вся идея свойств, механизм потоков (файлы форм - — DFM), и среда Delphi, начиная с Инспектора Объектов, сильно опирается на RTTI классов. У класса TObject есть (кроме прочих) методы ClassName и ClassType. ClassType возвращает переменную типа класса, объект специального типа ссылки на класс (который не является самим классом).
 
Java: Как и в Object Pascal, в Java тоже есть единый базовый класс, помогающий следить за информацией о классе. Безопасное преобразование типов (type-safe downcast) встроено в этот язык. Метод getClass() возвращает своего рода метакласс (объект класса, описывающего классы), и Вы можете применить функцию getName() для того, чтобы получить строку с именем класса. Вы можете также использовать оператор instanceof. Java включает в себя расширенную RTTI для классов или интроспекцию, которая была введена для поддержки компонентной модели JavaBeans. В Java существует возможность создавать классы во время исполнения программы.
=== Обработка исключений ===
 
Свойство: Основная идея обработки исключений - — упростить код обработки ошибок в программе, предоставив стандартный встроенный механизм, с целью сделать программы более устойчивыми. Обработка исключений - — это тема, требующая отдельного рассмотрения, поэтому я только очерчу некоторые ключевые элементы и различия.
 
C++: C++ использует ключевое слово throw для генерации исключения, try для отметки охраняемого блока и catch для записи кода обработки исключения. Исключения - — объекты специального класса, которые могут образовывать некоторую иерархию во всех трёх языках. При возникновении исключения C++ выполняет очистку стека до точки перехвата исключения. Перед удалением каждого объекта в стеке вызывается соответствующий деструктор.
 
OP: Object Pascal использует подобные ключевые слова: raise, try, и except и обладает подобными свойствами. Единственное существенное отличие состоит в том, что опустошение стека не производится, просто потому, что в стеке нет объектов. Кроме того, вы можете добавить в конце блока try слово finally, отмечая блок, который должен выполняться всегда, независимо от того, было или нет вызвано исключение. В Delphi классы исключений - — производные Exception.
 
Java: Использует ключевые слова C++, но ведёт себя как Object Pascal, включая дополнительное ключевое слово finally. (Это общее свойство всех языков со ссылочно-объектной моделью, оно включено Borland также и в C++Builder 3.) Присутствие алгоритма сборки мусора ограничивает использование finally в классе, который распределяет другие ресурсы, кроме памяти. Также Java строже требует, чтобы все функции, которые могут вызвать исключение, описывали в соответствующем блоке, какие исключения могут быть вызваны функцией. Эти описания исключений проверяются компилятором, что является хорошим свойством, даже если оно подразумевает некоторую дополнительную работу для программиста. В классах Java объекты-исключения должны наследовать классу Throwable.
=== Шаблоны (обобщенное программирование) ===
 
Свойство: Обобщенное программирование - — это техника написания функций и классов, оставляя некоторые типы данных неопределёнными. Спецификация типа осуществляется, когда эта функция или класс используется в исходном коде. Всё делается под строгим контролем компилятора, и ничего не остаётся для определения во время выполнения. Наиболее типичный пример шаблона класса - — это контейнерные классы.
 
C++: В C++ есть шаблонные классы и функции, отмечаемые ключевым словом template. Стандартный C++ включает обширную библиотеку шаблонов, называемую STL (Стандартная библиотека шаблонов), которая поддерживает специфический и мощный стиль программирования: обобщенное программирование. C++ - — единственный из этих трех языков, который концентрируется на поддержке обобщенного программирования, помимо ООП.
 
OP: В Object Pascal нет шаблонов. Контейнерные классы обычно строятся как контейнеры объектов класса TObject, а затем уточняются для необходимых объектов.
 
Java: Шаблоны в Java реализуются в рамках Generics (введенного в JDK 1.5 "«Tiger"»). Концептульно они не отличаются от шаблонов в C++, но имеют некоторые особенности, которые диктуются свойствами самого языка.
 
=== Другие специфические свойства ===
Свойство: Есть еще другие свойства, не упомянутые мной, хотя они важны, только из-за того, что они специфичны только для одного из трёх языков.
 
C++: Я уже упомянул множественное наследование, виртуальные базовые классы и шаблоны. Эти свойства отсутствуют в двух других ОО языках. В C++ есть ещё перегрузка операторов, тогда как перегрузка методов присутствует также в Java и была недавно добавлена в Object Pascal. C++ позволяет программистам перегружать и глобальные функции. Вы можете перегрузить операторы преобразования типов, написав конвертирующие методы, которые будут вызываться "«за кулисами"». Объектная модель C++ требует копировать конструкторы и перегружать операторы присваивания, в чем не нуждаются остальные два языка, поскольку базируются на ссылочно-объектной модели.
 
Java: Только Java поддерживает многопоточность непосредственно в языке. Объекты и методы поддерживают механизм синхронизации (с ключевым словом synchronized): два синхронизированных метода одного класса не могут выполняться одновременно. Для создания нового потока вы просто наследуете от класса Thread, перегружая метод run(). Как альтернативу вы можете осуществить интерфейс Runnable (что вы обычно делаете в апплетах, поддерживающих многопоточность). Мы уже обсуждали сборщик мусора. Ещё одно ключевое свойство Java, конечно, идея переносимого байтового кода, но это не относится непосредственно к языку. Другое примечательное свойство - — это поддержка основанных на языке компонентов, известных как JavaBeans и многие другие свойства, недавно добавленные в этот язык.
 
OP: Вот некоторые специфические черты Object Pascal: ссылки на классы, легкие для использования указатели на методы (основа модели обработки событий) и, в частности, свойства (property). Свойство - — это просто имя, скрывающее путь, которым вы получаете доступ к данным или методу. Свойство может проецироваться на прямое чтение или запись данных, а может ссылаться на метод, обеспечивающий доступ. Даже если вы меняете способ доступа к данным, вам не нужно менять вызывающий код (хотя вам нужно будет его перекомпилировать): это делает свойства очень мощным средством инкапсуляции.
 
== Стандарты ==
C++: Стандарт ANSI/ISO C++ явился завершением многотрудных усилий соответствующего комитета. Большинство авторов компиляторов, кажется, пытаются подчиняться стандарту, хотя есть ещё много странностей. Теоретически развитие языка должно на этом закончиться. На практике, инициативы вроде компилятора Borland C++Builder, конечно, не способствуют улучшению ситуации, но многие чувствуют, что C++ очень нуждается в визуальном окружении программирования. В то же время, популярный Visual C++ тянет C++ в другом направлении, например, с явным злоупотреблением макросов. (По моему личному мнению, у каждого языка есть собственная модель развития, и поэтому нет большого смысла в попытках использовать язык для того, для чего он не был предназначен.) Много новых возможностей будут введены новым стандартом C++ 0x.
 
OP: Object Pascal - — язык-собственность, поэтому у него нет стандарта. Borland лицензировал язык для пары продавцов небольших компиляторов на OS/2, но это не оказало большого влияния. Borland расширяет язык с каждым новым выпуском Delphi.
 
Java: Компания-создатель Sun обладает торговой маркой Java. Однако Sun лицензирует его для продавцов других компиляторов, и убедило ISO создать стандарт Java, не создавая специальный комитет, а просто приняв предложения Sun как есть. Кроме формального стандарта, однако, Java требует высокосовместимых JVM. С недавней поры Sun выдвинула инициативу открыть исходные коды Java (OpenJDK) и сделать ее доступной для всех разработчиков в рамках лицензии GPL 2.
== Заключение: Языки и программное окружение ==
 
Как я упоминал в начале, хотя я пытался исследовать эти языки, только сравнивая синтаксические и семантические характеристики, важно рассмотреть их в соответствующем контексте. Языки нацелены на различные потребности, что означает, что они решают разные проблемы разными способами и используются в очень разных средах программирования. Хотя как языки, так и их среда копируют характеристики друг друга, они были сконструированы для разных потребностей, и в этом вы можете убедиться, сравнивая их характеристики.
 
Цель C++ - — мощность и контроль за счет сложности. Целью Delphi является легкое, визуальное программирование (не отказываясь от мощности) и прочная связь с Windows. Цель Java - — мобильность, даже за счет некоторого отказа от скорости, и распределённые приложения или исполняемое содержание WWW (хотя это, конечно, -  — не Microsoft-овский взгляд на Java!).
 
Можно определить, что успех этих трех языков зависит не от технических характеристик, которые я включил в эту статью. Финансовый статус Borland, операционная система управления Microsoft, популярность Sun в мире Internet, тот факт, что Java рассматривается как anti-Microsoft-овский язык, будущее броузеров Паутины и Win32 API, роль и признание модели ActiveX (из-за связанной с ней проблемой безопасности) и три уровня архитектуры Delphi - — вот показатели, которые могли повлиять на ваши выбор сильнее, чем технические элементы. Например, такой хороший язык как Eiffel, у которого Object Pascal и Java взяли не только некоторое вдохновение, никогда не получит реальной доли рынка, хотя он был популярен во многих университетах земного шара.
 
Просто имейте в виду, что "«модный"» становится все более частым словом в компьютерном мире. Как пользователи хотят иметь инструменты этого года (вероятно, по этой причине операционные системы называются по тому году, в котором они выпущены), программисты любят работать с последним языком программирования и первыми овладеть им. Можно наверняка утверждать, что Java - — не последний из языков ООП. Через несколько следующих лет найдется кто-то с новым модным языком, и все прыгнут в этот поезд, думая, что нельзя отставать, и забывая, что большинство программистов в мире всё ещё печатают на клавиатуре на добром старом Cobol! (С последним утверждением можно поспорить.)
 
== Ссылки ==
* [http://www.brpreiss.com/books/opus4/ Data Structures and Algorithms with Object-Oriented Design Patterns in C++/C#/Java/Python/Ruby/ and others]
 
46

правок