Pull to refresh
0
@flatscoderead⁠-⁠only

User

Send message
Вы берет частный случай

Блин, да это не частный, это как раз общий случай!

Это у вас был простой частный случай.

Что если еще одного консюмера не будет, а будут только продюсеры?

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

Что если не будет этой сложной структуры данных?

Это общий случай, с данными.

Что если определить queue пакет и там иметь интерфейсы консюмеру и продюсеру и общую структуру данных?

Да пожалуйста.
Определением интерфейса в пакете, в котором он используется.

Ну разместили, получили зависимость consumer от producer.



Я бы определил этот сложный тип в консюмере и как уже сказал — при этом пакет с реализацией зависел бы от пакета с интерфейсом.

Ну перенесли туда Data. И что видим? Зачем-то у нас реализация зависит от Consumer. А если у нас есть Consumer2, то он зачем-то зависит от Consumer.



А теперь, как правильно.

Опять же, в данном случае реализация интерфейса (интерфейс в пакете foo) будет зависеть от пакета foo (от интерфейса)

Ну так как вы от этого избавитесь, кроме как выносом общих типов в отдельный пакет?

но не наоборот

Вот если вы сделаете реализацию, которая не в курсе о потребителях (как в примере https://github.com/golang/go/wiki/CodeReviewComments#interfaces), и у которого Thing() будет чуть сложнее (с использованием типа в producer), то при попытке вынести интерфейс в consumer у вас будет прямая явная зависимость consumer от producer.
Никто не мешает вам так сделать, если вы так привыкли. Пример с io.Reader сами привели. Иногда так даже лучше делать. Я стараюсь такого избегать.

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

Более того, если у вас в интерфейсе присутствует хотя бы с один метод с параметром или возвратом проблемно-ориентированного интерфейса/структуры, то иного пути достичь желаемого (уменьшения зависимости) нет — нужно выделять общие сущности отдельно.

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

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

То, что вам понравилось в Go, полагаю, является возможностью использования тривиального адаптера, см. Design Patterns in Golang: Adapter в конце:

If the Target and Adaptee has similarities then the adapter has just to delegate the requests from the Target to the Adaptee.
Я создаю интерфейс, я выдвигаю требования, тот кто использует мою библиотеку должен выполнить мое требование.

Вот, отлично. Если реализация должна учитывать спецификации, то она зависит от интерфейса (явно или неявно).

Теперь возвращаемся к началу и видим:

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

Ну и каким образом явная зависимость убивает отделение интерфейса от реализации, если «кто использует мою библиотеку должен выполнить мое требование»?

Чем мешает явная зависимость размещению интерфейса у пользователя?
Где тут зависимость от реализации Publisher?

Повторяю, если у вас здесь не описан контракт, то он описан где-то в другом месте. А это означает, что интерфейс зависит от этого места.

В случае Go это неявная (логическая, семантическая) зависимость. Из-за более свободной системы типов приходится больше внимания уделять логическим/семантическая ограничениям и связям.
А это уже другая история. Речь идёт про технические аспекты.

Вы вспомните теорию, для чего вообще была введена система типов.

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

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

И обратно, чем свободнее система типов, тем больше необходимо накладывать семантические ограничения на код.
flatscode непонятно, с чем вы спорите. В Go интерфейсы используются несколько иначе, чем в Джаве например. В Go интерфейс как-правило создается только при надобности и после того, как как написаны «реализации». Тоесть, сначала был *os.File и *http.Request, а потом был io.Reader

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

Выше я привел пример io.Reader — это классический интерфейс со спецификацией и благодаря ей есть возможность:

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

Реализация может или явно (как в Java, например с помощью implements), или неявно (в комментариях упомянуты совместимые интерфейсы или просто описание) ссылаться на спецификацию.

Неявная ссылка на интерфейс (спецификацию) — это не отсутствие логической связи между реализацией и интерфейсом (спецификации). Логические/семантические ограничения никуда не деваются!

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

Это здесь причем? Нужна доработка — сделаем, согласно букве O в принципе SOLID.

Реализация — может быть и нет, а программист, пишущий реализацию — таки да.

Ну так отсутствие «технической» связи (например, явного упоминания интерфейса в реализации) не означает, что нет логической связи.
Если здесь не описана спецификация Publish(), значит вы надеетесь на какую-то реализацию.

Сразу напомню, как выглядит спецификация. Посмотрите, какой объем описания дан всего для одного метода.

// Reader is the interface that wraps the basic Read method.
//
// Read reads up to len(p) bytes into p. It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered. Even if Read
// returns n < len(p), it may use all of p as scratch space during the call.
// If some data is available but not len(p) bytes, Read conventionally
// returns what is available instead of waiting for more.
//
// When Read encounters an error or end-of-file condition after
// successfully reading n > 0 bytes, it returns the number of
// bytes read. It may return the (non-nil) error from the same call
// or return the error (and n == 0) from a subsequent call.
// An instance of this general case is that a Reader returning
// a non-zero number of bytes at the end of the input stream may
// return either err == EOF or err == nil. The next Read should
// return 0, EOF.
//
// Callers should always process the n > 0 bytes returned before
// considering the error err. Doing so correctly handles I/O errors
// that happen after reading some bytes and also both of the
// allowed EOF behaviors.
//
// Implementations of Read are discouraged from returning a
// zero byte count with a nil error, except when len(p) == 0.
// Callers should treat a return of 0 and nil as indicating that
// nothing happened; in particular it does not indicate EOF.
//
// Implementations must not retain p.
type Reader interface {
	Read(p []byte) (n int, err error)
}
Совершенно верно. Вот только кто у нас задаёт спецификацию? В Go и в современном C++, внезапно — потребитель.

Это невозможно, т.к. потребитель не может изменить реализацию. Поэтому, он может потребить только то, что есть.

Как реализация может узнать, что нужно потребителю? Реализация ни про потребителей, ни про их количество абсолютно не в курсе.
Где тут зависимость от реализации Publisher

Если здесь не описана спецификация Publish(), значит вы надеетесь на какую-то реализацию.

Отсутствие «технической» зависимости не означает, что логическая связь отсутствует. Как узнать, подходит какая-то структура семантически для использования в Send() или нет?
Смысл интерфейса в отделении данных и поведения.

Если не сложно, приведите ссылку на определение интерфейса, которое используете. Откуда оно?
С какого перепугу? В случае размещения интерфейса у клиента вы можете сделать есть таким узким, каким только возможно

Узким относительно чего? Релизации? Ну так это переворот с ног на голову отношение интерфейс-реализация (общее-частное).

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

Интерфейс не конечное и фиксированное количество сценариев задает, а стандартизирует поведение согласно разработанной спецификации.

Интерфейс это не общее, интерфейс описывает реализацию.

Интерфейс — это спецификация, контракт. Он определяет требования в максимально приемлемом общем виде.

Реализация интерфейса — это написание кода, который удовлетворяет его спецификации.

Каким образом интерфейс будет зависеть от реализации в случае когда интерфейс объявлен в месте, где он используется?

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

Размещение интерфейса у пользователя лишено смысла.

Во первых, потому что никто, кроме пользователя, им воспользоваться не может.

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

Но размещение интерфейса у пользователя (потребителя) — это какое-то извращение, когда реализация не в курсе (явно или неявно, не важно) спецификации, которую она реализует.

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

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

Кстати, а здесь вы что имеете ввиду?

Information

Rating
Does not participate
Registered
Activity