Каковы плюсы и минусы использования интерфейсов в Delphi? - PullRequest
28 голосов
/ 01 февраля 2011

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

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

Ответы [ 9 ]

26 голосов
/ 01 февраля 2011

Все, что я могу сейчас придумать:

Плюсы:

  • Четкое разделение между интерфейсом и реализацией
  • Уменьшенные зависимости от единиц *
  • Нескольконаследование
  • Подсчет ссылок (при желании можно отключить)

Минусы:

  • Класс и ссылки на интерфейсы нельзя смешивать (по крайней мере, с подсчетом ссылок))
  • Функции получения и установки требуются для всех свойств
  • Подсчет ссылок не работает с циклическими ссылками
  • Трудности отладки (спасибо Габру и Уоррену за указание на это)
12 голосов
/ 01 февраля 2011

Добавление к ответам еще нескольких преимуществ:

  1. Используйте интерфейсы для представления поведения, и каждая реализация поведения будет реализовывать интерфейс.
  2. Публикация API: Интерфейсы отлично подходят для публикации API. Вы можете опубликовать интерфейс, не выдавая фактическую реализацию. Таким образом, вы можете вносить внутренние структурные изменения, не вызывая проблем у клиентов.
10 голосов
/ 02 февраля 2011

Все, что я говорю, это то, что интерфейсы БЕЗ подсчета ссылок ОЧЕНЬ ВЫСОКИ в моем списке пожеланий для delphi !!!

-> Реальное использование интерфейсов - это объявление интерфейса.Не умение подсчитывать ссылки!

7 голосов
/ 13 февраля 2011

Существуют некоторые НЕДОСТАТОЧНЫЕ минусы интерфейсов, которые я не знаю, считают ли люди при их использовании:

  1. Отладка становится более сложной.Я видел много странных трудностей, связанных с вызовами интерфейсных методов в отладчике.

  2. Интерфейсы в Delphi поставляются с семантикой IUnknown, если вам это нравится или нет, вы застряли с подсчетом ссылок в качестве поддерживаемого интерфейса.И, таким образом, с любыми интерфейсами, созданными в мире Delphi, вы должны быть уверены, что правильно обрабатываете подсчет ссылок, и если вы этого не сделаете, вы получите утечки.Если вы хотите избежать подсчета ссылок, ваш единственный выбор - переопределить addref / decf и фактически ничего не освобождать, но это не без его собственных проблем.Я обнаружил, что более насыщенные интерфейсом кодовые базы имеют некоторые из наиболее трудно обнаруживаемых нарушений доступа и утечек памяти, и это, я думаю, потому что очень трудно объединить семантику refcount и семантику delphi по умолчанию (владелецосвобождает объекты, а никто другой этого не делает, и большинство объектов живет всю жизнь своих родителей.).

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

5 голосов
/ 01 февраля 2011

Я в основном использую интерфейсы, когда хочу, чтобы объекты с различным происхождением предлагали общий сервис.Лучший пример, который я могу вспомнить из собственного опыта, - это интерфейс под названием IClipboard:

IClipboard = interface
  function CopyAvailable: Boolean;
  function PasteAvailable(const Value: string): Boolean;
  function CutAvailable: Boolean;
  function SelectAllAvailable: Boolean;
  procedure Copy;
  procedure Paste(const Value: string);
  procedure Cut;
  procedure SelectAll;
end;

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

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

4 голосов
/ 01 февраля 2011

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

Если вы хотите указать или проверить, поддерживает ли класс набор методов - используйте интерфейсы.

Вы не можете сделать это любым другим способом.

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

3 голосов
/ 10 февраля 2011

Дополнительная заметка на Минусы: Производительность

Я думаю, что многие люди слишком беспечно игнорируют снижение производительности интерфейсов. (Не то чтобы я не люблю и не использую интерфейсы, но вы должны знать, во что вы ввязываетесь). Интерфейсы могут быть дорогими не только из-за попадания _AddRef / _Release (даже если вы просто возвращаете -1), но и из-за того, что свойства ОБЯЗАНЫ иметь метод Get. По моему опыту, большинство свойств в классе имеют прямой доступ для средства чтения (например, свойство Prop1: целочисленное чтение FProp1, запись SetProp1). Если изменить это прямое, то отсутствие штрафного доступа к вызову функции может существенно повлиять на вашу скорость (особенно если вы начинаете добавлять 10 циклов вызовов свойств внутри цикла.

Например, простой цикл с использованием класса

for i := 0 to 99 do
begin
  j := (MyClass.Prop1 + MyClass.Prop2 + MyClass.Prop3) / MyClass.Prop4;
  MyClass.Update;
  // do something with j
end;

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

Штраф _AddRef / _Release, который вы можете улучшить с помощью нескольких советов (я уверен, что есть и другие советы. Это не в моей голове):

  • Используйте WITH или назначайте временную переменную, чтобы понести штраф только в один _AddRef / _Release за блок кода
  • Всегда передавайте интерфейсы, используя ключевое слово const , в функцию (в противном случае вы получаете дополнительный _AddRef / _Release при каждом вызове этой функции.
1 голос
/ 02 февраля 2011

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

Я недавно написал пост на эту тему, который можно найти здесь: http://www.nexusdb.com/support/index.php?q=intf-aggregation(tl; dr: вы можете иметь несколько объектов, каждый из которых реализует интерфейс, а затем собрать их в совокупность, которая для внешнего мира выглядит как единый объект, реализующий все эти интерфейсы)

Возможно, вы захотите взглянутьв ссылках "Основы интерфейса" и "Расширенное использование интерфейса и шаблоны".

1 голос
/ 01 февраля 2011

Единственный случай, когда у нас было для использования интерфейсов (помимо COM / ActiveX), был случай, когда нам требовалось множественное наследование, и интерфейсы были единственным способом получить его.В некоторых других случаях, когда мы пытались использовать интерфейсы, у нас возникали различные виды проблем, в основном с подсчетом ссылок (когда к объекту обращались как в виде экземпляра класса, так и через интерфейс).

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

Обновление: как напомнил Дэвид, с интерфейсами вы получаете множественное наследование только интерфейсов, а не реализации.Но это было хорошо для наших нужд.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...