|
|
@@ -22,4 +22,172 @@ type Interface2 interface{
|
|
|
Interface1
|
|
|
Write(string)error
|
|
|
}
|
|
|
-```
|
|
|
+```
|
|
|
+
|
|
|
+`func1` не различаем фактические типы `ObjType1` и `ObjType2` -- для неё это один тип `Interface1`.
|
|
|
+
|
|
|
+## Что даёт интерфейс
|
|
|
+
|
|
|
+- гарантия контракта поведения объекта;
|
|
|
+- не нужно изменять существующий алгоритм при передаче объекта нового типа;
|
|
|
+- создание возможностей тестирования на разных этапах;
|
|
|
+- скрытие деталей реализации объекта.
|
|
|
+
|
|
|
+## Контракт поведения
|
|
|
+
|
|
|
+Контракт поведения -- это гарантия того, что объект передаваемый как параметр вызова обладает нужным поведением. Контракт поведения -- эт набор методов и их аргументов с типами для успешной передачи.
|
|
|
+
|
|
|
+Разные вызовы требуют разных контрактов (наборов методов), таки образом один и тот же объект может __удовлетворять__ разным интерфейсам, например:
|
|
|
+
|
|
|
+```golang
|
|
|
+type IWriter interface {
|
|
|
+ Write(fileName string, binData []byte)error
|
|
|
+}
|
|
|
+
|
|
|
+type IReader interface {
|
|
|
+ Read(fileName string)([]bte, error)
|
|
|
+}
|
|
|
+
|
|
|
+type IWriterReader interface {
|
|
|
+ IWriter
|
|
|
+ IReader
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+
|
|
|
+Вызову на чтение -- не интересна возможность записи.
|
|
|
+
|
|
|
+Вызову на запись -- не интересна возможность чтения.
|
|
|
+
|
|
|
+Вызову на запись и чтение одновременно требует обоих методов.
|
|
|
+
|
|
|
+Объект передаваемый во все три вызова может __удовлетворять__ как конкретным интерфейсу, так и все сразу.
|
|
|
+
|
|
|
+Конкретный вызов с требованием интерфейса:
|
|
|
+
|
|
|
+- нужное поведение требует;
|
|
|
+- ненужно поведение проигнорирует и обратиться к такому поведению из интерфейса невозможно.
|
|
|
+
|
|
|
+## Сохранение алгоритма
|
|
|
+
|
|
|
+Алгоритм может быть расположен в вендоринге. Алгоритм сложный, специфичный, обобщённый и покрыт тестами. Как передать туда объект нового типа -- интерфейс.
|
|
|
+
|
|
|
+Типичный пример: пакет `sort` из стандартной библиотеки:
|
|
|
+
|
|
|
+[Описание swap](https://pkg.go.dev/sort)
|
|
|
+```golang
|
|
|
+type Sort interface {
|
|
|
+ Less(adr0, adr1)bool
|
|
|
+ Swap(adr0, adr1)
|
|
|
+ Len()int
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+_Не важно_ какой тип реализует этот интерфейс -- он быдет имет ьвозможность сортировки _оптимальным_ способом.
|
|
|
+
|
|
|
+**Объекты разные -- алгоритм один**.
|
|
|
+
|
|
|
+## Тестирование
|
|
|
+
|
|
|
+Интерфейсы пригодятся:
|
|
|
+
|
|
|
+- нужна БД, а она недоступна но локальной машине разработчика;
|
|
|
+- нужно имитировать отстутсвие связи с шиной данных;
|
|
|
+- нужно имитировать внезапный сбой.
|
|
|
+
|
|
|
+Пример:
|
|
|
+
|
|
|
+```golang
|
|
|
+type ILink interface {
|
|
|
+ Get()(string, error)
|
|
|
+}
|
|
|
+
|
|
|
+func Get(link ILink)(string, error) {
|
|
|
+ resp,err:=link.Get()
|
|
|
+ return resp, err
|
|
|
+}
|
|
|
+
|
|
|
+type Link struct{}
|
|
|
+
|
|
|
+func (sf *Link)Get()(string, error){
|
|
|
+ . . . // Вот здесь на реальном клиенте НЕВОЗМОЖНО имитировать обрыв связи
|
|
|
+ return resp, err
|
|
|
+}
|
|
|
+
|
|
|
+type MockLink struct{
|
|
|
+ IsBadLink_ bool // Устанавливаемый извне признак отсутствия связи для тесто
|
|
|
+}
|
|
|
+
|
|
|
+func (sf *MockLink)Get()(string, error){
|
|
|
+ if sf.IsLinkBad {
|
|
|
+ return "", fmt.Errorf("MockLink.Get(): IsBadLink_==true")
|
|
|
+ }
|
|
|
+ return "ok", nil
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Подставляя во время тестов `MockLink` можно сымитировать любую ситуацию сбоя реального клиента.
|
|
|
+
|
|
|
+Подставляя любые структуры мок-клиента БД -- можно сымитировать любую ситуацию с данными в БД
|
|
|
+(в том числе -- обрыв связи, испорченные данные, неожиданные данные, задержки при перегрузках базы и т.п.)
|
|
|
+
|
|
|
+## Скрытие деталей реализации объекта
|
|
|
+
|
|
|
+Это полезное свойство заставляет выделять существенные части объекта и защищать внутренее устройство объекта.
|
|
|
+
|
|
|
+Пример:
|
|
|
+
|
|
|
+```golang
|
|
|
+
|
|
|
+type ILink interface {
|
|
|
+ Get()(string, error)
|
|
|
+}
|
|
|
+
|
|
|
+func GetInterface(link ILink)(string, error) {
|
|
|
+ resp, err := link.Get()
|
|
|
+ return resp, err
|
|
|
+}
|
|
|
+
|
|
|
+type Link struct{
|
|
|
+ Url string // Вот это поле при прямом доступе можно легко испортить
|
|
|
+}
|
|
|
+
|
|
|
+func GetInterface(link Link)(string, error) {
|
|
|
+ link.Url = "bad_url"
|
|
|
+ resp, err := link.Get() // Здесь с вероятность 146% будет ошибка
|
|
|
+ return resp, err
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+В первом случае -- невозможно испортить (намеренно или необдуманно) внутреннее состояние объекта (интерфейс не имеет состояния).
|
|
|
+
|
|
|
+Во втором случае -- работа программы будет нарушена неизбежно и последствия ошибки будут распространены на всю программу.
|
|
|
+
|
|
|
+## Как вернуть объект
|
|
|
+
|
|
|
+Интерфейс не скрывает до конца объект. Но если вызывающая сторона не знает о реальном типе объекта -- она его не получит.
|
|
|
+
|
|
|
+Пример:
|
|
|
+
|
|
|
+```golang
|
|
|
+func Get(link ILink)(string, error){
|
|
|
+ switch link.(type){
|
|
|
+ case Link:
|
|
|
+ ...
|
|
|
+ case LinkProxy:
|
|
|
+ ...
|
|
|
+ case LinkForward:
|
|
|
+ ...
|
|
|
+ default:
|
|
|
+ return "", fmt.Errorf("Get(): unknown type ILink(%#v)", link)
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## Проблемы интерфейсов
|
|
|
+
|
|
|
+- сложнее отлаживать (нужен отладочный вывод или отличный отладсик в IDE);
|
|
|
+- нужно выработать привычку делать _узкие_ интерфейсы;
|
|
|
+- можно _случайно_ удовлетворить требованиям интерфейса.
|
|
|
+
|
|
|
+**Интерфейс -- это полезный инструмент, позволяющий решать проблемы связности, развития, изоляции.**
|