Последние записи
- TChromium (CEF3), сохранение изображений
- Как в Delphi XE обнулить таймер?
- Изменить цвет шрифта TextBox на форме
- Ресайз PNG без потери прозрачности
- Вывод на печать графического файла
- Взаимодействие через командную строку
- Перенести программу из Delphi в Lazarus
- Определить текущую ОС
- Автоматическая смена языка (раскладки клавиатуры)
- Сравнение языков на массивах. Часть 2
Интенсив по Python: Работа с API и фреймворками 24-26 ИЮНЯ 2022. Знаете Python, но хотите расширить свои навыки?
Slurm подготовили для вас особенный продукт! Оставить заявку по ссылке - https://slurm.club/3MeqNEk
Online-курс Java с оплатой после трудоустройства. Каждый выпускник получает предложение о работе
И зарплату на 30% выше ожидаемой, подробнее на сайте академии, ссылка - ttps://clck.ru/fCrQw
17th
Апр
Перебор свойств класса и присвоение им значения в цикле
Я хочу чтобы в цикле перебирались поля объекта «класса» и в них присваивалось соответствующее значение с ассоциативного массива. (читать всё…)
21st
Мар
Создать класс: морская волна
Разработать программу для рисования на форме геометрического объекта, используя объектно-ориентированное программирование. В программе должно быть не менее 1 класса.
Морская волна (синусоида, задается высота и длина, и фаза — положение синусоиды относительно начала координат). (читать всё…)
20th
Мар
Все экземпляры класса
russian-stalker
Как мне перебрать все экземпляры класса MyClass, и оставить тот у которого name равен ‘q1’?
19th
Мар
public/published методы
Altera
Вопрос в следующем:
Экспериментируя с вызовом методов, указатели которых находились в tList я обнаружил, что public и published методы всех экземпляров одного и того же класса (или объекта, не знаю, как правильно), не важно, выделена ли под них (экземпляры классов) память, или нет, имеют один и то-то же адрес, не равный Nil. Вот я и хочу, что-бы вы мне это объяснили, почему так происходит?
2nd
Дек
Пример класса для создания лога программы
3D Hunter
если ваше приложение многопоточное, то лучше завернуть эту функцию в класс, например: (читать всё…)
23rd
Май
Что такое полиморфизм?
Знаю что такое инкапсуляция – это объединения данных(методов),
и кода(переменных) в объект и скрытие реализации объекта от пользователя предоставив ему лишь интерфейс объекта.
Наследование-способность другим классам получать свойства родительского класса.
Не могу въехать что означает понятие Полиморфизм, хотя и прочитал не раз.
Полиморфизм – это объект класса, который можно использовать в коде для разных целей?
30th
Апр
Изменения в языке Дельфи 2010
Задача предназначена для представления краткого обзора нововведений в язык Дельфи (2010) по сравнению с Дельфи 7.
Изменения в языке Дельфи 2010
Utkin
Благодаря активным попыткам компании Embecadero влиять на рынок продуктов разработки программ язык Дельфи быстро развивается, однако это развитие направлено в основном на попытки наверстать все нововведения в современных языках программирования (таких как С#). Никаких принципиально новых разработок и концепций не внедряется.
Директива Inline (появилась в Дельфи 2005)
По аналогии с С++, функции и процедуры теперь могут быть встраиваемыми со всеми вытекающими последствиями. А именно использование данной директивы не гарантирует вставку тела функции вместо ее вызова. Кроме того, существует еще целый ряд ограничений (согласно справочной системе). Эта директива бесполезна:
. при позднем связывании (virtual, dynamic, message);
. для функций и процедур имеющих ассемблерные вставки;
. для конструкторов и деструкторов, там она работать не будет (о чем Вам компилятор обязательно пожалуется);
. для главного блока программы, секций инициализации и финализации модулей;
. если метод класса обращается к членам класса с более низкой видимостью, чем сам метод. Например, если public метод обращается к private методу, то для такого метода inline-подстановка осуществляться не будет;
. для процедур и функций, которые используются в выражениях проверки условия циклов while и repeat.
Как сделать процедуру встроенной?
Procedure Add (var x: Integer; y: Integer); Inline;
Регулировать поведение inline можно следующими директивами:
{$INLINE ON} – по умолчанию включена, разрешает работу Inline;
{$INLINE AUTO} – будет осуществлена попытка встраивание кода функций и процедур, если:
а) они помечены как Inline;
б) если их размер будет менее 32-х байт.
{$INLINE OFF} – не разрешает работу Inline.
Следует отметить, что и в классическом С++ Inline никогда не была высокоэффективным механизмом, а учитывая ограничения, накладываемые компилятором Дельфи, ее использование под большим вопросом.
Перегрузка операторов (появилась в Delphi.Net)
В отличие от С++ перегрузка осуществляется немного по-другому. Для перегрузки операторов перегружается не символ оператора, а его символическое обозначение (сигнатура). Перегружать можно только для операций с экземплярами классов.
Нужно обратить внимание – TRUNC, ROUND, INC, DEC считаются операторами, а не процедурами и функциями.
Вот пример использования:
TMyClass = class
class operator Add(a, b: TMyClass): TMyClass; // Перегрузка сложение для TMyClass
class operator Subtract(a, b: TMyClass): TMyclass; // Вычитание для TMyClass
class operator Implicit(a: Integer): TMyClass; // Неявное преобразование Integer в TMyClass
class operator Implicit(a: TMyClass): Integer; // Неявное преобразование TMyClass в Integer
class operator Explicit(a: Double): TMyClass; // Явное преобразование Double в TMyClass
end;
// Пример описание сигнатуры Add для перегрузки сложения для типа TMyClass
TMyClass.Add(a, b: TMyClass): TMyClass;
begin
…
end;
var
x, y: TMyClassbegin
x := 12; // Неявное преобразование из Integer
y := x + x; // Вызов TMyClass.Add(a, b: TMyClass): TMyClass
b := b + 100; // Вызов TMyClass.Add(b, TMyClass.Implicit(100))
end;
Подробней о перегрузке операторов можно почитать здесь: http://www.realcoding.net/articles/delphinet-peregruzka-operatorov.html
Помощники класса (Class Helpers)
Интересный механизм (ответ Дельфи на расширители классов в С#), призванный решить некоторые проблемы в обход наследования. Служит для дополнения класса новыми методами и свойствами.
type
TMyClass = class
procedure MyProc;
function MyFunc: Integer;
end;
…
procedure TMyClass.MyProc;
var
X: Integer;
begin
X := MyFunc;
end;
function TMyClass.MyFunc: Integer;
begin
…
end;
…
type
TMyClassHelper = class helper for TMyClass
procedure HelloWorld;
function MyFunc: Integer;
end;
…
procedure TMyClassHelper.HelloWorld;
begin
WriteLn(Self.ClassName); // Здесь будет возвращен тип TMyClass, а не TMyClassHelper
end;
function TMyClassHelper.MyFunc: Integer;
begin
…
end;
…
var
X: TMyClass;
Begin
X := TMyClass.Create;
X.MyProc; // Вызов TMyClass.MyProc
X.HelloWorld; // Вызов TMyClassHelper.HelloWorld
X.MyFunc; // Вызов TMyClassHelper.MyFunc
end;
По сути, вариация на тему множественного наследования, но есть одна особенность – помощники класса позволяют дополнять любой существующий класс, без создания нового. Обратите внимание, что механизм помощника класса не использует явного упоминания Self при обращении к полям класса (помогаемого класса). То есть, HelloWorld имеет право обращаться к полям TMyClass (просто в нашем примере их нет). Аналогично TMyClass также имеет доступ к полям TMyClassHelper (в случае, если класс и его помощник объявлены в одном модуле).
С практической точки зрения удобный механизм, кроме одной детали – класс должен иметь только одного помощника, имеет ли он помощника проверить во время выполнения программы нельзя. Если в классе имеется несколько помощников (неважно в каком юните, лишь бы он видел класс), считаться рабочим будет только самый последний из объявленных. Это значит, что если TMyClass уже имел помощника, то будут доступны методы именно TMyClassHelper, поскольку именно он объявлен последним. Таким образом, в лучшем случае, два и более помощника для одного класса вызовут ошибку компиляции, в худшем трудно отлавливаемую ошибку, жалобы программиста на багги в IDE и компиляторе и много потерянного времени. Чем сложней проект, тем трудней будет установить причину ошибки.
С теоретической точки зрения механизм противоречивый – он увеличивает сцепляемость объектов и юнитов между собой. Перед использованием помощника, я должен проверить все модули, из которых доступен данный класс на предмет проверки существования такого помощника (представьте большой проект). Это нарушает принципы инкапсуляции – если раньше перед использованием класса нужно было знать только его интерфейс, то теперь для использования помощников, я должен отслеживать существование класса во всех модулях, где имеется доступ к данному классу. С этого момента механизм интерфейсов уже не играет особой роли, поскольку, обращаясь к объекту какого-либо класса, всегда можно обнаружить такой букет неизвестных методов, что интерфейсная часть класса становится даже вредной. Это нарушает принцип сокрытия данных – благодаря помощникам я могу менять работу своих и чужих классов и могу иметь доступ к его полям (в рамках юнита). Кстати, это ограничение на доступ к полям в рамках юнита также сводит на нет многие его плюсы – проще вписать новые методы в сам класс (или наследовать новый), чем создавать путаницу в классе, юните и проекте.
Записи стали объектами
И теперь имеют свои методы, свойства и конструкторы.
type
TMyRecord = record
type
TInnerColorType = Integer;
var
Red: Integer;
class var
Blue: Integer;
procedure printRed();
constructor Create(val: Integer);
property RedProperty: TInnerColorType read Red write Red;
class property BlueProp: TInnerColorType read Blue write Blue;
end;
constructor TMyRecord.Create(val: Integer);
begin
Red := val;
end;
procedure TMyRecord.printRed;
begin
writeln(’Red: ‘, Red);
end;
Но, сокращенная запись по-прежнему разрешена (поэтому старые проекты должны переноситься и с сокращенной формой записей).
Абстрактные классы
type
TAbstractClass = class abstract
procedure SomeProcedure;
end;
Разрешены полностью абстрактные классы (раньше допускались только конкретные методы), содержащие объявления методов для дальнейшего их перекрытия в потомках.
strict private и strict protected
Строгое private – метод или свойство для класса и невидимое никому, вне класса даже в рамках текущего юнита.
Строгое protected – методы в этой секции будут видимы самому классу и его наследникам.
Таким образом, полное объявление выглядит теперь так
type
TKlass = class(TForm)
strict protected
protected
strict private
private
public
published
automated
end;
Не наследуемые классы
По аналогии с С#, в Дельфи 2010 существуют классы от которых дальнейшее наследование невозможно:
type
TAbstractClass = class sealed
procedure SomeProcedure;
end;
Весьма сомнительное удовольствие для рядового разработчика. Никаких реальных преимуществ такой класс не дает. Точные причины создания такого механизма не известны и преимущества от его использования очень призрачны – наследование не разрушает класса предка, поэтому и запечатывать их особой необходимости нет. Считается, что запечатанные классы работают быстрей обычных (сам не проверял) и они применяются для .NET платформы (сугубо в утилитарных целях – не все обертки над низкоуровневыми операциями, такими как WinApi, можно сделать наследуемыми).
Классовые константы (возникло в Delphi
Классы могут иметь константы – сами классы, а не порождаемые от них объекты.
type
TClassWithConstant = class
public
const SomeConst = ‘This is a class constant’;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ShowMessage(TClassWithConstant.SomeConst);
end;
Классовые типы (возникло в Delphi
Класс может теперь содержать описание типа, которое можно использовать только в пределах данного класса.
type
TClassWithClassType = class
private
type
TRecordWithinAClass = record
SomeField: string;
end;
public
class var
RecordWithinAClass: TRecordWithinAClass;
end;
…
procedure TForm1.FormCreate(Sender: TObject);
begin
TClassWithClassType.RecordWithinAClass.SomeField := ‘This is a field of a class type declaration’;
ShowMessage(TClassWithClassType.RecordWithinAClass.SomeField);
end;
Еще одно сомнительное удовольствие. Описание типа это не конкретная структура, зачем прятать его описание в тело класса?
Классовые переменные (возникло в Delphi
Класс может содержать переменные по аналогии с константами:
Type
X = class (TObject)
Public
Var
Y: Integer;
End;
Пожалуй, единственное, где это может пригодиться это работа с RTTI, вообще классы в Дельфи стали больше напоминать юниты – такие вот юниты внутри юнитов. Обратите внимание, что переменные класса могут находиться в любой секции (секции в данном случае влияют на область видимости данных переменных), тогда как поля класса не могут быть public (в Дельфи 7 могли). Применение статических полей в классе делает Дельфи все более ориентированным в сторону С# (и менее в сторону Паскаля).
Вложенные классы
Теперь классы можно объявлять внутри классов, цель избежать конфликта имен, локализовать все связанные классы между собой:
type
TOuterClass = class
strict private
MyField: Integer;
public
type
TInnerClass = class
public
MyInnerField: Integer;
procedure InnerProc;
end;
procedure OuterProc;
end;
procedure TOuterClass.TInnerClass.InnerProc;
begin
…
end;
Классы все больше перенимают концепцию модулей. Понятно, что данное нововведение дань .Net, но реальной пользы от него опять же не очень много – раньше конфликта имен избегали префиксами A и F, не могу сказать, что новый механизм дал программистам новые возможности. Также как и попытка использовать вложенные классы для складывания всего в одну большую кучу наряду с помощниками классов сильно напоминают лебедь, рак и щуку, растаскивающие Дельфи в разные стороны.
Финальные методы класса
В классах можно создавать виртуальные методы, которые перекрыть нельзя:
TAbstractClass = class abstract
public
procedure Bar; virtual;
end;
TFinalMethodClass = class(TAbstractClass)
public
procedure Bar; override; final;
end;
Переопределить «Bar» уже больше нельзя.
Статические методы класса
У классов могут быть статические методы класса – то есть методы, которые можно вызвать от типа класса. Такие методы не имеют доступа к полям класса (также как и не могут получить Self на конкретный экземпляр данного класса):
type
TMyClass = class
strict private
class var
FX: Integer;
strict protected
// Note: accessors for class properties must be declared class static.
class function GetX: Integer; static;
class procedure SetX(val: Integer); static;
public
class property X: Integer read GetX write SetX;
class procedure StatProc(s: String); static;
end;
TMyClass.X := 17;
TMyClass.StatProc(’Hello’);
Здесь же представлен пример организации свойств классов. Их использование полностью аналогично использованию переменных и констант класса.
for-element-in-collection
Теперь компилятор способен распознавать итерации в контейнерах:
for Element in ArrayExpr do Stmt;
for Element in StringExpr do Stmt;
for Element in SetExpr do Stmt;
for Element in CollectionExpr do Stmt;
Вот развернутый пример:
var
A: Array [1..6] of String;
I: String;
….
for i in A do
begin
Memo1.Lines.Add(i);
end;
Обратите внимание, что I имеет тип String это не индекс массива, а конкретные значения, которые будут получаться из массива. Кое-где конечно автоматизирует, но представьте, что мне нужно написать некий метод, в котором происходит копирование одного массива в другой. Использовать все равно придется стандартный цикл for, либо писать еще один метод – добавление элемента в массив.
Динамическая инициализация массивов
Теперь массивы получили свои конструкторы:
Type
TMas = Array of String;
Var
Mas: TMas;
Mas := TMas.Create(‘Hello’, ’World’, ’!’);
Я, конечно, не против «Create» как конструктора по умолчанию, но уже сейчас из-за отсутствия внятной русскоязычной литературы по данной теме встречаются статьи, в которых авторитетные господа пишут, что конструктор обязательно должен называться Create (речь идет не только о массивах, но также о записях и конструкторах класса). Так вот конструктор должен называться Create только для массивов. Для всех остальных имя конструктора не обязательно должно быть Create (но желательно, особенно для классов).
Дженерики
Шаблоны, они и в С++ шаблоны. Считается что первые шаблоны возникли в С++, но вообще-то они пришли из функционального программирования и правильное их название параметрический полиморфизм. Явление, когда компилятор сам вырабатывает соответствующие варианты кода на основании обобщенного алгоритма:
TList<T> = class
private
FItems: array of T;
FCount: Integer;
procedure Grow(ACapacity: Integer);
function GetItem(AIndex: Integer): T;
procedure SetItem(AIndex: Integer; AValue: T);
public
procedure Add(const AItem: T);
procedure AddRange(const AItems: array of T);
procedure RemoveAt(AIndex: Integer);
procedure Clear;
property Item[AIndex: Integer]: T read GetItem write SetItem; default;
property Count: Integer read FCount;
end;
Вот пример списка содержащего произвольные (но однотипные элементы). Тип элементов определяется на момент объявления переменной:
ilist: TList<Integer>;
То есть мы создали список целых чисел (а можно, к примеру, список строк). Дженерики удобно использовать применительно к алгоритмам контейнеров данных и комбинаторным алгоритмам. Конкретные реализации алгоритмов можно посмотреть в модуле Generics.Collections, где есть TArray, TList, TStack, TQueue, TDictionary, TObjectList, TObjectQueue, TObjectStack, TObjectDictionary и TEnumerator, способные работать с разными типами данных.
Также необходимо отметить особенность дженериков (и шаблонов в С++) – обобщенные алгоритмы экономят время программиста, сокращая только его код, но для каждого типа (для каждой комбинации типов) всегда генерируется новая версия алгоритма (поэтому размер скомпилированных программ увеличивается).
Заключение
Большинство механизмов представленных здесь:
. обеспечивают совместимость с .NET
. дань моде
. попытка угнаться за Microsoft Visual Studio
Язык не содержит принципиальных отличий и мощных механизмов, которые действительно были бы востребованы именно программистами на языке Дельфи. Все нововведения навязаны, искусственны и не всегда соответствуют концепциям ООП. Большое количество противоречивых инструментов может только запутать программистов и в течение ближайших лет можно ожидать некоторого количества критических статей в адрес языка программирования Дельфи.
Комментарий автора
Личные впечатления о среде сложились следующие: сплошные недоделки (да и в 2009-м не лучше), ждать следующую версию наверно не стоит. FrameWork идет в комплекте, ничего доустанавливать не надо. Несмотря на заявленные требования не ниже 1 гигабайта ОЗУ, у меня и при 512-ти с тормозами, но работает.
Ресурсы
. Хроники «айтишника» http://skiminog.livejournal.com/33610.html
. Общество разработчиков Embecadero http://edn.embarcadero.com
. Углубленный материал по перегрузке операторов в Дельфи http://www.realcoding.net/articles/delphinet-peregruzka-operatorov.html.
. Онлайн-перевод англоязычных материалов статьи http://www.translate.ru
Статья из второго выпуска “журнала ПРОграммистов”.
Скачать этот номер можно по ссылке.
Ознакомиться со всеми номерами журнала.
29th
Апр
Рабство программистов
Статья посвящена стереотипному мышлению о высокой эффективности имеющихся механизмов ООП. Для программистов использующих ООП языки. Цель статьи дать углубленное представление о базовых концепциях объектно-ориентированного программирования.
Рабство программистов
ООПрограммист – рядовой муравей, увеличивающий всемирную энтропию путем написания никому не нужного кода
http://absurdopedia.wikia.com/wiki/ООП
Атор Utkin
Каждый из тех, кто знаком с принципами ООП, прекрасно знает о тех преимуществах, удобствах и больших плюсах, которые оно представляет программистам. Но так ли это на самом деле? Все познается в сравнении, преимущества по сравнению с чем?
Что такое ООП?
Да, что такое ООП? Несмотря на значительное время существования данной концепции точного определения ООП не существует и по сей день. Есть определения ООП в рамках конкретных языков программирования, но все они различны, имеют свою терминологию, механизмы использования, особенности реализации и т.д. Любой учебник по ООП даст Вам либо определение в привязке к языку программирования, либо весьма туманное объяснение или же вовсе, с места в карьер, речь пойдет о принципах, определениях класса и объекта и т.д.
Не имея точного определения обучаемый, словно Алиса проваливается в кроличью нору нового для него мира. Особенно остро это ощущается, если уже имел навыки программирования. Но ощущения субъективны, рассмотрим основные концепции ООП под другим углом зрения.
Базовые понятия ООП
Их все знают, это:
. инкапсуляция
. наследование
. полиморфизм
Сейчас также добавляют еще понятие абстракции данных. Рассмотрим их всех по порядку…
Инкапсуляция – это принцип, согласно которому любой класс должен рассматриваться как чёрный ящик — пользователь класса должен видеть и использовать только интерфейсную часть класса (т. е. список декларируемых свойств и методов класса) и не вникать в его внутреннюю реализацию. Поэтому данные принято инкапсулировать в классе таким образом, чтобы доступ к ним по чтению или записи осуществлялся не напрямую, а с помощью методов. Принцип инкапсуляции (теоретически) позволяет минимизировать число связей между классами и, соответственно, упростить независимую реализацию и модификацию классов (определение взято из Википедии http://ru.wikipedia.org/wiki/Объектно-ориентированное_программирование).
Но, инкапсуляция не ноу-хау ООП, она существовала и ранее – это обычное описание функций и процедур. Пример на Паскале:
Function Sum (a, b: Integer): Integer;
Begin
Result := a+b
End;
Чтобы использовать функцию мне не обязательно знать, как она устроена (и в ряде случаев такое знание даже противопоказано), достаточно лишь описание интерфейса Function Sum (a, b: Integer): Integer; Я должен знать, что имя функции Sum, она принимает два параметра типа Integer (важен также порядок их следования), возвращает также Integer, а вот каким образом проводится сложение это уже совершенно безразлично. Еще пример:
Type
TMas = record
Data: Array of Integer;
Count: Integer;
End;
Procedure Sort (var Mas: TMas);
Теперь чтобы отсортировать такой вот массив, мне не нужно знать, как он устроен, сколько в нем элементов и т.д. Я просто передам его процедуре Sort.
Так чем же примечательна инкапсуляция? Справедливости ради, надо отметить, что инкапсуляция как описание указанного выше явления получило признание только в ООП. Потому что такое определение там является одним из главных особенностей построения программ. Никаких преимуществ, в сравнении с теми же структурным или функциональным программированиями, инкапсуляция в ООП не несет.
Однако на этом инкапсуляция не заканчивается:
Сокрытие данных (взят из книги Тимоти Бадд «ООП в действии») – неотделимая часть ООП, управляющая областями видимости. Является логическим продолжением инкапсуляции. Целью сокрытия является невозможность для пользователя узнать или испортить внутреннее состояние объекта. Но это тоже существует в структурном программировании:
Function fact(x: integer): integer;
var
i, n: Integer;
begin
n:=1;
for i:=1 to x do
begin
n:=n*i;
end;
fact:=n;
end;
Разве я могу получить доступ к i и n не в рамках данной функции? Также стоит еще раз внимательно почитать определение – невозможность для пользователя. Если речь идет о программисте, то он испортить может все и вся и никакое сокрытие данных Вам не поможет, по одной простой причине – раз имеются данные, то также и имеются некоторые механизмы для их использования. Поэтому область видимости не защищает данные от ошибок в методах данного класса. Пример на Дельфи:
Type
MyClass = class (TObject)
Protected
MyData: TStringList;
Private
Public
Constructor Create;
Destructor Destroy;
End;
Constructor MyClass.Create;
Begin
Inherited Create
End;
Destructor MyClass.Destroy;
Begin
Inherited Destroy
End;
Опытный программист уже догадался, о чем идет речь – любое обращение к MyData вызовет ошибку, поскольку перед использованием такие объекты нуждаются в инициализации (между прочим одна из распространенных ошибок начинающих программистов). Так что же дают игры с областями видимости?
Если же говорить о методах других объектов, то доступа к MyData они не получат, но согласно принципам ООП они и не должны его получать. Иными словами MyData никогда не должен находиться в секции Public (кстати, Дельфи это позволяет). Доступ к полям класса всегда должен осуществляться через методы либо свойства.
То есть здесь должна быть аналогия с функциями и процедурами структурного программирования – объявление структур данных должно осуществляться аналогично секции var. Это очень важный момент – сокрытие данных в рамках ООП предназначено для программирования программиста, а не реализации алгоритма. Я говорю о сокрытии данных, потому что я боюсь допустить ошибку, которая в рамках структурного программирования не может возникнуть в принципе. Компилятору безразлично, в какой секции находится поле, он выдаст код в любом случае (речь идет о Дельфи версии 7), все ограничения видимости введены для программиста.
Если Вы думаете, что в С++ это невозможно, то сильно ошибаетесь и виной тому указатели (а в С++ указатели не менее важный механизм, нежели механизмы ООП). Имея указатель на класс, можно не только прочесть его приватные поля, но и модифицировать их.
Class Sneaky
{
private:
int safe;
public:
// инициализировать поле safe значением 10
Sneaky () { safe = 10; }
Int &sorry() { return safe; }
}
И далее:
Sneaky x;
x.sorry () = 17;
Инкапсуляция тесно связана с таким понятием как абстрагирование. Это придание объекту характеристик, которые отличают его от всех других объектов, четко определяя его концептуальные границы. Основная идея состоит в том, чтобы отделить способ использования составных объектов данных от деталей их реализации в виде более простых объектов. И здесь снова модули и структуры решают эту задачу без участия ООП – интерфейсы функций, процедур и модулей могут полностью скрывать внутреннее представление по реализации тех или иных задач. Сомневаетесь? Вот пример взаимодействия модулей (Дельфи). Поместите на форму одну кнопку:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Unit2, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
AddX();
Button1.Caption:=IntToStr(GetX());
end;
////////////////////////////////////////////////////////////////////////////////
procedure TForm1.FormCreate(Sender: TObject);
begin
Init();
end;
////////////////////////////////////////////////////////////////////////////////
end.
И подключите второй модуль:
unit Unit2;
interface
procedure Init();
procedure AddX();
function GetX(): Integer;
implementation
var
x: Integer;
procedure Init();
begin
x:=0;
end;
////////////////////////////////////////////////////////////////////////////////
procedure AddX();
begin
x:=x+1;
end;
////////////////////////////////////////////////////////////////////////////////
function GetX(): Integer;
begin
result:=x;
end;
////////////////////////////////////////////////////////////////////////////////
end.
Выполните программу, понажимайте на кнопку. Теперь попробуйте получить доступ к Х без использования функций и процедур из Unit2. Модуль хранит в себе данные, скрывает их представление и ограничивает к ним доступ. При этом в Unit2 нет и намека на класс (и не думайте, что Вы сможете прочесть или изменить Х обычными процедурами и функциями Unit1 (не из формы)).
Наследование
один из четырёх важнейших механизмов объектно-ориентированного программирования, позволяющий описать новый класс на основе уже существующего (родительского), при этом свойства и функциональность родительского класса заимствуются новым классом. Не то чтобы я не согласен с таким определением, но давайте посмотрим очередной пример:
Type
TMas = record
Data: Array of Integer;
Count: Integer;
End;
Type
TGroupMas = record
Data: Array of TMas;
Count: Integer;
End;
Procedure Sort (var Mas: TMas);
Разве для построения TGroupMas я не опираюсь на TMas? Я ведь мог написать определение TGroupMas с нуля, но в моем случае каждый элемент массива Data структуры TGroupMas является ни кем иным как TMas. Более того, мне ничего не стоит написать процедуру сортировки указанного элемента TGroupMas. Все что требуется это лишь правильно передать параметры процедуре Sort.
Таким образом, я описываю новую структуру TGroupMas данных на основании существующей TMas и я мог создать процедуру сортировки элемента массива на основании Sort:
Procedure SortItem (var GropuMas: TGroupMas; Index: Integer);
Begin
Sort (GroupMas.Data[Index]);
End;
При этом согласно определению наследования, я заимствую все свойства TMas и функциональность Sort; и все это в рамках структурного программирования, никакого ООП для этого не требуется. Вот пример, который часто любят давать в учебниках ООП (язык программирования Дельфи):
Type
TMaterial = record
Massa: Integer;
End;
Type
TAnimal = record
Material: TMaterial;
Sort: String;
End;
Type
TMamal = record
Animal: TAnimal;
Family: String;
End;
Type
THuman = record
Mamal: TMamal;
Race: String;
Floor: String;
Name: String;
Family: String;
End;
Type
TProgrammer = record
Human: THuman;
Sertificate: String;
End;
Type
TProgrammer_of_Pascal = record
Programmer: TProgrammer;
IDE: String;
End;
И пусть в меня «кинут камнем», если программист в данной иерархии не является млекопитающим и не обладает его свойствами (имеет массу, принадлежит к определенному виду).
Для использования механизма наследования не требуется использование объектно-ориентированного программирования. Достаточно, чтобы язык имел возможность организации структур данных определяемых программистом.
Множественное наследование полностью аналогично – я могу определить новую структуру – цвет глаз и включить ее в TMamal и тогда программист обретет новые свойства. Кстати, множественное наследование одна из самых известных мозолей ООП, но именно поэтому о ней мы больше упоминать не будем. Цель данной статьи как раз показать те, моменты, о которых говорить не любят.
Полиморфизм
взаимозаменяемость объектов с одинаковым интерфейсом. Язык программирования поддерживает полиморфизм, если классы с одинаковой спецификацией могут иметь различную реализацию — например, реализация класса может быть изменена в процессе наследования. Кратко смысл полиморфизма можно выразить фразой: «Один интерфейс, множество реализаций».
Можно сказать, что полиморфизм одна из самых загадочных концепций ООП. Итак, в некотором роде полиморфизм тесно связан с наследованием. Вы получаете некоторые методы от родительского класса и можете переопределить их функциональность. Адекватного механизма в структурном программировании не существует, но какие собственно выгоды дает полиморфизм? Итак, это выглядит следующим образом – некий класс (допустим программист) имеет в своем составе метод (допустим, пить чай). Программист на Яве переопределяет метод и пьет чай марки Ява под именем метода пить чай. То есть мы подразумеваем, что когда программист на Ява пьют чай, то он пьет чай марки Ява. Стандартно, формально, но теперь вопрос, какие в этом плюсы?
. уменьшение сложности программ;
. позволяет повторно использовать один и тот же код;
. позволяет использовать в потомках одинаковые имена методов для решения схожих задач.
Не густо, ну что, рассмотрим каждый пункт:
0 – честно говоря, так и не увидел, в чем это выражается (хотя упоминается об этом практически везде), концепция структур и модулей достаточна для решения всех задач, которые могут быть решены полиморфизмом. С другой стороны, многочисленные одноименные методы усложняют программу. Результатом полиморфизма являются объекты, которые имеют и одноименные методы и могут работать с разными типами данных. Использование одноименных методов в таком случае не так тривиально, как хотелось бы. Пришлось срочно создавать новую концепцию (вернее сказать, воровать концепцию, поскольку ООП суть ряда заимствований от других парадигм) – RTTI. Проще всего ее можно представить как информацию о типе объекта во время выполнения программы. То есть перед запуском нужного метода используется явное определение того типа данных, с которым предстоит работать. Обычно такая ситуация возникает только в сложных программных объектах, но и полиморфизм в объектах с несложным поведением не имеет смысла и может быть заменен даже обычными операторами селекторами (например case в Дельфи) и введением дополнительных переменных. Более того, RTTI перечеркивает абстрагирование – для решения задачи динамически знать тип данных противопоказано, это увеличивает сцепляемость объектов – их сложней заменять, переносить, модернизировать. RTTI также уменьшает такую возможность полиморфизма, как использование обобщенных алгоритмов (о параметризации речь ниже).
1 – об этом уже упоминалось:
Procedure SortItem (var GropuMas: TGroupMas; Index: Integer);
Begin
Sort (GroupMas.Data[Index]);
End;
Я же не пишу функцию сортировки снова, просто передаю ей нужные параметры. На лицо явное использование уже существующего ранее кода и совершенно без единого класса, поэтому никакого преимущества в сравнении скажем с функциональным программированием здесь нет. Если же речь идет о параметризации, то с каждым новым набором параметров генерируется новая версия этой же функции, поэтому код в таком случае используется не повторно, а каждый раз новый.
2 – Я тоже могу написать кучу модулей для решения каждой задачи (все равно в классах каждое решение надо описывать явно) и вызывать их одноименные функции также через точечный синтаксис (в Дельфи), только в обратном порядке – имя_функции.имя_модуля. И я бы не сказал, что сильно путаюсь в программе, если однотипные действия названы по-разному (причем без разницы два у меня метода или десяток). Если имеется достаточно полное описание методов (те самые интерфейсы, которые ООП также считает своим достижением), и они имеют осмысленные имена, то никаких проблем между вызовом метода ProgrammTea и ProgrammJavaTea не возникает. ООП полностью игнорирует такие подходы как правильное оформление кода и правила именования объектов (хотя в нем же использование одноименных методов считается плюсом).
Организация тысячи то же что и организация одного. Это вопрос организации (Конфуций).
Кроме того, я не считаю, что выбор нужного метода осуществляется компилятором – все действия жестко прописаны в каждом классе, а поскольку любой класс является еще и типом, то он ничего не выбирает, выбирает программист в каком классе, какой чай должен пить объект-программист на этапе переопределения родительского метода. Вот я бы задал список методов вообще без привязки к конкретному классу, просто как набор функций и процедур в отдельном модуле, а уж компилятор сам вызывал бы соответствующий метод, тогда я бы согласился с этим утверждением на все 100. Далее в современные библиотеки классов, обычно содержат более 100 классов, которые могут содержать десятки методов, и все их держать в голове никакой полиморфизм еще никогда не помогал… Без разницы сколько мне надо знать 1000 методов или 10 000, все равно их все помнить в любой момент времени нет необходимости.
Что касается параметризации это действительно мощный механизм, но с теми же симптомами – это не универсальный рецепт, то есть имеет смысл его применять только в ряде случаев (часть из которых может быть решена комбинаторными алгоритмами) и преимущественно к простым типам. Вот пример алгоритма (С++):
// Описание шаблонной функции
template <typename T> T max(T x, T y)
{
if (x < y)
return y;
else
return x;
}
Не трудно догадаться, что она возвращает максимальный элемент из двух указанных, но только в том случае, если программист сможет описать строгое и однозначное сравнение объектов, то есть удобно для типов, которые по умолчанию поддерживаются транслятором. Для сложных типов, определяемых программистом параметризация не дает никаких преимуществ в сравнении с традиционными подходами. Весьма проблематична параметризация, когда в качестве типа выступают объекты классов, хотя, казалось бы, их строгая иерархия, наследование полей и полиморфизм должны как раз способствовать написанию обобщенных алгоритмов для работы с такими типами. Да это возможно, но требует тщательного продумывания иерархии и написание индивидуальных методов для каждого класса может оказаться предпочтительней.… Здесь решение аналогично RTTI, но проблема не в ООП, это попытка перешагнуть через строгое ограничение типов (наряду с типом Variant в Дельфи). Можно провести следующую аналогию – сначала перед бегуном понаставили барьеров и сказали, что теперь он будет учувствовать в беге с препятствиями, а потом пытаются научить его прыгать, выдают ему специальные кроссовки, рекомендуют методики тренировки…
Также не все гладко с перегрузкой операторов – трансляторы языков программирования часто не могут предоставить нужные средства идентификации перегруженных операторов (а те, что могут, сложны и не так эффективны, порождают большой и медлительный код), что вызывает двусмысленность в толковании всего выражения. Самый простой пример операция – (вычитание), дело в том, что она существует как минимум в двух видах это унарная операция и бинарная — -х и х-y (а в некоторых языках есть еще ее различные формы – инфиксная, постфиксная и т.д.). Далее, необходимо определить приоритет операции, скажем, для строк определение подобных операций может быть не так очевидно, как для чисел. Не думайте что сложение в этом случае лучше. Например, сложение строк x + y не эквивалентно y + x.
Также перегрузке свойственны общие беды полиморфизма – она не является обязательным элементом программирования (это значит, что нет алгоритмов, которые невозможно реализовать без использования перегрузки), и может привести к обратному результату – не упрощению, а усложнению программы. Подобно механизму RTTI перегрузка увеличивает связность кода – для понимания работы перегруженного оператора требуется знать тип объекта (или требуется его уточнение для понимания программистом) используемого в конкретной строке кода, отсюда всякие болячки – уменьшение переносимости, сложности при модификации отдельных объектов и т.д.
Это еще один пример, когда не программист создает алгоритм, а язык программирует программиста – Вы должны следовать этому принципу, так как это приносит некоторые удобства, но при этом все умалчивают о том, что это не гарантия преодоления нарастающей сложности программ (и более того, такой механизм сам может являться источником усложнения программы). И еще один момент – полиморфизм не обязательная черта ООП. Это значит, что если Вы напишите программу, использующую классы и объекты, но не использующую полиморфизм, то это все равно будет ООП. Кроме того, если Вам не хочется отказываться от полиморфизма, его можно имитировать в рамках модулей (юнитов) программ.
Что еще, не так как надо
ООП «вещь в себе», и оно не вписывается в ряд задач – например механизм подключения DLL. Да, функция или процедура возвращает значение, но какого типа? Это отследить невозможно (и RTTI Вам не помощник). ООП очень плохо подходит для парсинга структурированных текстов (например, программ) в сравнении с функциональным программированием. ООП не дружит с рекурсией (не то чтобы она там не возможна, просто не эффективна в сравнении с тем же функциональным программированием), которая позволяет упростить представление многих задач. Также нужно учитывать, что программа, составленная без ООП, как правило, быстрей, чем с ним. ООП официально не поддерживает концепцию ленивых вычислений (вообще это черта языков функционального программирования) – потенциальное увеличение производительности, упрощение решения ряда задач.
Итоги
Данная статья вовсе не призывает отказываться от принципов ООП, задача дать более полное представление об некоторых аспектах ООП. Без понимания этих моментов программисты уверены, что ООП существенный шаг вперед (на самом деле существенный шаг вперед – обширные библиотеки, которые в свете новых веяний написаны в стиле ООП), хотя возможно, всего лишь шаг в сторону…
Возьмите параметризацию – не стоит возлагать на нее большие надежды, она существует уже давно в том или ином виде в функциональном программировании (как и весь полиморфизм во всех его проявлениях) и пока не принесла кардинальных изменений в деле создания программ.
Список использованной литературы
. Тимоти Бадд. Объектно-ориентированное программирование в действии. – С.Петербург, Питер, перевод
по лицензии издательства Addison Wesley, 1997
. Г. Шилдт. Теория и практика С++. – С.Петербург, БХВ-Петербург, 2001
. Раздел википедии http://ru.wikipedia.org/wiki/Объектно-ориентированное_программирование
. Раздел абсурдопедии http://absurdopedia.wikia.com/wiki/ООП
. Э. Хювенен, И. Сеппянен. Мир Лиспа. – М., перевод с финского изд.Мир, в 2-х томах, 1990
. Ф. Брукс. Серебряной пули не существует. – М., Символ-Плюс, 1995, юбилейное издание первой книги
1987 года, перевод по лицензии издательства Addison Wesley
Статья из второго выпуска “журнала ПРОграммистов”.
Скачать этот номер можно по ссылке.
Ознакомиться со всеми номерами журнала.
Облако меток
css реестр ассемблер timer SaveToFile ShellExecute программы массив советы word MySQL SQL ListView pos random компоненты дата LoadFromFile form база данных сеть html php RichEdit indy строки Win Api tstringlist Image мысли макросы Edit ListBox office C/C++ memo графика StringGrid canvas поиск файл Pascal форма Файлы интернет Microsoft Office Excel excel winapi журнал ПРОграммист DelphiКупить рекламу на сайте за 1000 руб
пишите сюда - alarforum@yandex.ru
Да и по любым другим вопросам пишите на почту
пеллетные котлы
Пеллетный котел Emtas
Наши форумы по программированию:
- Форум Web программирование (веб)
- Delphi форумы
- Форумы C (Си)
- Форум .NET Frameworks (точка нет фреймворки)
- Форум Java (джава)
- Форум низкоуровневое программирование
- Форум VBA (вба)
- Форум OpenGL
- Форум DirectX
- Форум CAD проектирование
- Форум по операционным системам
- Форум Software (Софт)
- Форум Hardware (Компьютерное железо)