Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Создание и удаление объектов
Объект " появляется на свет" в результате вызова специального метода, который инициализирует объект — конструктора.Созданный экземпляр уничтожается другим методом — деструктором: AMyObject: = TMyObject.Create; { действия с созданным объектом } AMyObj ect.Destroy; Но, — скажет внимательный читатель, — ведь объекта еще нет, как мы можем вызывать его методы? Справедливое замечание. Однако обратите внимание, что вызывается метод TMyObject. Create, a нe AMyObject. Create. Есть такие методы (в том числе конструктор), которые успешно работают до (или даже без) создания объекта. О таких методах, называемых методами класса, пойдет речь чуть ниже. Те, кто раньше использовал ООП в работе на C++ и особенно в Turbo Pascal, будьте внимательны: в Object Pascal экземпляры объектов могут быть только динамическими. Это означает, что в приведенном выше фрагменте переменная AMyObject на самом деле является указателем, содержащим адрес объекта. В Object Pascal конструкторов у класса может быть несколько. Общепринято называть конструктор create (в отличие от Borland Pascal 7.0, где конструктор обычно назывался Init, и от C++, где его имя совпадает с именем класса). Типичное название деструктора — Destroy. type TMyObject = class(TObject) MyField: Integer; Constructor Create; Destructor Destroy; Function MyMethod: Integer; end; Для уничтожения экземпляра объекта рекомендуется использовать метод Free, который первоначально проверяет указатель (не равен ли он nil) и только затем вызывает Destroy: AMyObject.Free; До передачи управления телу конструктора происходит собственно создание объекта — под него отводится память, значения всех полей обнуляются. Далее выполняется код конструктора, написанный программистом для инициализации экземпляров данного класса. Таким образом, хотя на первый взгляд синтаксис конструктора схож с вызовом процедуры (не определено возвращаемое значение), но на самом деле конструктор — это функция, возвращающая созданный и проинициализированный объект. Конструктор создает новый экземпляр объекта только в том случае, если перед его именем указано имя класса. Если указать имя уже существующего объекта, он поведет себя по-другому: не создаст новый экземпляр, а только выполнит код, содержащийся в теле конструктора. Чтобы правильно проинициализировать в создаваемом объекте поля, относящиеся к классу-предку, нужно сразу же при входе в конструктор вызвать конструктор предка при помощи зарезервированного слова inherited: constructor TMyObject.Create; begin inherited Create; end; Взяв любой из примеров, прилагаемых к этой книге или поставляемых вместе в Delphi, вы почти не увидите там вызовов конструкторов и деструкторов. Почему? Дело в том, что любой компонент, попавший при визуальном проектировании в ваше приложение из Палитры компонентов, включается в определенную иерархию. Иерархия эта замыкается на форме (класс TForm): для всех ее составных частей конструкторы и деструкторы вызываются автоматически, незримо для программиста. Кто создает и уничтожает формы? Это делает приложение (глобальный объект с именем Application). В файле проекта (с расширением dpr) вы можете увидеть вызовы метода Application. createForm, предназначенного для этой цели. Что же касается объектов, создаваемых вами динамически (во время выполнения приложения), то здесь нужен явный вызов конструктора и метода Free. Инкапсуляция Инкапсуляция – это принцип ООП, объединяющий в одном классе данные и методы, манипулирующие этими данными и защищающие данные класса от внешнего воздействия. Обратимся к примеру из жизни, чтобы понять смысл этого принципа. Представьте себе, что в вашу квартиру пришел знакомый и переставил вашу мебель по своему усмотрению, не считаясь с вашим вкусом. Скорее всего, вам это не понравится, и вы перестанете приглашать этого человека в свой дом. Если же вы для перестановки мебели позовете помощников, то сначала объясните им, что и как надо переставить, тогда в результат их работы вы получите квартиру, обустроенную так, как это нужно вам. Класс также защищает свое состояние (значения свойств) от несанкционированного изменения, это реализуется с помощью раздела класса private. Включенные в privateсвойства доступны только этому объекту, поэтому не могут быть изменены другими объектами. Для доступа к свойствам в раздел класса public включаются методы. Методы проверяют, возможно ли такое изменение состояния объекта под влиянием других объектов, и только после этого изменяют значения переменных состояния. Если переход в новое состояние невозможен, методы оставляют объект в прежнем состоянии. В языках программирования инкапсуля́ ция имеет одно из следующих значений, либо их комбинацию: · языковой механизм ограничения доступа к определённым компонентам объекта; · языковая конструкция, способствующая объединению данных с методами (или другими функциями), обрабатывающими эти данные. Инкапсуляция — один из четырёх важнейших механизмов объектно-ориентированного программирования (наряду с абстракцией, полиморфизмом и наследованием). В то же время, в языках поддерживающих замыкания, инкапсуляция рассматривается как понятие не присущее исключительно объектно-ориентированному программированию. Также, реализации абстрактных типов данных (например, модули) предлагают схожую модель инкапсуляции. Сокрытие реализации целесообразно применять в следующих целях: · предельная локализация изменений при необходимости таких изменений, · прогнозируемость изменений (какие изменения в коде нужно сделать для заданного изменения функциональности) и прогнозируемость последствий изменений. В Delphi для создания скрытых полей или методов их достаточно объявить в секции private. TMyClass = class Private FMyField: Integer; procedure SetMyField( const Value: Integer ); function GetMyField: Integer; Protected Public property MyField: Integer read GetMyField write SetMyField; end; Для создания интерфейса доступа к скрытым полям в Delphi введены свойства. Инкапсуляция и модули Инкапсуляция - одна из специфических особенностей программирования, ориентированного на объекты. Эта особенность предполагает не только возможности " разложения целого на части" (принципа, определяющего основы любого программирования), но и умения " скрывать" частности от общегo (целого). Такой подход позволяет программисту не знать частных деталей реализации програмной системы, осуществлять конструирование из элементов, реализация которых скрыта от него " под оболочкой" модуля. Модуль в этом подходе приобретает роль основного конструктивного элемента, используемого для синтеза и разработки новых систем. Специфические особенности модуля заключаются в следующем: 1) модуль - это автономно компилируемая програмная единица; 2) информационные и управляющие связи между модулями требуют использования в его описании деклараций, которые в совокупности определяют оболочку модуля, регламентирующую такие связи; 3) сборка програмной системы из модулей связана с отдельным технологическим этапом - компоновкой (линковкой) программы. Правила такой компоновки полностью определяются системой модульных оболочек. Концепция оболочки реализуется декларациями импорта/экспорта, регламентирующими, какие объекты, определенные внутри модуля, можно использовать " за его пределами". Подобные декларации могут быть оформлены в разных видах. В Модуле-2, например, для этого используется специальный вид описания модуля - так называемая специфицирующая оболочка (оболочка опpеделений, DEFINITION MODULE). В этой оболочке перечисляются объекты, экспортируемые из модуля, и специфициpуются методы их использования (фактически, действия над объектами). Пpичем, спецификация пpоцедуpных методов пpоводится на уpовне пpогpаммиста, использующего модуль (потpебителя), котоpому пpедставляются только заголовки пpоцедуp для pаботы с экспоpтиpуемыми объектами, но не пpогpаммы их pеализации. Напpимеp: DEFINITION MODULE A; EXPORT QUALIFIED B, C, D; TYPE B; VAR C: B; PROCEDURE D(C: B); END A. В этом примере разрешено использование " за пределами" модуля A трех определенных в нем програмных объектов: типа В, переменной С и процедуры D. Концепция модуля как програмного эквивалента класса объектов пpедполагает использование его как определителя собственной (индивидуальной) алгебры: множества возможных объектов и действий над ними. Такая концепция подразумевает, что в модуле определяется абстрактный тип и методы - процедуры, манипулирующие с объектами этого типа. При этом стиль программирования, ориентированного на объекты, рекомендует экспортировать за пределы модуля только тип и процедуры - создание объектов этого типа должно производиться вне модуля - экспортеpа. Предыдущий пример в этом отношении нарушает такой стиль, разрешая экспорт переменной C. Подобные стилевые особенности экспорта определяются следующими соображениями. Ведь переменная C в приведенном примере - собственная (внутренняя) переменная модуля A, размещенная в его статической памяти. Можно ли менять значение этой переменной за пределами модуля? Или это не соответствует общим " житейским" представлениям об экспорте? И вообще, что можно делать с переменной C за пределами модуля? Если что угодно, то какой смысл заводить C в модуле А? Если действия над C внутpи A регламентированы процедурами A, то целесообразно экспортировать только такой регламент, т.е. процедуры. В любом случае переменная, определенная в одном модуле и используемая в другом, приобретает характер разделяемой переменной - с ней могут работать программы, определенные в различных модулях и, возможно, написанные разными людьми. Конечно, существуют ситуации, когда от такого экспорта невозможно или нецелесообразно отказываться, но, согласитесь, что в некоторых случаях он может быть похож на экспорт станков, которые используются как металлолом. Для идентификации " своих" и " чужих" объектов (принадлежащих другому модулю) могут использоваться две формы импорта/экспорта: квалифицированный и неквалифицированный. Первая форма связана с использованием ключевого слова QUALIFIED в предложении экспорта и позволяет обращаться к экспортируемым объектам только по их " внутреннему" имени, без префиксации именем модуля-экспортера. Вторая форма не требует использования этого ключевого слова, но корректная идентификация экспортируемых объектов в этом случае всегда связана с префиксацией. Например, для использования переменной C за пределами специфицирующей оболочки, определенной выше для модуля A, в случае квалифицированного экспорта достаточно простого именования C, а при неквалифицированном экспорте связано с использованием префиксированного имени A.C. Кроме того, существуют еще две формы экспорта: закрытый и открытый. Открытый экспорт определяет передачу объектов, с которыми за пределами модуля-экспортеpа можно осуществлять любые операции, определенные в языке программирования. В этом отношении открытый экспорт снимает все ограничения, свойственные объектно-ориентированному стилю программирования и разрешает использовать станки не только как металлолом, но и как строительные конструкции, фундаментные блоки или парковые скульптуры. Закрытый экспорт запрещает использование каких-либо операций над экспортируемыми объектами кроме тех, которые определены в модуле-экспортеpе. В этом смысле закрытый экспорт - это " экспорт сырья", " потребительских продуктов" и т.п. Закрытым экспортом обычно экспортируется тип данных, при этом в специфицирующей оболочке модуля отсутствует определение этого типа, он просто декларируется. В приведенном выше примере так экспортируется тип В. Модула-2 разрешает такой экспорт для ссылочных типов и некоторых отрезков типов. Вот, например, как может быть определен экспорт сигналов, используемых для синхронизации квазипараллельных процессов: DEFINITION MODULE SINCHRON; EXPORT QUALIFIED SIGNAL, SEND, WAIT; TYPE SIGNAL; PROCEDURE SEND (VAR S: SIGNAL); PROCEDURE WAIT (VAR S: SIGNAL); END SINCHRON. Закрытость экспорта в этом модуле позволяет его рассматривать как полностью инкапсулиpованное определение абстрактного типа (алгебры) синхpонизиpущих сигналов. Все имманентные свойства объектов-сигналов скрыты от пользователя (в реализующей оболочке модуля - IMPLEMENTATION) и лишь два метода (SEND и WAIT) вынесены на экспорт. Закрытость экспорта разрешает над любыми сигналами, определенными вне SINCHRON, выполнение только двух действий: SEND и WAIT; использование для этого каких-либо других процедур и/или операторов языка невозможно. Реализующие определения и имманентные свойства класса SIGNAL, определенные в модуле IMPLEMENTATION, уточняют определение сигнала SIGNAL = POINTER TO PASPORT (см. pазд.VII) и определяют все детали работы с объектами этого типа. Концепция инкапсуляции и взаимосвязей модулей через импорт-экспорт приводит к тому, что компоновка из модулей программных моделей, основанная на декларациях импорта-экспорта, оказывается связанной с образованием некоторых межмодульных структур, отображающих экспортные связи. Например, ниже приведена иллюстрация такой структуры: ---- ---------> --+ A ¦ ¦ LTTT- ¦ -----> ----¦L---< ---- ¦ --+- --^- --+- ¦ ¦ B ¦ ¦ C +--> --+ D ¦ ¦ L-T-- LT-T- L-T-- ¦ ¦ ¦ ¦ ¦ ¦ --+- ¦ ¦ ¦ L-+ E +--> ---- ¦ ¦ L-T-- ¦ ¦ L----< -----+--------- -----^--- ¦ SYSTEM ¦ L--------- Здесь главный модуль A использует модули B, C, D, E и системный модуль SYSTEM. Стpелки показывают напpавление экспоpта пpогpаммных объектов, инкапсулиpованных в соответствующих модулях. Стpуктуpа связей на этой иллюстpации хаpактеpизуется наличием базовых модулей (из них стpелки только выходят), модулей веpхнего уpовня (он здесь один - A), в котоpые стpелки только входят, путей между базовыми и веpхними модулями (SYSTEM-C-A), (SYSTEM-C-D-A), (SYSTEM-C-D-E-B-A и т.д.) и петель (C-D-E-C). Несмотpя на то, что наличие петель, вообще говоpя, не является фатальным пpи компоновке модели A из модулей нижних уpовней, тем не менее " pазвязка" таких петель связана с некотоpыми пpоблемами. Pеализационно и технологически они pешаются коppектным констpуиpованием последовательности деклаpаций импоpта в модуле A. Методологически же любая петля отpажает некачественную декомпозицию задачи, непpодуманную иеpаpхию понятий и методов, связанных с ее pешением. В этом плане лучшая схема импоpта-экспоpта должна основываться на выделении пpогpаммных слоев, начиная с базового уpовня и кончая веpхним, пpедметно-оpиентиpованным пакетом пpикладных пpогpамм. Пpи этом напpавление стpелок экспоpта должно быть только снизу-ввеpх от базового слоя к веpхним и, pазумеется, петли должны быть исключены. Подобное pасслоение свойств на основе механизмов импоpта-экспоpта и инкапсуляции позволяет вести послойную pазpаботку пpогpамм модулей, отладку на pазных уpовнях и в конечном счете позволяет повысить надежность и коppектность pазpабатываемого пакета пpогpамм. Заключение Объектно-оpиентиpованный подход к pазpаботке пpогpамм и связанный с ним стиль пpогpаммиpования, оpиентиpованный на объекты, основаны на концепции абстpагиpования типов. Модуль как пpогpаммный эквивалент класса объектов, инкапсулиpующий в себе опpеделение такого абстpактного типа, является в этом отношении той констpуктивной единицей в объектно-оpиентиpованном подходе, котоpая позволяет совеpшить естественный пеpеход от тpадиционного пpоцедуpного пpогpаммиpования к констpуиpованию пакетов пpикладных пpогpамм путем их послойной pазpаботки и отладки. Данное пособие, посвященное отдельным аспектам объектно-оpиентиpованного подхода, пpеследует фактически одну цель - сфоpмиpовать у читателя общее пpедставление о паpадигме абстpагиpования, используя для этого пpедставления и теpминологию объектно-оpиентиpованного подхода к pазpаботке пpогpамм. Пособие, pазумеется, не исчеpпывает всех вопpосов и не освещает всех тонкостей пpогpаммиpования, оpиентиpованного на объекты. Более того, пpи написании этого пособия автоp умышленно не оpиентиpовался на конкpетный объектно-оpиентиpованный язык (напpимеp, Smalltalk). Такой подход опpеделяется тем, что специфика pеализации, теpминологии и методологии использования конкpетного языка всегда затушевывает интуитивные, абстpактные начала в пpоцессе pазpаботки пpогpамм, отpывает пользователя от пpивычных категоpий пpогpаммиpования и тем самым поpождает некотоpый психологический баpьеp, а поpою и непpиятие нового подхода. В этом смысле автоp считал для себя важным " сломать" такой баpьеp, показав читателю, что интуитивно легко ощущаемая категоpия объекта является абсолютно естественной для пpогpаммиpования, " впитывает" в себя все аспекты пpоцесса стpуктуpизации и в этом плане логически pазвивает и дополняет обычное пpоцедуpное пpогpаммиpование новыми сpедствами абстpагиpования. Пpоцесс абстpагиpования является неотъемлемой частью логического мышления, и в этом отношении его pазвитие не имеет гpаниц, как и pазвитие пpоцесса познания вообще. Pеализация такого pазвития на основе использования ЭВМ обеспечивает пpи этом не только (и не столько) новые возможности пpогpаммиpования, сколько новые возможности моделиpования сложных объектов pеального миpа. Директивы видимости
Помимо объявлений элементов класса (полей, методов, свойств) описание класса, как правило, содержит несколько директив, которые устанавливают степень видимости элементов класса в программе.Рассмотрим семантику директив видимости.1. Директива private. ТТРегsоп = class Все что объявлено в секции private, доступно только внутри модуля в котором объявлен класс (приватные объявления). Здесь как правило объявляются переменные, в которых хранятся значения свойств, а также методы (процедуры или функции) доступа к ним. Все что объявлено в секции protected, доступно как и в секции private, а также наследникам данного класса (интерфейс разработчика). Здесь можно объявить методы доступа к значениям свойств (если вы хотите позволить изменять эти методы потомкам вашего компенента), а также свойства, методы и события (методы реакции на события) в компонентах типа TCustomXXX. Все что объявлено в секции public, доступно любому пользователю компонента (интерфейс этапа выполнения). Здесь объявляются, как правило методы. В секции published можно объявлять только свойства и события (они объявляются в виде свойств). Они доступны во время проектирования приложения (интерфейс этапа проектирования). При описании нового класса важен разумный компромисс. С одной стороны, требуется скрыть от других методы и поля, представляющие собой внутреннее устройство класса (для этого и придуманы свойства). Маловажные детали на уровне пользователя объекта будут бесполезны и только помешают целостности восприятия. С другой стороны, если слишком ограничить того, кто будет порождать классы-потомки, и не обеспечить ему достаточный набор инструментальных средств и свободу маневра, то он и не станет использовать ваш класс. В модели объектов языка Object Pascal существует механизм доступа к составным частям объекта, определяющий области, где ими можно пользоваться (области видимости). Поля и методы могут относиться к четырем группам (секциям), отличающимся областями видимости. Методы и свойства могут быть общими (секция public), личными (секция private), защищенными (секция protected) и опубликованными (секция published). Есть еще и пятая группа, automated, она ранее использовалась для создания объектов СОМ; теперь она присутствует в языке только для обратной совместимости с программами на Delphi версий 3—5. Области видимости, определяемые первыми тремя директивами, таковы. § Поля, свойства и методы секции public не имеют ограничений на видимость. Они доступны из других функций и методов объектов как в данном модуле, так и во всех прочих, ссылающихся на него. § Поля, свойства и методы, находящиеся в секции private, доступны только в методах класса и в функциях, содержащихся в том же модуле, что и описываемый класс. Такая директива позволяет полностью скрыть детали внутренней реализации класса. Свойства и методы из секции private можно изменять, и это не будет сказываться на программах, работающих с объектами этого класса. Единственный способ для кого-то другого обратиться к ним — переписать заново созданный вами модуль (если, конечно, доступны исходные тексты). § Поля, свойства и методы секции protected также доступны только внутри модуля с описываемым классом. Но — и это главное — они доступны в классах, являющихся потомками данного класса, в том числе и в других модулях. Такие элементы особенно необходимы для разработчиков новых компонентов — потомков уже существующих. Оставляя свободу модернизации класса, они все же скрывают детали реализации от того, кто только пользуется объектами этого класса. § Три области видимости — private, protected, public — как бы упорядочены по возрастанию видимости методов. В классах-потомках можно повысить видимость методов и свойств, но не понизить ее. При описании дочернего класса можно переносить методы и свойства из одной сферы видимости в другую, не переписывая их заново и даже не описывая — достаточно упомянуть о нем в другом месте: § type § TFirstObj = class § private § FNumber: Integer; § protected § property Number: Integer read: FNumber; § end; §... § TSecondObj = class(TFirstObj) § published § property Number; § end; § Если какое-либо свойство объекта из состава VCL принадлежит к области public, вернуть его в private невозможно. Напротив, обратная процедура широко практикуется в Delphi. У многих компонентов (например, TEdit) есть предок (в данном случае TCustomEdit), который отличается только отсутствием опубликованных свойств. Так что, если вы хотите создать новый редактирующий компонент, порождайте его на базе TCustomEdit и публикуйте только те свойства, которые считаете нужными. Разумеется, если вы поместили свойство в область private, " достать" его оттуда в потомках возможности уже нет.
Популярное:
|
Последнее изменение этой страницы: 2016-07-14; Просмотров: 1044; Нарушение авторского права страницы