Re[13]: Расскажите про интерфейсы
От: Sinclair Россия https://github.com/evilguest/
Дата: 28.01.19 14:18
Оценка: 3 (1) +2
Здравствуйте, Kswapd, Вы писали:

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

Вы чего-то увлеклись каким-то заоблачным теоретизированием.
Для чего вообще нужны все эти дефолтные реализации и екстеншн методы?
Для того, чтобы покрыть частый сценарий.
Классика жанра — интерфейс потока байт IWriter.
Понятно, что для его реализации необходимо и достаточно реализовать единственный метод bool WriteByte(byte b).
Клиентам, однако, неудобно пихать в него байты по одному. Хочется иметь метод типа int WriteBytes(byte[] bytes), а также метод int WriteBytes(byte[] bytes, int offset, int count).
Теперь вопрос — куда мы включим эти методы?
В декларацию интерфейса?
Но тогда каждый реализатор будет вынужден писать по три метода. А ведь ещё же есть искушение работать со Span<byte> и ReadOnlySpan<byte>...

Все эти методы тривиально строятся на методе WriteByte. Функционально.
То есть, мы могли бы написать простенький класс WriteWrapper, который наружу выставляет их все, а сам использует только IWriter.WriteByte.
Но на практике мы скорее всего бы потеряли в производительности — в разы. Надо как-то магически давать возможность пользоваться тривиальными реализациями, но в случае необходимости подменять их более эффективными перегрузками.

Хорошо, если у нас есть уверенность в одиночном наследовании. Тогда мы отказываемся от интерфейса, и пилим простейший класс Writer, в котором WriteByte — абстрактный, а остальные методы — виртуальные.
А если у нас такой уверенности нет? т.е. мы не можем потребовать от всех реализаций наследоваться от Writer?

Тогда мы имеем классическое инженерное противоречие. Мы хотим одновременно иметь в интерфейсе IWriter один метод и много методов.
Концепции дефолтных методов и методов-расширений позволяют нам это противоречие решить.
То есть мы пишем w.WriteBytes(...), и это биндится либо к специфичному методу w, либо к дефолтной медленной реализации — в зависимости от того, что имел в виду автор класса, стоящего за w.
С методами-расширениями всё, к сожалению, не так хорошо, т.к. биндинг выполняется в call site на основе статической информации о типе переменной, а не о фактическом типе объекта. Но идея — примерно та же.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[15]: Расскажите про интерфейсы
От: · Великобритания  
Дата: 28.01.19 21:22
Оценка: +1 -1
Здравствуйте, Kswapd, Вы писали:

K> А если объявить три интерфейса: ByteWriter, BytesWriter, ByteCountWriter? Каждый содержит ровно один соответствующий метод. И реализаторы имплементируют тот набор, который им нужен (можно же имплементировать множество интерфейсов). Так не принято делать в C#? В Go, например, это рекомендованный подход; принцип ISP (Interface Segregation Principle), доведённый до логического предела.


вот такой код попробуй переписать:
void writeStuff(Writer w)
{
    writeHeader(w);
    writeBody(w);
    writeFooter(w);
}
void writeHeader(Writer w)
{
    w.write(MAGIC_BYTES);
    w.write(typeByte);
}
void writeBody(Writer w)
{
    w.write(contentArray, contentOffset, contentCount);
}
void writeFooter(Writer w)
{
    w.write(moreStuff, stuffOffset, stuffContent);
    w.write(FOOTER_BYTES);
}



Дефолтные методы интерфейса — круто. Методы-расширения — не нужны.
avalon/2.0.6
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[3]: Расскажите про интерфейсы
От: _NN_ www.nemerleweb.com
Дата: 20.01.19 18:14
Оценка: 13 (1)
Здравствуйте, LaptevVV, Вы писали:

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

vsb>>Это класс, у которого все методы абстрактные.
LVV>Сомневаюсь. В С++ в этом случае можно создать переменную — указатель на объекты такого класса.
LVV>В Яве и Сишарпе с интерфейсами что можно делать, кроме реализации и наследования?

Отстали вы от прогресса.
Вот вам Java 8:

// A simple program to Test Interface default 
// methods in java 
interface TestInterface 
{ 
    // abstract method 
    public void square(int a); 
  
    // default method 
    default void show() 
    { 
      System.out.println("Default Method Executed"); 
    } 
} 

// A simple Java program to TestClassnstrate static 
// methods in java 
interface TestInterface 
{ 
    // abstract method 
    public void square (int a); 
  
    // static method 
    static void show() 
    { 
        System.out.println("Static Method Executed"); 
    } 
}
http://rsdn.nemerleweb.com
http://nemerleweb.com
Расскажите про интерфейсы
От: Khimik  
Дата: 18.01.19 10:39
Оценка: :)
Я выучил что такое классы, но не осилил интерфейсы. Расскажите пожалуйста понятно, что это такое.
Я одно время думал, что интерфейс – это такой абстрактный класс, который поддерживает множественное наследование, поэтому его можно добавить к реальному (не абстрактному) классу при наследовании, соответственно расширив набор функций этого класса. А переменные в интерфейсе есть?

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

var
  s: TStrings;
begin
  s := TStringList.Create;
  try
    DoSomething(s);
  finally
    s.Free;
  end;
end;


С помощью интерфейсов этот код можно заменить на такой:

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


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


Я не понимаю суть этого кода. Например где хранится счётчик, сколько элементов s было создано? И в каких участках кода проверяется, не пора ли удалить неиспользуемые элементы?
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[5]: Расскажите про интерфейсы
От: fmiracle  
Дата: 18.01.19 11:59
Оценка: +1
Здравствуйте, LaptevVV, Вы писали:

LVV>Ага. Опять что-то типа принципа подстановки...


Да, для нее они и нужны. Интерфейс это просто описание ЧТО умеет делать данный класс, но без какой-либо реализации.

LVV>Класс, который реализует, является типа наследником от интерфейса.


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

Интерфейсы дают полиморфизм, но не отягощены разделением кода с базовым классом. Переиспользование кода базового класса часто хорошо, а часто только лишний груз и связность.
Re[4]: Расскажите про интерфейсы
От: Kswapd Россия  
Дата: 20.01.19 19:05
Оценка: +1
Здравствуйте, _NN_, Вы писали:

_NN>
_NN>interface TestInterface 
_NN>{ 
_NN>    // abstract method 
_NN>    public void square(int a); 
  
_NN>    // default method 
_NN>    default void show() 
_NN>    { 
_NN>      System.out.println("Default Method Executed"); 
_NN>    } 
_NN>} 
_NN>


Отвратительно . Так испортить гениальную идею чистого интерфейса.
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: Расскажите про интерфейсы
От: 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[3]: Расскажите про интерфейсы
От: Sinclair Россия https://github.com/evilguest/
Дата: 26.01.19 16:55
Оценка: +1
Здравствуйте, vdimas, Вы писали:

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

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

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

V>Просто они в Дельфи реализованы аккурат так же, как в С++, дополнительно к этому унаследованы от IUnknown с соблюдением протокола последнего.
Дополнительно к этому переменные и аргументы интерфейсного типа ведут себя как умные указатели, в отличие от обычных переменных в дельфи.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
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[14]: Расскажите про интерфейсы
От: AlexRK  
Дата: 28.01.19 21:11
Оценка: :)
Здравствуйте, Sinclair, Вы писали:

S>Для чего вообще нужны все эти дефолтные реализации и екстеншн методы?


Полагаю, в качестве довольно кривого костыля, призванного через пень-колоду закрыть дыру под названием "прибитость гвоздями функций к данным". Что, впрочем, является одним из столпов так называемого ООП.
Re: Расскажите про интерфейсы
От: vsb Казахстан  
Дата: 18.01.19 10:47
Оценка:
Здравствуйте, Khimik, Вы писали:

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


Это класс, у которого все методы абстрактные.
Re[2]: Расскажите про интерфейсы
От: LaptevVV Россия  
Дата: 18.01.19 11:02
Оценка:
K>>Я выучил что такое классы, но не осилил интерфейсы. Расскажите пожалуйста понятно, что это такое.
vsb>Это класс, у которого все методы абстрактные.
Сомневаюсь. В С++ в этом случае можно создать переменную — указатель на объекты такого класса.
В Яве и Сишарпе с интерфейсами что можно делать, кроме реализации и наследования?
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re: Расскажите про интерфейсы
От: Maniacal Россия  
Дата: 18.01.19 11:02
Оценка:
Здравствуйте, Khimik, Вы писали:

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

K>Я одно время думал, что интерфейс – это такой абстрактный класс, который поддерживает множественное наследование, поэтому его можно добавить к реальному (не абстрактному) классу при наследовании, соответственно расширив набор функций этого класса. А переменные в интерфейсе есть?

Всё верно. Переменный по идеологии не положено. Properties в интерфейсах можно, это же по сути set/get

K>Вот пример использования интерфейсов для реализации умных указателей, который мне подсказали. Исходный код:

K>[...skipped...]

А вот в примерах не интерфейсы, а дженерики (аналог шаблонов в C++).

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


Счётчик и проверка хранится внутри реализации класса Shared<>
Отредактировано 18.01.2019 11:18 Maniacal . Предыдущая версия .
Re[3]: Расскажите про интерфейсы
От: vsb Казахстан  
Дата: 18.01.19 11:12
Оценка:
Здравствуйте, LaptevVV, Вы писали:

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

vsb>>Это класс, у которого все методы абстрактные.
LVV>Сомневаюсь. В С++ в этом случае можно создать переменную — указатель на объекты такого класса.
LVV>В Яве и Сишарпе с интерфейсами что можно делать, кроме реализации и наследования?

Точно так же создать переменную-указатель (собственно в жаве все переменные или указатели или примитивные числовые типы).

interface MyInterface {
  void foo();
}
class MyImpl implements MyInterface {
  void foo() { ... }
}
MyInterface variable = new MyImpl();
variable.foo();
Re[4]: Расскажите про интерфейсы
От: LaptevVV Россия  
Дата: 18.01.19 11:25
Оценка:
K>>>>Я выучил что такое классы, но не осилил интерфейсы. Расскажите пожалуйста понятно, что это такое.
vsb>>>Это класс, у которого все методы абстрактные.
LVV>>Сомневаюсь. В С++ в этом случае можно создать переменную — указатель на объекты такого класса.
LVV>>В Яве и Сишарпе с интерфейсами что можно делать, кроме реализации и наследования?
vsb>Точно так же создать переменную-указатель (собственно в жаве все переменные или указатели или примитивные числовые типы).
vsb>
vsb>interface MyInterface {
vsb>  void foo();
vsb>}
vsb>class MyImpl implements MyInterface {
vsb>  void foo() { ... }
vsb>}
vsb>MyInterface variable = new MyImpl();
vsb>variable.foo();
vsb>

Ага. Опять что-то типа принципа подстановки...
Класс, который реализует, является типа наследником от интерфейса.
Поэтому можно создавать ссылку на базовый тип, присваивая ей ссыль на производный.
В таком разрезе — да, интерфейс — это абстрактный класс.
В котором запрещены поля-переменные.
Поля константы вроде разрешалось, не?
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re: Расскажите про интерфейсы
От: Qulac Россия  
Дата: 18.01.19 12:03
Оценка:
Здравствуйте, Khimik, Вы писали:

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

K>Я одно время думал, что интерфейс – это такой абстрактный класс, который поддерживает множественное наследование, поэтому его можно добавить к реальному (не абстрактному) классу при наследовании, соответственно расширив набор функций этого класса. А переменные в интерфейсе есть?

K>Вот пример использования интерфейсов для реализации умных указателей, который мне подсказали. Исходный код:


K>
K>var
K>  s: TStrings;
K>begin
K>  s := TStringList.Create;
K>  try
K>    DoSomething(s);
K>  finally
K>    s.Free;
K>  end;
K>end;
K>


K>С помощью интерфейсов этот код можно заменить на такой:


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


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


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


Интерфейс — это контракт в чистом его виде, так как интерфейс не содержит его реализацию. Один класс может реализовывать много таких контрактов, поэтому ограничения на множественное наследование интерфейсов нету. Гугли на тему "контрактное программирование".
Программа – это мысли спрессованные в код
Re[2]: Расскажите про интерфейсы
От: Khimik  
Дата: 18.01.19 12:09
Оценка:
K>>Я не понимаю суть этого кода. Например где хранится счётчик, сколько элементов s было создано? И в каких участках кода проверяется, не пора ли удалить неиспользуемые элементы?

M>Счётчик и проверка хранится внутри реализации класса Shared<>


Вы явно что-то путаете с моими примерами. Когда в последнем коде вызывается s := TStringList.Create; , вообще причём здесь Shared? Обычное присвоение переменной созданного экземпляра класса TStringList.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[6]: Расскажите про интерфейсы
От: _Raz_  
Дата: 18.01.19 12:10
Оценка:
Здравствуйте, fmiracle, Вы писали:

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


Есть и третья сторона — в разговоре об интерфейсах слово "наследование" должно быть под запретом.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Re[2]: Расскажите про интерфейсы
От: GarryIV  
Дата: 18.01.19 12:18
Оценка:
Здравствуйте, vsb, Вы писали:

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

vsb>Это класс, у которого все методы абстрактные.

Это не всегда верно, бывают и не абстрактные методы в интерфейсах. Надо про какой-то конкретный язык говорить а то у каждого свое.
WBR, Igor Evgrafov
Re[3]: Расскажите про интерфейсы
От: Maniacal Россия  
Дата: 18.01.19 12:25
Оценка:
Здравствуйте, Khimik, Вы писали:


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


M>>Счётчик и проверка хранится внутри реализации класса Shared<>


K>Вы явно что-то путаете с моими примерами. Когда в последнем коде вызывается s := TStringList.Create; , вообще причём здесь Shared? Обычное присвоение переменной созданного экземпляра класса TStringList.


А переменная типа Shared<> при этом. В Delphi все объекты это указатели. Свежесозданный указатель помещается в класс-контейнер умного указателя, там запоминается и счётчику присваивается единица. Если в s уже что-то хранилось, то сначала счётчик уменьшится, если станет равным нулю, то память, занимаемая предыдущим объектом удалится. Следующая операция, как описано выше: копирование нового указателя, счётчику присваивается 1.
Re[5]: Расскажите про интерфейсы
От: vsb Казахстан  
Дата: 18.01.19 13:50
Оценка:
Здравствуйте, LaptevVV, Вы писали:

LVV>Поля константы вроде разрешалось, не?


Нет. Ну если не считать таковыми статические константы, но я на это смотрю, как на пространство имён. Грубо говоря MyInterface.SOME_CONSTANT можно заменить по всей программе на MyInterface_SOME_CONSTANT и ничего не изменится.

Тут надо отметить, что в современной Java в интерфейсы накрутили много дополнительного, например можно делать реализации методов по умолчанию, но это уже отступление от идеи чистых интерфейсов на мой взгляд и сделано скорее для удобства, чем для изменения концепции.
Re[4]: Расскажите про интерфейсы
От: Khimik  
Дата: 18.01.19 13:52
Оценка:
Здравствуйте, Maniacal, Вы писали:

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



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


M>>>Счётчик и проверка хранится внутри реализации класса Shared<>


K>>Вы явно что-то путаете с моими примерами. Когда в последнем коде вызывается s := TStringList.Create; , вообще причём здесь Shared? Обычное присвоение переменной созданного экземпляра класса TStringList.


M>А переменная типа Shared<> при этом. В Delphi все объекты это указатели. Свежесозданный указатель помещается в класс-контейнер умного указателя, там запоминается и счётчику присваивается единица. Если в s уже что-то хранилось, то сначала счётчик уменьшится, если станет равным нулю, то память, занимаемая предыдущим объектом удалится. Следующая операция, как описано выше: копирование нового указателя, счётчику присваивается 1.


Мне по-прежнему непонятно.
Пока вот вопрос: умные указатели могут быть реализованы через обычные классы, без интерфейсов?
В последнем примере с Shared<> я совершенно не вижу, где здесь второй объект — счетчик. Я вижу только переменную S, которой присваивается ссылка на создаваемый класс TStringList.
Вот мой пример, как я бы сделал умный счётчик (с моим текущим пониманием всего этого):

var
ProjectPointersCounter:tProjectPointersCounter;
...
var
S:tstringlist;
begin
s:=projectpointerscounter.AddClass(TStringList.Create);
DoSomething(s);

...
projectpointerscounter.CheckEmptyObjects;


И мне всё ещё неясна общая логика, как использовать такую конструкцию, чтобы в другом месте кода обратиться к этому StringList.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[3]: Расскажите про интерфейсы
От: vsb Казахстан  
Дата: 18.01.19 13:52
Оценка:
Здравствуйте, GarryIV, Вы писали:

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

vsb>>Это класс, у которого все методы абстрактные.

GIV>Это не всегда верно, бывают и не абстрактные методы в интерфейсах. Надо про какой-то конкретный язык говорить а то у каждого свое.


Ну тут уже вопрос терминологии. Даже понятие указателя в разных языках отличается. В принципе можно считать интерфейс абстрактным классом без полей. А можно вообще считать, что понятия интерфейса нет, а есть только классы. Хотя в той же Java и C# множественное наследование реализовано только для интерфейсов, поэтому в каком-то смысле они там есть в любом случае.
Re: Расскажите про интерфейсы
От: vmpire Россия  
Дата: 18.01.19 14:00
Оценка:
Здравствуйте, Khimik, Вы писали:

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

K>Я одно время думал, что интерфейс – это такой абстрактный класс, который поддерживает множественное наследование, поэтому его можно добавить к реальному (не абстрактному) классу при наследовании, соответственно расширив набор функций этого класса.
Упрощённо и схематично это вот так:

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

Интерфейс же никуда не приписывается, а задаёт "шаблон", которому должен соответствовать наследник.
Это не наследование реализации и вообще не наследование. Это реализация интерфейса.

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

То есть, если вы говорите "у человека должна быть нога, если она у него есть — то это человек" — это интерфейс.
Если "Вот нога, если её к кому-то приклеить, то то, что получится можно назвать человеком" — это класс.
Если "Вот заготовка для ноги, если её допилить и приклеить к кому-то, то то, что получится, можно назвать человеком" — это абстрактный класс.
Re[2]: Расскажите про интерфейсы
От: Khimik  
Дата: 19.01.19 11:05
Оценка:
Здравствуйте, vmpire, Вы писали:

V>Абстрактный класс (как и вообще класс) — это дополнительный компонент (методы, свойства и т.п.), который автоматически приписывается к своему наследнику.

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

V>Интерфейс же никуда не приписывается, а задаёт "шаблон", которому должен соответствовать наследник.

V>Это не наследование реализации и вообще не наследование. Это реализация интерфейса.

Пока для меня по-прежнему слишком мало конкретики.
1) Могут ли у интерфейса быть переменные (поля)?
2) Может ли интерфейс добавить дополнительную процедуру, например, для конструктора или деструктора класса? Т.е. если класс B наследуется от класса A совместно с интерфейсом I, то можно ли сделать, чтобы при каждом вызове конструктора B, кроме кода собственно конструктора B, inherited конструктора A и inherited предыдущих конструкторов вызывалась ещё какая-то процедура, объявленная в I?
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[5]: Расскажите про интерфейсы
От: _NN_ www.nemerleweb.com
Дата: 20.01.19 19:49
Оценка:
Здравствуйте, Kswapd, Вы писали:

K>Отвратительно . Так испортить гениальную идею чистого интерфейса.

А чем интерфейс не чистый ?
В любом случае определить метод никто не запрещает.
http://rsdn.nemerleweb.com
http://nemerleweb.com
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[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[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[4]: Расскажите про интерфейсы
От: vdimas Россия  
Дата: 26.01.19 19:09
Оценка:
Здравствуйте, Sinclair, Вы писали:

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

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

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


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


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


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

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

В VB так же.

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

Но опять и снова — это лишь подкапотные тонкости.
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
Re[14]: Расскажите про интерфейсы
От: Kswapd Россия  
Дата: 27.01.19 14:05
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Как именно нарушают ?

_NN>Старый код не станет работать по другому ведь.

Только благодаря костылю — возможности добавлять в функцию, объявленную в интерфейсе, какую-то реализацию. Эта возможность противоречит самому духу понятия "интерфейс". И зачем это понадобилось, когда класс реализации всегда мог объявить, что он собирается реализовать и интерфейс Map, и интерфейс Map1 и, возможно, сто других. Вот так языки и движутся в направлении ада: добавляя и добавляя новые фичи, которые просто позволяют сделать что-то, что уже можно было (в данном случае даже изящнее) сделать раньше.
Re[15]: Расскажите про интерфейсы
От: _NN_ www.nemerleweb.com
Дата: 27.01.19 14:20
Оценка:
Здравствуйте, Kswapd, Вы писали:

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


_NN>>Как именно нарушают ?

_NN>>Старый код не станет работать по другому ведь.

K>Только благодаря костылю — возможности добавлять в функцию, объявленную в интерфейсе, какую-то реализацию. Эта возможность противоречит самому духу понятия "интерфейс". И зачем это понадобилось, когда класс реализации всегда мог объявить, что он собирается реализовать и интерфейс Map, и интерфейс Map1 и, возможно, сто других. Вот так языки и движутся в направлении ада: добавляя и добавляя новые фичи, которые просто позволяют сделать что-то, что уже можно было (в данном случае даже изящнее) сделать раньше.


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

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

Кстати, часть методов расширений классов таки перешла в реализацию классов, хотя казалось бы правильней по канонам оставить как методы расширения.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[14]: Расскажите про интерфейсы
От: Kswapd Россия  
Дата: 28.01.19 20:51
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Вы чего-то увлеклись каким-то заоблачным теоретизированием.


Почему бы и нет, в философском подфоруме .

S>Классика жанра — интерфейс потока байт IWriter.

S>Понятно, что для его реализации необходимо и достаточно реализовать единственный метод bool WriteByte(byte b).
S>Клиентам, однако, неудобно пихать в него байты по одному. Хочется иметь метод типа int WriteBytes(byte[] bytes), а также метод int WriteBytes(byte[] bytes, int offset, int count).

А если объявить три интерфейса: ByteWriter, BytesWriter, ByteCountWriter? Каждый содержит ровно один соответствующий метод. И реализаторы имплементируют тот набор, который им нужен (можно же имплементировать множество интерфейсов). Так не принято делать в C#? В Go, например, это рекомендованный подход; принцип ISP (Interface Segregation Principle), доведённый до логического предела.
Re[16]: Расскажите про интерфейсы
От: AlexRK  
Дата: 28.01.19 21:32
Оценка:
Здравствуйте, ·, Вы писали:

·>Дефолтные методы интерфейса — круто. Методы-расширения — не нужны.


Дефолтные методы не заменяют методов расширения.
Re[17]: Расскажите про интерфейсы
От: · Великобритания  
Дата: 28.01.19 21:44
Оценка:
Здравствуйте, AlexRK, Вы писали:

ARK> ·>Дефолтные методы интерфейса — круто. Методы-расширения — не нужны.

ARK> Дефолтные методы не заменяют методов расширения.
Ну да. Разные вещи, понятное дело. Я имею в виду, что дефолтные методы — ничем не заменимы.
Методы расширения — обычные статические методы, только синтаксис немного отличается.
avalon/2.0.6
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[15]: Расскажите про интерфейсы
От: Sinclair Россия https://github.com/evilguest/
Дата: 29.01.19 02:59
Оценка:
Здравствуйте, Kswapd, Вы писали:

K>А если объявить три интерфейса: ByteWriter, BytesWriter, ByteCountWriter? Каждый содержит ровно один соответствующий метод. И реализаторы имплементируют тот набор, который им нужен (можно же имплементировать множество интерфейсов). Так не принято делать в C#? В Go, например, это рекомендованный подход; принцип ISP (Interface Segregation Principle), доведённый до логического предела.

Ок, допустим, я ленивый и реализовал в своём потоке только ByteWriter.
Как будет выглядеть клиентский код, который записывает в мой поток байтовый массив?
Если завтра я пойму, что меня не устраивает производительность, и реализую дополнительно в своём классe BytesWriter — как поменяется клиентский код?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[16]: Расскажите про интерфейсы
От: Kswapd Россия  
Дата: 03.02.19 22:28
Оценка:
Здравствуйте, Sinclair, Вы писали:

K>>А если объявить три интерфейса: ByteWriter, BytesWriter, ByteCountWriter? Каждый содержит ровно один соответствующий метод. И реализаторы имплементируют тот набор, который им нужен (можно же имплементировать множество интерфейсов). Так не принято делать в C#? В Go, например, это рекомендованный подход; принцип ISP (Interface Segregation Principle), доведённый до логического предела.


S>Ок, допустим, я ленивый и реализовал в своём потоке только ByteWriter.

S>Как будет выглядеть клиентский код, который записывает в мой поток байтовый массив?
S>Если завтра я пойму, что меня не устраивает производительность, и реализую дополнительно в своём классe BytesWriter — как поменяется клиентский код?

А что принимает клиент? По идее, должен принимать интерфейс ByteWriter. В вашем юзкейсе клиент ничего не заметит. Но он ничего не заметит и если будет принимать расширенный интерфейс с пустым методом. Всё равно для повышения производительности придётся вникать в код клиента и заменять цикл на вызов. То есть сверхзадача увеличения производительности без изменения клиента не выполнена.
Re[17]: Расскажите про интерфейсы
От: Sinclair Россия https://github.com/evilguest/
Дата: 04.02.19 04:18
Оценка:
Здравствуйте, Kswapd, Вы писали:

K>А что принимает клиент? По идее, должен принимать интерфейс ByteWriter. В вашем юзкейсе клиент ничего не заметит. Но он ничего не заметит и если будет принимать расширенный интерфейс с пустым методом. Всё равно для повышения производительности придётся вникать в код клиента и заменять цикл на вызов. То есть сверхзадача увеличения производительности без изменения клиента не выполнена.

Ну, вот видите, как плохо, когда один из собеседников отказывается писать код.
Вот вам код с выполненной сверхзадачей:
public void Client(IWriter w)
{
  var b = new byte[100];
  w.WriteBytes(b);
}


Вот две реализации интерфейса IWriter:
public class LazyCountingWriter: IWriter
{
  IWriter Inner {protected get; private set};

  int Count {public get; private set;} = 0;
  public LazyCountingWriter(IWriter inner) => Inner = inner ?? throw new ArgumentNullException(nameof(inner));
  public bool WriteByte(byte b) => Count += Inner.WriteByte(b) ? 1 : 0;
}


public class LessLazyCountingWriter: LazyCountingWriter
{
  public LazyCountingWriter(IWriter inner): base(inner) {};
  public int WriteBytes(byte[] bytes) => Count += Inner.WriteBytes(bytes);
}


Понятно, как это работает?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[18]: Расскажите про интерфейсы
От: Kswapd Россия  
Дата: 04.02.19 12:30
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Ну, вот видите, как плохо, когда один из собеседников отказывается писать код.


Разве было обязательство? У нас ведь не спор, а просто обмен мнениями. Я не собираюсь никого переубеждать, просто высказываю некоторые, возможно, сомнительные идеи и интересуюсь, как другие их понимают и как принято строить в разных языках. Ну, а писать на C#, C++ или Java тем более не хочется :D.

S>Понятно, как это работает?


Тогда я неправильно понял условие. Сначала было вроде про интерфейс, реализующий только WriteByte. Очевидно, клиент такого интерфейса должен был бы записывать массив в цикле.
Re[19]: Расскажите про интерфейсы
От: Sinclair Россия https://github.com/evilguest/
Дата: 05.02.19 05:50
Оценка:
Здравствуйте, Kswapd, Вы писали:

S>>Понятно, как это работает?


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

Нет, это как раз вы предложили разделить интерфейсы. Поэтому именно в вашем решении клиент был бы вынужден писать массив в цикле.
Это и есть недостаток вашей идеи по сравнению с default implementation.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.