Re[6]: Расскажите про интерфейсы
От: Kswapd Россия  
Дата: 20.01.19 20:04
Оценка:
_NN>А чем интерфейс не чистый ?
_NN>В любом случае определить метод никто не запрещает.

Тем, что может содержать не только абстрактные функции. А возможность определения дефолтных методов сводит интерфейс к банальному классу, как в C++. Нарушается чистота дизайна, абстракции "протекают". Наследование реализации вообще зло.
Re[7]: Расскажите про интерфейсы
От: _NN_ www.nemerleweb.com
Дата: 21.01.19 07:09
Оценка:
Здравствуйте, Kswapd, Вы писали:

_NN>>А чем интерфейс не чистый ?

_NN>>В любом случае определить метод никто не запрещает.

K>Тем, что может содержать не только абстрактные функции. А возможность определения дефолтных методов сводит интерфейс к банальному классу, как в C++. Нарушается чистота дизайна, абстракции "протекают". Наследование реализации вообще зло.


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

Разве нелогично иметь реализацию по умолчанию для некоторых методов ?
Например:
interface Comparable<T>
{
  boolean lessThan(T left, T right) { return !equalsTo(left, right) && !greaterThan(left, right); }
  boolean greaterThan(T left, T right) { return !equalsTo(left, right) && !lessThan(left, right); }
  boolean equalsTo(T left, T right) { return !lessThan(left, right) && !greaterThan(left, right); }
}


Кстати, методы расширения тоже нарушают чистоту ?
interface MyInterface { int G(); }

static class InterfaceExtensions
{
  public static int F(this MyInterface i) { return i.G() + 1; }
}

// ...
MyInterface i;
i.F();
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[8]: Расскажите про интерфейсы
От: Kswapd Россия  
Дата: 21.01.19 08:02
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Основная проблема это наследование состояния, а его в интерфейсе нет.

_NN>Не хотите реализацию по умолчанию, переопределяйте все методы по старинке. Никто не запрещает.

И зачем тогда методы с реализацией в интерфейсе? Замусоривание синтаксиса, ничего более. Если уж так нужно, можно сделать абстрактный класс. Но вообще не нужно. Интерфейс служит для объявления контракта — всё. Остальное излишне и может только добавить в код избыточную сложность.

_NN>Кстати, методы расширения тоже нарушают чистоту ?

_NN>[c#]
_NN>interface MyInterface { int G(); }

_NN>static class InterfaceExtensions

_NN>{
_NN> public static int F(this MyInterface i) { return i.G() + 1; }
_NN>}

А тут всё нормально: есть чистый интерфейс, есть код подстановки. В функцию F будут передаваться экземпляры конкретных классов, реализующих MyInterface.
Отредактировано 21.01.2019 12:06 Kswapd . Предыдущая версия .
Re[3]: Расскажите про интерфейсы
От: vmpire Россия  
Дата: 21.01.19 10:25
Оценка: +1
Здравствуйте, Khimik, Вы писали:

K>Пока для меня по-прежнему слишком мало конкретики.

Конкретика может появится только с конкретной реализацией, в конкретном языке.
Пока же обсуждаются общме понятия.

K>1) Могут ли у интерфейса быть переменные (поля)?

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

K>2) Может ли интерфейс добавить дополнительную процедуру, например, для конструктора или деструктора класса? Т.е. если класс B наследуется от класса A совместно с интерфейсом I, то можно ли сделать, чтобы при каждом вызове конструктора B, кроме кода собственно конструктора B, inherited конструктора A и inherited предыдущих конструкторов вызывалась ещё какая-то процедура, объявленная в I?

В интерфейсе нет кода, поэтому и конструкторов нет. Перечитайте ещё раз, что я написал. Интерфейс — это просто шаблон, набор признаков для класса. Кода в нём нет и быть не может.
Хотя, метод, объявленный в иентерфейсе, если он есть в классе, может вызываться из конструктора, как и любой другой метод.

Вот ещё пример для понимания:
Допустим, мы говорим (словами, не в языке программирования): "У класса плагина должен быть метод Init() без аргуентов и ничего не возвращающий".
Этот — уже описание интерфейса, но не на языке программирования. Естественно, никакой реализации словесное описание не содержит.
Есоли мы создадим интерфейс на конкретном языке для этого класса, то он точно также не будет содержать реализации.
Для чего же он тогда нужен в языке? Для того, чтобы иметь лучше локументированный код и, в некоторых языках, компилятор будет иметь возможность статически проверить, что вызываемый метод в классе действительно есть.

Абстрактный же класс — это уже реализация. Даже если метод пока не реализован, он уже резервирует под себя ячейку в таблице методов класса.
А если метод не абстрактный, то сразу эту ячейку и заполняет.

Так что, несмотря на внешнюю схожесть, интерфейс и абстрактный класс — совершенно разные понятия.
Путаницу создаёт то, что никто не запрещает применять одно вместо другого (а техлиду потом разгребать архитектуру )
Re[4]: Расскажите про интерфейсы
От: Khimik  
Дата: 21.01.19 12:10
Оценка:
K>>Пока для меня по-прежнему слишком мало конкретики.
V>Конкретика может появится только с конкретной реализацией, в конкретном языке.
V>Пока же обсуждаются общме понятия.

K>>1) Могут ли у интерфейса быть переменные (поля)?

V>Поля — это реализация, поэтому нет. Свойства — могут быть. Впрочем, тут нужно конкретный язык смотреть.

K>>2) Может ли интерфейс добавить дополнительную процедуру, например, для конструктора или деструктора класса? Т.е. если класс B наследуется от класса A совместно с интерфейсом I, то можно ли сделать, чтобы при каждом вызове конструктора B, кроме кода собственно конструктора B, inherited конструктора A и inherited предыдущих конструкторов вызывалась ещё какая-то процедура, объявленная в I?

V>В интерфейсе нет кода, поэтому и конструкторов нет. Перечитайте ещё раз, что я написал. Интерфейс — это просто шаблон, набор признаков для класса. Кода в нём нет и быть не может.
V>Хотя, метод, объявленный в иентерфейсе, если он есть в классе, может вызываться из конструктора, как и любой другой метод.

Вот ещё один мой пример. Предположим, у меня есть несколько классов, содержащих bitmap-ы. Они могут содержать также, например, процедуры и функции для рисования на этих bitmap-ах.
Я хочу добавить к одному из этих классов (назовём его TBitmap32) новое свойство — Z-буфер. Тогда мне нужно переделать функцию SetSize(newwidth,newheight). Эта функция, если я правильно понимаю, динамическая.
Могу ли я объявить интерфейс, который содержит функцию, где задаются размеры этого Z-буфера, и эта функция будет автоматически вызываться (в классе — наследнике TBitmap32 и моего интерфейса) вместе со стандартным SetSize?
Этот пример наверно немного неказистый, но вот ещё один. Могу ли я объявить интерфейс для сборщика мусора, который я описал в этой теме
Автор: Khimik
Дата: 30.11.18
. Мне нужно, чтобы я мог объявить класс, например наследник от TList и от моего интерфейса, так чтобы при каждом вызове конструктора и деструктора этого класса-наследника запускались отдельные процедуры, которые сообщают объекту ProjectGarbageCollector, что такой-то класс создался или был уничтожен?
Как я понимаю, если всё это и возможно, то только через динамические (а не статические) вызовы функций.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[9]: Расскажите про интерфейсы
От: _NN_ www.nemerleweb.com
Дата: 21.01.19 13:42
Оценка:
Здравствуйте, Kswapd, Вы писали:

K>Здравствуйте, _NN_, Вы писали:


_NN>>Основная проблема это наследование состояния, а его в интерфейсе нет.

_NN>>Не хотите реализацию по умолчанию, переопределяйте все методы по старинке. Никто не запрещает.

K>И зачем тогда методы с реализацией в интерфейсе? Замусоривание синтаксиса, ничего более. Если уж так нужно, можно сделать абстрактный класс. Но вообще не нужно. Интерфейс служит для объявления контракта — всё. Остальное излишне и может только добавить в код избыточную сложность.

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

В любом случае вы всегда можете сделать чистый интерфейс без реализации и от него наследовать интерфейс, предоставляющий реализацию по умолчанию.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: Расскажите про интерфейсы
От: scf  
Дата: 21.01.19 13:45
Оценка:
Здравствуйте, Khimik, Вы писали:

K>Я выучил что такое классы, но не осилил интерфейсы. Расскажите пожалуйста понятно, что это такое.


https://ru.wikipedia.org/wiki/Интерфейс_(объектно-ориентированное_программирование)

Читали?
Re[5]: Расскажите про интерфейсы
От: vmpire Россия  
Дата: 21.01.19 14:16
Оценка:
Здравствуйте, Khimik, Вы писали:

K>Вот ещё один мой пример. Предположим, у меня есть несколько классов, содержащих bitmap-ы. Они могут содержать также, например, процедуры и функции для рисования на этих bitmap-ах.

K>Я хочу добавить к одному из этих классов (назовём его TBitmap32) новое свойство — Z-буфер. Тогда мне нужно переделать функцию SetSize(newwidth,newheight). Эта функция, если я правильно понимаю, динамическая.
K>Могу ли я объявить интерфейс, который содержит функцию, где задаются размеры этого Z-буфера, и эта функция будет автоматически вызываться (в классе — наследнике TBitmap32 и моего интерфейса) вместе со стандартным SetSize?
Нет. Интерфейсы — это вообще не про то.
Функцию в интерфейсе вы объявить можете, но автоматически вызываться она от этого не станет.

K>Этот пример наверно немного неказистый, но вот ещё один. Могу ли я объявить интерфейс для сборщика мусора, который я описал в этой теме
Автор: Khimik
Дата: 30.11.18
. Мне нужно, чтобы я мог объявить класс, например наследник от TList и от моего интерфейса, так чтобы при каждом вызове конструктора и деструктора этого класса-наследника запускались отдельные процедуры, которые сообщают объекту ProjectGarbageCollector, что такой-то класс создался или был уничтожен?

Нет. То, что вам нужно, реалтзуется через aspect-oriented programming. Как это реализовать в Delphi — не знаю.

K>Как я понимаю, если всё это и возможно, то только через динамические (а не статические) вызовы функций.
Re[9]: Расскажите про интерфейсы
От: anton_t Россия  
Дата: 25.01.19 10:03
Оценка:
Здравствуйте, Kswapd, Вы писали:

K>Здравствуйте, _NN_, Вы писали:


_NN>>Основная проблема это наследование состояния, а его в интерфейсе нет.

_NN>>Не хотите реализацию по умолчанию, переопределяйте все методы по старинке. Никто не запрещает.

K>И зачем тогда методы с реализацией в интерфейсе? Замусоривание синтаксиса, ничего более. Если уж так нужно, можно сделать абстрактный класс. Но вообще не нужно. Интерфейс служит для объявления контракта — всё. Остальное излишне и может только добавить в код избыточную сложность.


Методы с дефолтной реализацией добавили в интерфейсы для того, чтобы можно было добавлять в существующие интерфейсы новые методы и при этом не сломать существующий код.
Пример:
В джавовской стандартной библиотеке давно есть замечательный интерфейс Map. В нем есть метод put, который помещает в эту мапу значение, связанное с ключем.
Теперь мы хотим добавить в этот интерфейс метод putIfAbsent, который помещает в эту мапу значение, связанное с ключем, только если такого ключа еще нет в мапе.
И не можем. Потому-что иначе сломается куча кода, в котором есть реализация интерфейса Map.
А при добавлении возможности определять в интерфейсах методы с дефолтной реализацией — можем.
Что в Java и сделали в 8-й версси. И в C# тоже не так давно AFAIK.
Re[10]: Расскажите про интерфейсы
От: Khimik  
Дата: 25.01.19 10:10
Оценка:
_>Методы с дефолтной реализацией добавили в интерфейсы для того, чтобы можно было добавлять в существующие интерфейсы новые методы и при этом не сломать существующий код.
_>Пример:
_>В джавовской стандартной библиотеке давно есть замечательный интерфейс Map. В нем есть метод put, который помещает в эту мапу значение, связанное с ключем.
_>Теперь мы хотим добавить в этот интерфейс метод putIfAbsent, который помещает в эту мапу значение, связанное с ключем, только если такого ключа еще нет в мапе.
_>И не можем. Потому-что иначе сломается куча кода, в котором есть реализация интерфейса Map.
_>А при добавлении возможности определять в интерфейсах методы с дефолтной реализацией — можем.
_>Что в Java и сделали в 8-й версси. И в C# тоже не так давно AFAIK.

Должен честно сказать, что я пока практически ничего не понял из того, что вы написали. Т.е. я по-прежнему не знаю, что такое интерфейсы. В этой теме я задал несколько вопросов, на которые мне не ответили. Может поспрашиваю их на других форумах.
Тем не менее, я понял вот что: то, что вы описали, называется "нагромождение рудиментов" или "исторически сложившиеся особенности".
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re: Расскажите про интерфейсы
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.01.19 10:47
Оценка: +1
Здравствуйте, Khimik, Вы писали:

K>
K>var
K>  s: Shared<TStrings>;
K>begin
K>  s := TStringList.Create;
K>  DoSomething(s);
K>end;
K>


K>Я не понимаю суть этого кода. Например где хранится счётчик, сколько элементов s было создано?

Он хранится в экземпляре класса Shared<TStrings>.
K>И в каких участках кода проверяется, не пора ли удалить неиспользуемые элементы?
В end;
Delphi работает с интерфейсами специальным образом — в него встроен подсчёт ссылок.
Каждый раз, как указатель на класс, реализующий IUnknown, инициализируется в какое-то не-null значение, на этом значении вызывается AddRef. А на предыдущем значении, если оно было не-null, вызывается Release.
Release также вызывается на не-null переменных, выходящих из области видимости.

Нужно понимать, что это не имеет никакого отношения к концепции интерфейса в современном ООП.
Это специальная реализация, нарочно заточенная на взаимодействие с COM.

Вот тут есть более-менее детальное описание: https://www.codeproject.com/Articles/1046776/Managing-object-lifetime-in-Delphi
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[11]: Расскажите про интерфейсы
От: anton_t Россия  
Дата: 25.01.19 11:22
Оценка:
Здравствуйте, Khimik, Вы писали:

K>Должен честно сказать, что я пока практически ничего не понял из того, что вы написали. Т.е. я по-прежнему не знаю, что такое интерфейсы. В этой теме я задал несколько вопросов, на которые мне не ответили. Может поспрашиваю их на других форумах.

K>Тем не менее, я понял вот что: то, что вы описали, называется "нагромождение рудиментов" или "исторически сложившиеся особенности".

Зачем навешивать ярлыки на то, что не понял?
Re[2]: Расскажите про интерфейсы
От: vdimas Россия  
Дата: 25.01.19 15:58
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Нужно понимать, что это не имеет никакого отношения к концепции интерфейса в современном ООП.


Почему не имеют отношения?
Самые что ни на есть обычные интерфейсы.


S>Это специальная реализация, нарочно заточенная на взаимодействие с COM.


Просто они в Дельфи реализованы аккурат так же, как в С++, дополнительно к этому унаследованы от IUnknown с соблюдением протокола последнего.
Re[10]: Расскажите про интерфейсы
От: Kswapd Россия  
Дата: 25.01.19 16:48
Оценка:
Здравствуйте, anton_t, Вы писали:

_>В джавовской стандартной библиотеке давно есть замечательный интерфейс Map. В нем есть метод put, который помещает в эту мапу значение, связанное с ключем.

_>Теперь мы хотим добавить в этот интерфейс метод putIfAbsent, который помещает в эту мапу значение, связанное с ключем, только если такого ключа еще нет в мапе.
_>И не можем. Потому-что иначе сломается куча кода, в котором есть реализация интерфейса Map.
_>А при добавлении возможности определять в интерфейсах методы с дефолтной реализацией — можем.

Ну, типичный костыль. В Go намного лучше — композиция интерфейсов. Перефразируя пример, был интерфейс Map. Если хочется добавить putIfAbsent, объявляется новый интерфейс, скажем MapA (имена условны). Если клиент хочет у себя использовать оба, объявляет приватный интерфейс таким образом (композиция):

type fullMap interface {
    Map
    MapA
}


И использует, и старый код ничего не знает. Если новый интерфейс популярен (используется более чем одним пакетом), объявляется public, для чего имя меняется на FullMap. (нравится мне эта фишка — строчные приватные, заглавные публичные)
Re[2]: Расскажите про интерфейсы
От: Khimik  
Дата: 26.01.19 06:01
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Khimik, Вы писали:


K>>
K>>var
K>>  s: Shared<TStrings>;
K>>begin
K>>  s := TStringList.Create;
K>>  DoSomething(s);
K>>end;
K>>


K>>Я не понимаю суть этого кода. Например где хранится счётчик, сколько элементов s было создано?

S>Он хранится в экземпляре класса Shared<TStrings>.

Тогда непонятно, как срабатывает этот счётчик на строке s := TStringList.Create;
Если Shared<TStrings> — это обычный класс, то присвоение s просто присваивает s и ничего более. И конструктор вызывается у класса TStringList, а не у класса Shared<TStrings>. Или тут уже срабатывает некая взаимосвязь кода и среды разработки (как с исключениями, например)?
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[3]: Расскажите про интерфейсы
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.01.19 16:55
Оценка: +1
Здравствуйте, vdimas, Вы писали:

V>Почему не имеют отношения?

V>Самые что ни на есть обычные интерфейсы.
Для "самых обычных" интерфейсов никаких _AddRef и _Release магически не вызывается.
"Самые обычные" интерфейсы обычно не наследуются принудительно от предка, содержащего нетривиальную таблицу методов.

S>>Это специальная реализация, нарочно заточенная на взаимодействие с COM.

V>Просто они в Дельфи реализованы аккурат так же, как в С++, дополнительно к этому унаследованы от IUnknown с соблюдением протокола последнего.
Дополнительно к этому переменные и аргументы интерфейсного типа ведут себя как умные указатели, в отличие от обычных переменных в дельфи.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[4]: Расскажите про интерфейсы
От: vdimas Россия  
Дата: 26.01.19 19:09
Оценка:
Здравствуйте, Sinclair, Вы писали:

V>>Почему не имеют отношения?

V>>Самые что ни на есть обычные интерфейсы.
S>Для "самых обычных" интерфейсов никаких _AddRef и _Release магически не вызывается.

В VB вызывается.


S>"Самые обычные" интерфейсы обычно не наследуются принудительно от предка, содержащего нетривиальную таблицу методов.


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


V>>Просто они в Дельфи реализованы аккурат так же, как в С++, дополнительно к этому унаследованы от IUnknown с соблюдением протокола последнего.

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

В VB так же.

Но я тут согласен лишь частично.
ХЗ как в Дельфи, но VB генерит намного более оптимальный код, чем получается на плюсах с использованием COM-объектов и умных указателей на них.
Вряд ли Дельфи генерит код хуже, поэтому, эти переменные гораздо умнее, чем обычные умные указатели. ))

Но опять и снова — это лишь подкапотные тонкости.
Re[11]: Расскажите про интерфейсы
От: anton_t Россия  
Дата: 27.01.19 10:16
Оценка: +1
Здравствуйте, Kswapd, Вы писали:

K>Здравствуйте, anton_t, Вы писали:


K>Ну, типичный костыль. В Go намного лучше — композиция интерфейсов. Перефразируя пример, был интерфейс Map. Если хочется добавить putIfAbsent, объявляется новый интерфейс, скажем MapA (имена условны). Если клиент хочет у себя использовать оба, объявляет приватный интерфейс таким образом (композиция):


K>
K>type fullMap interface {
K>    Map
K>    MapA
K>}
K>


K>И использует, и старый код ничего не знает. Если новый интерфейс популярен (используется более чем одним пакетом), объявляется public, для чего имя меняется на FullMap. (нравится мне эта фишка — строчные приватные, заглавные публичные)


Тоже самое наследование интерфейсов из Java/C#, вид в профиль. И не решающее проблемы, для которых создавались дефолтные методы, от слова "совсем".
Какое-то восторженное обмызывание Golang-ом и бросание какашек в Java, вместо конструктивного обсуждения, право слово.
Re[12]: Расскажите про интерфейсы
От: Kswapd Россия  
Дата: 27.01.19 11:44
Оценка:
Здравствуйте, anton_t, Вы писали:

_>Тоже самое наследование интерфейсов из Java/C#, вид в профиль. И не решающее проблемы, для которых создавались дефолтные методы, от слова "совсем".

_>Какое-то восторженное обмызывание Golang-ом и бросание какашек в Java, вместо конструктивного обсуждения, право слово.

Приношу свои извинения, никого не хотел обидеть.

Попробуем посмотреть на пример с расширением интерфейса с точки зрения одного из принципов SOLID — OCP, Open/Closed Principle. Одна из его формулировок звучит так: "компонент должен быть открыт для расширения и закрыт для модификации". Добавление дефолтного метода, очевидно — модификация. Композиция старого интерфейса целиком (без изменения) вместе с другим интерфейсом в новый общий интерфейс — явно расширение. Следовательно, дефолтные методы интерфейсов (и вообще добавление новых функций в старый интерфейс на позднем этапе разработки) нарушают OCP. А без нарушения можно было бы добавить интерфейс и заставить новый класс реализовать оба, ведь это нормальный подход в C#/Java.
Re[13]: Расскажите про интерфейсы
От: _NN_ www.nemerleweb.com
Дата: 27.01.19 12:36
Оценка:
Здравствуйте, Kswapd, Вы писали:

K>Попробуем посмотреть на пример с расширением интерфейса с точки зрения одного из принципов SOLID — OCP, Open/Closed Principle. Одна из его формулировок звучит так: "компонент должен быть открыт для расширения и закрыт для модификации". Добавление дефолтного метода, очевидно — модификация. Композиция старого интерфейса целиком (без изменения) вместе с другим интерфейсом в новый общий интерфейс — явно расширение. Следовательно, дефолтные методы интерфейсов (и вообще добавление новых функций в старый интерфейс на позднем этапе разработки) нарушают OCP. А без нарушения можно было бы добавить интерфейс и заставить новый класс реализовать оба, ведь это нормальный подход в C#/Java.


Как именно нарушают ?
Старый код не станет работать по другому ведь.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.