Компьютерология - Информационный ресурс

Что такое interface в delphi. Лучшие IT-решения для бизнеса. Как мы работаем

Объектно-ориентированное программирование (ООП), помимо понятия класса, предусматривает также фундаментальное понятие интерфейса.

Что же такое интерфейс и каковы особенности работы с ним в языке программирования Delphi?

Интерфейс - семантическая и синтаксическая конструкция в коде программы, используемая для специфицирования услуг, предоставляемых классом или компонентом (Википедия).

По сути, интерфейс определяет перечень свойств и методов, которые должны использоваться при работе с классом, который этот интерфейс реализует, а также их сигнатуру (название, тип данных, принимаемые параметры (для процедур и функций) и т.д.). Таким образом, класс, реализующий тот или иной интерфейс, обязательно должен реализовать все его компоненты. При чём в строгом соответствии с тем как они в нём описаны.

Очень часто интерфейсы сравнивают с абстрактными классами, но при всей схожести это сравнение не совсем корректно. В абстрактных классах доступно, как минимум, управление видимостью членов. В то же время для интерфейсов области видимости не определены.

Интерфейсы позволяют сделать архитектуру более гибкой, так как унифицируют доступ к тому или иному функционалу, а также позволяют избежать целого ряда проблем связанных с наследованием классов (интерфейсы также могут наследоваться один от другого).

Для объявления интерфейса в Delphi служит ключевое слово interface. Это тоже самое ключевое слово, что определяет секцию модуля, к которой возможно обратиться из вне (между ключевыми словами interface и implementation). Однако при объявлении интерфейса применяется другой синтаксис, схожий с объявлением классов.

Delphi/Pascal

IMyNewInterface = interface procedure InterfaceProc; end;

IMyNewInterface =interface

procedure InterfaceProc ;

end ;

Таким образом, сам синтаксис объявления интерфейса не имеет принципиальных отличий от других языков программирования (особенности синтаксиса основанного на Pascal не в счёт). В тоже время, реализация интерфейсов имеет ряд характерных особенностей.

Дело в том, что Delphi интерфейсы изначально были введены для поддержки технологии COM . Поэтому интерфейс IInterface, который в Delphi является предком для всех других интерфейсов (своего рода аналог TObject), уже содержит в себе три базовых метода по работе с этой технологией: QueryInterface, _AddRef, _Release. Как результат, если класс реализует любой интерфейс, то он должен в обязательном порядке реализовать и эти методы. Даже, если этот класс не предназначен для работы COM.

Вследствие данной особенности интерфейса IInterface, в Delphi использование интерфейсов, в большинстве случаев, приводит к добавлению в класс заведомо не используемых возможностей.

Существует библиотечный класс TInterfaceObject, который уже содержит реализацию этих методов и, при наследовании от него, необходимость реализовывать их самостоятельно отпадает. Но так как Delphi не поддерживает множественное наследование классов, его применение часто только вызывает дополнительные сложности в проектировании и реализации уже требуемого функционала.

Всё это привело к тому, что, несмотря на все возможности предоставляемые интерфейсами, их практическое применение в Delphi почти не вышло за пределы работы с COM.

Будучи оптимизированными для работы главным образом с этой технологией, интерфейсы, а точнее добавляемые ими в обязательном порядке функционал и архитектурные ограничения, при решении других задач себя не оправдывают.

Поэтому, многие Delphi программисты до сих пор, по факту, лишены мощного и гибкого инструмента для разработки архитектуры приложений.

Создание интерфейса пользователя сводится к выбору из палитры компонентов необходимые для работы программы компоненты Delphi, служащие интерфейсом управления, а также интерфейсом отображения информации, и перенесение их на Форму с последующей компоновкой.

Интерфейс пользователя, создаваемый Вами, должен использовать стандартные, привычные пользователям элементы, и обеспечивать максимальное удобство. Всё это в конечном счёте определяется таким критерием как эффективность интерфейса - максимальный результат с минимальными усилиями.
Принципы создания удобного интерфейса известны. В качестве самых общих принципов при создании пользовательских интерфейсов можно рассматривать три основных положения:

  1. Программа должна помогать выполнить задачу, а не становиться этой задачей.
  2. При работе с программой пользователь не должен ощущать себя дураком.
  3. Программа должна работать так, чтобы пользователь не считал компьютер дураком.
Первый принцип - это так называемая "прозрачность" интерфейса. Интерфейс пользователя должен быть интуитивно понятным, простым для освоения, и не создавать для пользователя проблем, которые он вынужден будет преодолевать в процессе работы. Используйте стандартные, без излишнего украшательства компоненты, применяйте привычные, используемые аналогичными программами приёмы управления, и Вы достигнете критериев выполнения первого принципа .

Второй принцип заключается в пренебрежении интеллектуальными способностями пользователей. На собственном опыте мне известно, что часто пользователи не только не умеют работать за компьютером, но и просто боятся предпринять что-либо самостоятельно. Поэтому интерфейс пользователя должен быть максимально дружественным.
Тем более, что опасения пользователей зачастую оправданны, ведь стоимость программы, да и самого компьютера не идёт ни в какое сравнение со стоимостью, например, созданной многолетними усилиями базы данных. Именно поэтому программист при создании интерфейса пользователя обязан всегда встраивать в программу "защиту от дурака" - от неправильных действий и ввода пользователем неверных данных. Но некоторые программисты чересчур увлекаются такой защитой, делают ее слишком назойливой, и в результате работа программы напоминает знаменитое "шаг влево, шаг вправо считается побегом"! И то, что программист создаёт как решение проблемы, само начинает создавать проблемы.
Для соблюдения второго принципа не нужно позволять программе "исправлять" действия пользователя и указывать, что как именно ему действовать, загоняя в узкие рамки. Также не следует чрезмерно увлекаться выводом информационных сообщений-подсказок, особенно диалоговых, это отвлекает пользователя от работы. А лучше вообще предусмотреть возможность отключения подсказок.

Третий принцип заключается в том, чтобы создавать программу с максимально возможными "умственными" способностями. Несмотря на быстрое развитие компьютерной техники, даже широко распространённые программы лишь весьма условно можно назвать имеющими искуственный интеллект. Они мешают работе пользователя, выводя на экран диалоговые окна с глупыми вопросами, вызывающими недоумение даже в простейших ситуациях. В результате пользователи в сердцах восклицают: "Ну и тупая же эта машина"!
У меня лично вызывают раздражение постоянные вопросы практически всех текстовых редакторов о том, не сохранить ли изменённый текст, хотя первоначальный и нынешний текст не отличаются ни символом. Да, я что-то набирал, но потом вернул всё назад, неужели нельзя сообразить! Приходится проверять, не испортил ли я всё-таки что-нибудь.

Старайтесь придерживаться следующих правил:

Стандартные элементы интерфейса
Используйте стандартные для данного элемента интерфейса компоненты. Встретив Вашу программу, пользователь не будет терять время на знакомство, а сразу приступит к работе - это один из признаков профессионально сделанной программы.
Небольшая палитра инструментов
Старайтесь использовать не слишком большое количество разнообразных компонентов. И естественно, использовав где-то в одном месте один стандартный компонент, в аналогичном случае также применяйте именно его.
Одинаковое расстояние между элементами управления
Располагайте элементы интерфейса на одинаковом расстоянии между собой. Разбросанные как попало компоненты создают ощущение непрофессионально сделанного продукта. И наоборот, тщательно выверенное размещение на Форме кнопок, переключателей, флажков и других компонентов, составляющих интерфейс - признак качественной работы.
TabOrder. "Правильный" порядок
TabOrder - это порядок перемещения экранного курсора по элементам управления при нажатии клавиши Tab . В правильно написанной программе курсор перемещается, следуя логике работы пользователя с программой. При создании же программы программист часто меняет компоненты, одни удаляет, другие добавляет по мере необходимости. В результате в готовой программе курсор хаотично скачет по Форме. Завершив программу, не забывайте настроить TabOrder .
Выбор шрифтов
Шрифты просто оставьте в покое. Заданные по умолчанию самой Delphi шрифты подойдут для любой системы, на которой может работать Ваша программа. Полужирный шрифт используйте только для выделения важных элементов. Применение же курсива и особенно подчёркивания , которое пользователь может принять за гиперссылку - дурной тон.
Выбор цветов
Что касается цветов элементов интерфейса, то также, как и в случае со шрифтами, лучше оставьте их стандартными, по умолчанию. Delphi использует системную палитру Windows, и пользователь, изменив её, легко настроит цвета под себя.
Альтернативное управление
Профессионально сделанная программа должна иметь возможность управляться не только мышкой, но и с клавиатуры. Не должно быть функций, доступных выполнению только мышью (рисование в графических редакторах не в счёт!). Для наиболее используемых функций следует предусмотреть "горячие клавиши" для их быстрого вызова.
Кирпичики интерфейса
Что касается конкретных элементов интефейса пользователя, то качество взаимодействия пользователя с программой зависит от:
  • соответствия элемента управления выполняемой им задаче;
  • правил, по которым функционирует элемент управления.
    На этой страничке рассматриваются правила создания некоторых элементов интерфейса .
А сейчас хочу показать, какие инструменты предлагает Delphi для управления компонентами на Форме, их взаимным расположением и поведением курсора при нажатии клавиши Tab .

Для того, чтобы расположить относительно друг друга компоненты в правильном порядке, сначала необходимо их выделить . Можно просто обвести мышкой область на Форме, в которой содержатся выбранные компоненты. Или, удерживая "Shift ", указать той же мышкой каждый подлежащий выделению компонент. Повторный щелчок мышкой по выделенному компоненту (при нажатом "Shift ") снимает с него выделение.

Выделенными компонентами можно управлять как единым целым - передвигать по Форме, присвоить значение одинаковым свойствам, скопировать (для установки, например, на другую Форму), даже удалить.

Теперь щёлкните правой кнопкой по одному из компонентов, и из "всплывающего" меню выберите Position -> Align... Появится диалоговое окошко, позволяющее настроить положение компонентов в группе по горизонтали и вертикали. Например, нам нужно выровнять наши четыре кнопки по левому краю и сделать так, чтобы между ними было одинаковое расстояние по вертикали. Для этого выделим радиокнопки Horizontal: Left sides и Vertikal: Space equally .

Выбрав пункт Center , мы расположим компоненты так, что их центры будут располагаться на одной линии по горизонтали или вертикали, а пункт Center in window перемещает компоненты в центр окна, также по горизонтали или вертикали.

В этом же меню строка Tab O rder... вызывает появление диалогового окна, управляющего перемещением курсора по элементам интерфейса при нажатии клавиши Tab . В момент появления Формы на экране курсор будет находиться, естественно, на компоненте, располагающемся на первой строчке диалогового окна. И далее будет перемещаться вниз по списку. На диалоговом окне две синие стрелочки "вверх" и "вниз" управляют положением выделенного компонента. Выделяйте нужный компонент, стрелками перемещайте на нужную строчку в списке, и так далее.

При выборе пункта меню Control -> появляется подменю, состоящее из двех пунктов:

  • Bring to Front
  • Send to Back
Это методы компонента, доступные также программно. Button1.SendToBack перемещает кнопку на "задний план", а Button1.BringToFront - на "передний план". То есть, если один компонент располагается над другим, эти методы меняют их местами. Случаи, в которых это может применяться, довольно очевидны. Это статья по мотивам вопросов на форумах: "Как мне вернуть строку из DLL?", "Как передать и вернуть массив записей?", "Как передать в DLL форму?".

Чтобы вам не тратить половину жизни на разобраться - в этой статье я принесу всё на блюдечке.

Темы этой статьи в разной степени уже не раз затрагивались в этом блоге, но в этой статье они собраны в кучу, приведены обоснования. Короче, ссылкой на эту статью можно кидаться в тех, кто разрабатывает DLL.

Важное примечание : статью нужно читать последовательно . Примеры кода приводятся только как примеры , на каждом шаге (пункте) статьи код примеров добавляется новыми подробностями. Например, в самом начале статьи нет обработки ошибок, указываются "классические" способы (типа, использования GetLastError , соглашения sdtcall и т.д.), которые по ходу статьи заменяются на более адекватные. Сделано так по той причине, чтобы "новые" ("необычные") конструкции не вызывали вопросов. Иначе при пришлось бы к каждому примеру вставлять примечание вида: "вот это обсуждается в том пункте ниже, а вот то - в этом вот". В любом случае в конце статьи есть ссылка на уже готовый код, написанный с учётом всего сказанного в статье. Можете просто его брать и использовать. А статья объясняет зачем и почему. Если вам не интересно "зачем и почему" - листайте в конец к заключению и ссылке на скачивание примера.

Статья написана по результатам разборов программ, написанных молодыми разработчиками нашей группы.

Правильно расставляем последовательность переключения компонентов

Многие пользователи, особенно те, кто ранее работал в ДОС, имеют привычку переключаться между полями ввода не мышкой, а с помощью клавиатуры клавишей Tab. К тому же, это гораздо быстрее, чем выделять каждое поле мышью. Поэтому порядок переключения компонентов должен быть выставлен правильно. Это касается как компонентов внутри всех компонентов-контейнеров (панелей, GroupBox-ов и им подобных), так и самих компонентов-контейнеров, если их на форме несколько.

Порядок переключения компонентов внутри контейнера задается свойством TabOrder. Первым становится активным компонент, у которого TabOrder равен 0, вторым с 1 и т.д., пока не будут перебраны все компоненты. Кроме этого, у компонента есть свойство TabStop, которое показывает, будет ли компонент получать фокус при переключении клавишей Tab. Если нужно запретить переключение на какой-либо компонент, поставьте у него TabStop = false. В этом случае переключиться на данный компонент можно будет только с помощью мыши.

Бывают случаи, когда пользователи, привыкшие переключаться определенной клавишей в одной программе, по привычке продолжают пользоваться ей и в остальных. Часто это происходит с пользователями 1С, где для перехода по полям ввода может использоваться клавиша Enter. Что ж, дадим им такую возможность в наших программах, если они об этом просят. Устанавливаем у формы свойство KeyPreview в true и пишем обработчик события OnKeyPress:

Procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
if ord(key)=vk_Return then
Form1.SelectNext(PriemForm.ActiveControl, true, true);
end;

Такой обработчик обеспечивает переход по элементам формы при нажатии клавиши Enter. Следует заметить, что подобный метод не будет работать с кнопками, т.к. нажатие Enter на кнопке приводит к ее нажатию, тогда как нажатие Tab передает фокус ввода следующему в последовательности переключения компоненту.

Кнопки по умолчанию

Все те же пользователи довольно быстро привыкают, что в диалоговых окнах приложений, как правило, клавишей Enter можно подтвердить свой выбор, а клавишей Esc - отменить. Давайте не будем их разочаровывать в наших программах, тем более что сделать это очень просто. Для кнопки, реагирующей на Enter, устанавливаем свойство Default в true. Для кнопки, реагирующей на Esc, устанавливаем свойство Cancel в true. И все.

Да или нет

Все диалоговые окна, запрашивающие действия пользователя, должны иметь, по крайней мере, две кнопки: подтверждения действия и отказа от действия (Да/Нет, Сохранить/Отменить и т.п.). Отказ от действия может осуществляться закрытием окна кнопкой [X] в заголовке окна. Недопустимо, если имеется только одна кнопка для подтверждения действия, а для отказа предполагается закрывать окно кнопкой [X] в заголовке, либо возможность отказа вообще отсутствует. Это путает пользователя, вызывая логичный вопрос: а как отказаться?

Также не забываем о том, что было сказано выше в пункте "Кнопки по умолчанию".

Все диалоговые окна должны открываться по центру экрана

По центру, а не там, где они были созданы в режиме проектирования. Во-первых, это нагляднее, а во-вторых, автоматически устраняет проблему разного разрешения экрана у разных пользователей.

Исключение делается в том случае, если диалоговое окно не модально, и по результатам работы пользователя в этом окне сразу происходят изменения в основном окне (например, фильтрация набора данных, перерисовка графиков и т.п.).

Размеры окон не должны превышать размеров экрана

Ни в коем случае. Это безобразие, когда часть окна вылезает за экран. Данное требование не зависит от разрешения экрана у пользователя, т.е. отговорки типа "Пусть поставят большее разрешение" не проходят.

Корректное изменение размеров оконных элементов

Оконные элементы должны корректно изменять свои размеры или перемещаться при изменении размеров окна, при максимизации окна и при восстановлении окна после максимизации.

Все всегда видно

Уменьшение размеров окна не должно приводить к исчезновению оконных элементов и желательно не должно приводить к появлению полос прокрутки (scroller-ов) самого окна. Можно ограничивать минимальные размеры окна таким образом, чтобы все элементы были видны и доступны. Если нет возможности разместить компоненты таким образом, чтобы все были видны в окне, можно использовать закладки (типа PageControl) для разбиения компонентов на группы. Отговорки по поводу разрешения экрана тоже не пропускаем.

Хинты везде, хинты всегда

Для кнопок, особенно на панелях инструментов (типа ToolBar) должны быть заданы подсказки (hint), чтобы всегда было понятно, зачем нужна та или иная кнопка.

Цветовая гамма

Не стоит раскрашивать компоненты на форме во все цвета радуги. Это утомляет глаза и рассеивает внимание пользователя. Это не выглядит "круто". Выделение цветом используется в том случае, когда надо привлечь внимание пользователя к какому-то определенному элементу или определенной части окна. Например, раскрасить светло-красным цветом записи, в которых присутствуют ошибки или, наоборот, светло-зеленым цветом записи, проверка которых прошла успешно.

Заключение

Есть очень хороший метод, который позволяет найти недостатки программы вообще и интерфейса в частности. Он прост: представьте себя на месте пользователя и в течение получаса попытайтесь работать так, как работает он. Еще лучше, если ваш пользователь в пределах досягаемости (например, работает в той же организации). В таком случае сядьте рядом с ним, а лучше вместо него, и попробуйте сделать его работу. Вносить данные, изменять их, выводить отчеты и т.д. Если вы не знаете, как сделать правильно, спросите у своего пользователя. Сделайте не одну-две однотипные операции, как в отладочном режиме, а 20-30, а то и более различных операций, в разном порядке. Забудьте что-нибудь ввести или введите неправильно и посмотрите, как программа на это отреагирует. Вы достаточно быстро увидите слабые места вашей программы.

Автор статьи автоматизировал работу приемной комиссии в ВУЗе, и в первый год внедрения программы по 3-4 часа в день проводил в приемной комиссии, регистрируя абитуриентов, заполняя их персональные данные и выдавая им отчеты о сдаче экзаменов. А в оставшееся рабочее время исправлял ошибки и недочеты. Поверьте, на следующий год проблем практически не осталось. Аналогично было и при внедрении кадрового модуля.

Таким образом, помните об удобстве работы для пользователей. Пусть им будет легко и приятно работать с вашими программами.