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[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[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[14]: Расскажите про интерфейсы
От: AlexRK  
Дата: 28.01.19 21:11
Оценка: :)
Здравствуйте, Sinclair, Вы писали:

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


Полагаю, в качестве довольно кривого костыля, призванного через пень-колоду закрыть дыру под названием "прибитость гвоздями функций к данным". Что, впрочем, является одним из столпов так называемого ООП.
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[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...
Пока на собственное сообщение не было ответов, его можно удалить.