Просмотр исходного кода

SVI Добавление текста, ссылок

SVI 2 лет назад
Родитель
Сommit
38607cbd30
2 измененных файлов с 170 добавлено и 1 удалено
  1. 1 0
      README.md
  2. 169 1
      docs/lesson03.md

+ 1 - 0
README.md

@@ -4,3 +4,4 @@
 
 - [Занятие 1. Контексты](./docs/lesson01.md)
 - [Занятие 2. Каналы](./docs/lesson02.md)
+- [Занятие 3. Интерфейсы](./docs/lesson03.md)

+ 169 - 1
docs/lesson03.md

@@ -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);
+- нужно выработать привычку делать _узкие_ интерфейсы;
+- можно _случайно_ удовлетворить требованиям интерфейса.
+
+**Интерфейс -- это полезный инструмент, позволяющий решать проблемы связности, развития, изоляции.**