[{"data":1,"prerenderedAt":8401},["ShallowReactive",2],{"content:\u002F04-advanced\u002F05-oop":3},{"title":4,"description":5,"path":6,"body":7},"ООП в Go","Go — не классический ООП язык. Нет классов, наследования, конструкторов, перегрузки методов.","\u002F04-advanced\u002F05-oop",{"type":8,"value":9,"toc":8333},"minimark",[10,19,24,98,276,284,307,310,314,319,440,444,457,531,537,541,544,548,573,576,578,589,591,594,598,891,897,901,1053,1056,1060,1153,1157,1195,1199,1347,1351,1411,1413,1424,1426,1430,1434,1620,1627,1666,1670,1676,1719,1725,1886,1889,1895,1899,1973,1977,1983,1989,1991,2005,2007,2011,2015,2176,2179,2183,2189,2278,2284,2315,2318,2495,2501,2549,2555,2590,2593,2644,2647,2650,2653,2655,2666,2668,2672,2795,2799,2874,2880,2891,2894,2898,2901,3169,3173,3176,3234,3237,3241,3337,3340,3342,3362,3364,3368,3372,3600,3604,3706,3713,3717,3853,3856,3862,3891,3894,3934,3940,3976,3980,3984,4166,4168,4179,4181,4185,4254,4298,4302,4312,4317,4347,4350,4356,4360,4366,4439,4444,4524,4530,4569,4575,4608,4612,4625,4628,4652,4654,4665,4667,4671,4675,4730,4734,4810,4815,4820,4872,4876,5032,5037,5042,5046,5179,5186,5190,5251,5253,5267,5269,5273,5277,5448,5452,5619,5622,5626,5823,5829,5860,5864,5867,5869,5872,6200,6205,6216,6218,6221,6573,6575,6578,6742,6747,6861,6868,6871,7235,7250,7253,7259,7485,7490,7579,7584,7601,7606,7772,7774,7778,7812,8006,8329],[11,12,13,14,18],"p",{},"Go — ",[15,16,17],"strong",{},"не классический ООП язык",". Нет классов, наследования, конструкторов, перегрузки методов.",[11,20,21],{},[15,22,23],{},"Что есть:",[25,26,27,41],"table",{},[28,29,30],"thead",{},[31,32,33,38],"tr",{},[34,35,37],"th",{"align":36},"left","ООП концепция",[34,39,40],{"align":36},"Как в Go",[42,43,44,53,61,69,77,90],"tbody",{},[31,45,46,50],{},[47,48,49],"td",{"align":36},"Инкапсуляция",[47,51,52],{"align":36},"Exported\u002Funexported (заглавная буква)",[31,54,55,58],{},[47,56,57],{"align":36},"Полиморфизм",[47,59,60],{"align":36},"Интерфейсы (неявная реализация)",[31,62,63,66],{},[47,64,65],{"align":36},"Наследование",[47,67,68],{"align":36},"❌ Нет. Вместо него — композиция (embedding)",[31,70,71,74],{},[47,72,73],{"align":36},"Классы",[47,75,76],{"align":36},"Структуры + методы",[31,78,79,82],{},[47,80,81],{"align":36},"Конструкторы",[47,83,84,85,89],{"align":36},"Функции ",[86,87,88],"code",{},"New...()"," по конвенции",[31,91,92,95],{},[47,93,94],{"align":36},"Абстрактные классы",[47,96,97],{"align":36},"Интерфейсы",[99,100,105],"pre",{"className":101,"code":102,"language":103,"meta":104,"style":104},"language-go shiki shiki-themes github-dark","\u002F\u002F \"Класс\" в Go\ntype User struct {\n    name  string \u002F\u002F unexported — инкапсуляция\n    Email string \u002F\u002F exported\n}\n\n\u002F\u002F \"Конструктор\"\nfunc NewUser(name, email string) *User {\n    return &User{name: name, Email: email}\n}\n\n\u002F\u002F \"Метод\"\nfunc (u *User) Name() string { return u.name }\n","go","",[86,106,107,116,134,146,157,163,170,176,212,226,231,236,242],{"__ignoreMap":104},[108,109,112],"span",{"class":110,"line":111},"line",1,[108,113,115],{"class":114},"sAwPA","\u002F\u002F \"Класс\" в Go\n",[108,117,119,123,127,130],{"class":110,"line":118},2,[108,120,122],{"class":121},"snl16","type",[108,124,126],{"class":125},"svObZ"," User",[108,128,129],{"class":121}," struct",[108,131,133],{"class":132},"s95oV"," {\n",[108,135,137,140,143],{"class":110,"line":136},3,[108,138,139],{"class":132},"    name  ",[108,141,142],{"class":121},"string",[108,144,145],{"class":114}," \u002F\u002F unexported — инкапсуляция\n",[108,147,149,152,154],{"class":110,"line":148},4,[108,150,151],{"class":132},"    Email ",[108,153,142],{"class":121},[108,155,156],{"class":114}," \u002F\u002F exported\n",[108,158,160],{"class":110,"line":159},5,[108,161,162],{"class":132},"}\n",[108,164,166],{"class":110,"line":165},6,[108,167,169],{"emptyLinePlaceholder":168},true,"\n",[108,171,173],{"class":110,"line":172},7,[108,174,175],{"class":114},"\u002F\u002F \"Конструктор\"\n",[108,177,179,182,185,188,192,195,198,201,204,207,210],{"class":110,"line":178},8,[108,180,181],{"class":121},"func",[108,183,184],{"class":125}," NewUser",[108,186,187],{"class":132},"(",[108,189,191],{"class":190},"s9osk","name",[108,193,194],{"class":132},", ",[108,196,197],{"class":190},"email",[108,199,200],{"class":121}," string",[108,202,203],{"class":132},") ",[108,205,206],{"class":121},"*",[108,208,209],{"class":125},"User",[108,211,133],{"class":132},[108,213,215,218,221,223],{"class":110,"line":214},9,[108,216,217],{"class":121},"    return",[108,219,220],{"class":121}," &",[108,222,209],{"class":125},[108,224,225],{"class":132},"{name: name, Email: email}\n",[108,227,229],{"class":110,"line":228},10,[108,230,162],{"class":132},[108,232,234],{"class":110,"line":233},11,[108,235,169],{"emptyLinePlaceholder":168},[108,237,239],{"class":110,"line":238},12,[108,240,241],{"class":114},"\u002F\u002F \"Метод\"\n",[108,243,245,247,250,253,255,257,259,262,265,267,270,273],{"class":110,"line":244},13,[108,246,181],{"class":121},[108,248,249],{"class":132}," (",[108,251,252],{"class":190},"u ",[108,254,206],{"class":121},[108,256,209],{"class":125},[108,258,203],{"class":132},[108,260,261],{"class":125},"Name",[108,263,264],{"class":132},"() ",[108,266,142],{"class":121},[108,268,269],{"class":132}," { ",[108,271,272],{"class":121},"return",[108,274,275],{"class":132}," u.name }\n",[277,278,280,283],"h2",{"id":279},"что-отвечать-на-собесе-go-поддерживает-ооп-концепции-но-реализует-их-иначе-вместо-иерархий-наследования-композиция-и-маленькие-интерфейсы-это-by-design-не-ограничение",[15,281,282],{},"Что отвечать на собесе:"," Go поддерживает ООП-концепции, но реализует их иначе. Вместо иерархий наследования — композиция и маленькие интерфейсы. Это by design, не ограничение.",[285,286,287,295,301],"ul",{},[288,289,290,291,294],"li",{},"В Go нет private\u002Fpublic\u002Fprotected. ",[15,292,293],{},"Инкапсуляция — на уровне пакетов",": заглавная буква = экспортировано, строчная = приватно",[288,296,297,300],{},[15,298,299],{},"Единица инкапсуляции — пакет",", не структура. Всё внутри пакета видит всё, даже \"приватные\" поля чужих структур",[288,302,303,304],{},"Нет friend-классов, нет protected — только два уровня: ",[15,305,306],{},"видно снаружи пакета или нет",[308,309],"hr",{},[277,311,313],{"id":312},"go-без-классического-ооп","Go без классического ООП",[315,316,318],"h3",{"id":317},"правило","Правило",[99,320,322],{"className":101,"code":321,"language":103,"meta":104,"style":104},"package user\n\ntype User struct {\n    Name  string   \u002F\u002F экспортировано — доступно из других пакетов\n    email string   \u002F\u002F НЕ экспортировано — доступно только внутри пакета user\n}\n\nfunc (u *User) Email() string { return u.email }  \u002F\u002F геттер — единственный способ дать доступ\n\nfunc newSession() {}  \u002F\u002F приватная функция — только внутри пакета\nfunc NewUser() *User {}  \u002F\u002F экспортированная — конструктор\n",[86,323,324,332,336,346,356,366,370,374,405,409,422],{"__ignoreMap":104},[108,325,326,329],{"class":110,"line":111},[108,327,328],{"class":121},"package",[108,330,331],{"class":125}," user\n",[108,333,334],{"class":110,"line":118},[108,335,169],{"emptyLinePlaceholder":168},[108,337,338,340,342,344],{"class":110,"line":136},[108,339,122],{"class":121},[108,341,126],{"class":125},[108,343,129],{"class":121},[108,345,133],{"class":132},[108,347,348,351,353],{"class":110,"line":148},[108,349,350],{"class":132},"    Name  ",[108,352,142],{"class":121},[108,354,355],{"class":114},"   \u002F\u002F экспортировано — доступно из других пакетов\n",[108,357,358,361,363],{"class":110,"line":159},[108,359,360],{"class":132},"    email ",[108,362,142],{"class":121},[108,364,365],{"class":114},"   \u002F\u002F НЕ экспортировано — доступно только внутри пакета user\n",[108,367,368],{"class":110,"line":165},[108,369,162],{"class":132},[108,371,372],{"class":110,"line":172},[108,373,169],{"emptyLinePlaceholder":168},[108,375,376,378,380,382,384,386,388,391,393,395,397,399,402],{"class":110,"line":178},[108,377,181],{"class":121},[108,379,249],{"class":132},[108,381,252],{"class":190},[108,383,206],{"class":121},[108,385,209],{"class":125},[108,387,203],{"class":132},[108,389,390],{"class":125},"Email",[108,392,264],{"class":132},[108,394,142],{"class":121},[108,396,269],{"class":132},[108,398,272],{"class":121},[108,400,401],{"class":132}," u.email }  ",[108,403,404],{"class":114},"\u002F\u002F геттер — единственный способ дать доступ\n",[108,406,407],{"class":110,"line":214},[108,408,169],{"emptyLinePlaceholder":168},[108,410,411,413,416,419],{"class":110,"line":228},[108,412,181],{"class":121},[108,414,415],{"class":125}," newSession",[108,417,418],{"class":132},"() {}  ",[108,420,421],{"class":114},"\u002F\u002F приватная функция — только внутри пакета\n",[108,423,424,426,428,430,432,434,437],{"class":110,"line":233},[108,425,181],{"class":121},[108,427,184],{"class":125},[108,429,264],{"class":132},[108,431,206],{"class":121},[108,433,209],{"class":125},[108,435,436],{"class":132}," {}  ",[108,438,439],{"class":114},"\u002F\u002F экспортированная — конструктор\n",[315,441,443],{"id":442},"ключевые-отличия-от-классического-ооп","Ключевые отличия от классического ООП",[11,445,446,449,450,453,454,456],{},[15,447,448],{},"Нет уровня структуры",": в Java private поле недоступно даже другому классу в том же пакете (без геттера). В Go — любой код в пакете ",[86,451,452],{},"user"," видит ",[86,455,197],{}," напрямую.",[99,458,460],{"className":101,"code":459,"language":103,"meta":104,"style":104},"\u002F\u002F Файл user.go\ntype User struct { email string }\n\n\u002F\u002F Файл admin.go (тот же пакет user)\nfunc resetEmail(u *User) {\n    u.email = \"\"  \u002F\u002F ОК — тот же пакет, хоть и другой файл\n}\n",[86,461,462,467,483,487,492,512,527],{"__ignoreMap":104},[108,463,464],{"class":110,"line":111},[108,465,466],{"class":114},"\u002F\u002F Файл user.go\n",[108,468,469,471,473,475,478,480],{"class":110,"line":118},[108,470,122],{"class":121},[108,472,126],{"class":125},[108,474,129],{"class":121},[108,476,477],{"class":132}," { email ",[108,479,142],{"class":121},[108,481,482],{"class":132}," }\n",[108,484,485],{"class":110,"line":136},[108,486,169],{"emptyLinePlaceholder":168},[108,488,489],{"class":110,"line":148},[108,490,491],{"class":114},"\u002F\u002F Файл admin.go (тот же пакет user)\n",[108,493,494,496,499,501,504,507,509],{"class":110,"line":159},[108,495,181],{"class":121},[108,497,498],{"class":125}," resetEmail",[108,500,187],{"class":132},[108,502,503],{"class":190},"u",[108,505,506],{"class":121}," *",[108,508,209],{"class":125},[108,510,511],{"class":132},") {\n",[108,513,514,517,520,524],{"class":110,"line":165},[108,515,516],{"class":132},"    u.email ",[108,518,519],{"class":121},"=",[108,521,523],{"class":522},"sU2Wk"," \"\"",[108,525,526],{"class":114},"  \u002F\u002F ОК — тот же пакет, хоть и другой файл\n",[108,528,529],{"class":110,"line":172},[108,530,162],{"class":132},[11,532,533,536],{},[15,534,535],{},"Нет protected",": нет наследования — нет смысла в \"видно только наследникам\".",[315,538,540],{"id":539},"зачем-так","Зачем так",[11,542,543],{},"Go поощряет маленькие пакеты с чётким API. Пакет = команда из 1-3 файлов, все авторы контролируют инварианты. Если пакет разросся — разбей на подпакеты.",[315,545,547],{"id":546},"подвох-на-собесе","Подвох на собесе",[99,549,551],{"className":101,"code":550,"language":103,"meta":104,"style":104},"json.Unmarshal(data, &user)  \u002F\u002F НЕ заполнит приватные поля (email)\n",[86,552,553],{"__ignoreMap":104},[108,554,555,558,561,564,567,570],{"class":110,"line":111},[108,556,557],{"class":132},"json.",[108,559,560],{"class":125},"Unmarshal",[108,562,563],{"class":132},"(data, ",[108,565,566],{"class":121},"&",[108,568,569],{"class":132},"user)  ",[108,571,572],{"class":114},"\u002F\u002F НЕ заполнит приватные поля (email)\n",[11,574,575],{},"Рефлексия и encoding\u002Fjson не видят неэкспортированные поля чужого пакета.",[308,577],{},[285,579,580,583,586],{},[288,581,582],{},"Полиморфизм в Go — через интерфейсы. Имплицитная реализация: нет implements, тип автоматически удовлетворяет интерфейсу если имеет все методы",[288,584,585],{},"Интерфейсы маленькие (1-2 метода): io.Reader, io.Writer, fmt.Stringer, error. \"The bigger the interface, the weaker the abstraction\"",[288,587,588],{},"Определяй интерфейс на стороне потребителя, не на стороне реализации",[308,590],{},[277,592,97],{"id":593},"интерфейсы",[315,595,597],{"id":596},"имплицитная-реализация","Имплицитная реализация",[99,599,601],{"className":101,"code":600,"language":103,"meta":104,"style":104},"type Writer interface {\n    Write(p []byte) (n int, err error)\n}\n\ntype FileWriter struct{ path string }\nfunc (f *FileWriter) Write(p []byte) (int, error) { \u002F* ... *\u002F }\n\ntype BufferWriter struct{ buf []byte }\nfunc (b *BufferWriter) Write(p []byte) (int, error) { \u002F* ... *\u002F }\n\n\u002F\u002F Оба удовлетворяют Writer без явного объявления\nfunc Save(w Writer, data []byte) error {\n    _, err := w.Write(data)\n    return err\n}\n\nSave(&FileWriter{\"\u002Ftmp\u002Fx\"}, data)   \u002F\u002F ОК\nSave(&BufferWriter{}, data)          \u002F\u002F ОК\n",[86,602,603,615,650,654,658,674,719,723,739,779,783,788,817,833,841,846,851,875],{"__ignoreMap":104},[108,604,605,607,610,613],{"class":110,"line":111},[108,606,122],{"class":121},[108,608,609],{"class":125}," Writer",[108,611,612],{"class":121}," interface",[108,614,133],{"class":132},[108,616,617,620,622,624,627,630,633,636,639,641,644,647],{"class":110,"line":118},[108,618,619],{"class":125},"    Write",[108,621,187],{"class":132},[108,623,11],{"class":190},[108,625,626],{"class":132}," []",[108,628,629],{"class":121},"byte",[108,631,632],{"class":132},") (",[108,634,635],{"class":190},"n",[108,637,638],{"class":121}," int",[108,640,194],{"class":132},[108,642,643],{"class":190},"err",[108,645,646],{"class":121}," error",[108,648,649],{"class":132},")\n",[108,651,652],{"class":110,"line":136},[108,653,162],{"class":132},[108,655,656],{"class":110,"line":148},[108,657,169],{"emptyLinePlaceholder":168},[108,659,660,662,665,667,670,672],{"class":110,"line":159},[108,661,122],{"class":121},[108,663,664],{"class":125}," FileWriter",[108,666,129],{"class":121},[108,668,669],{"class":132},"{ path ",[108,671,142],{"class":121},[108,673,482],{"class":132},[108,675,676,678,680,683,685,688,690,693,695,697,699,701,703,706,708,711,714,717],{"class":110,"line":165},[108,677,181],{"class":121},[108,679,249],{"class":132},[108,681,682],{"class":190},"f ",[108,684,206],{"class":121},[108,686,687],{"class":125},"FileWriter",[108,689,203],{"class":132},[108,691,692],{"class":125},"Write",[108,694,187],{"class":132},[108,696,11],{"class":190},[108,698,626],{"class":132},[108,700,629],{"class":121},[108,702,632],{"class":132},[108,704,705],{"class":121},"int",[108,707,194],{"class":132},[108,709,710],{"class":121},"error",[108,712,713],{"class":132},") { ",[108,715,716],{"class":114},"\u002F* ... *\u002F",[108,718,482],{"class":132},[108,720,721],{"class":110,"line":172},[108,722,169],{"emptyLinePlaceholder":168},[108,724,725,727,730,732,735,737],{"class":110,"line":178},[108,726,122],{"class":121},[108,728,729],{"class":125}," BufferWriter",[108,731,129],{"class":121},[108,733,734],{"class":132},"{ buf []",[108,736,629],{"class":121},[108,738,482],{"class":132},[108,740,741,743,745,748,750,753,755,757,759,761,763,765,767,769,771,773,775,777],{"class":110,"line":214},[108,742,181],{"class":121},[108,744,249],{"class":132},[108,746,747],{"class":190},"b ",[108,749,206],{"class":121},[108,751,752],{"class":125},"BufferWriter",[108,754,203],{"class":132},[108,756,692],{"class":125},[108,758,187],{"class":132},[108,760,11],{"class":190},[108,762,626],{"class":132},[108,764,629],{"class":121},[108,766,632],{"class":132},[108,768,705],{"class":121},[108,770,194],{"class":132},[108,772,710],{"class":121},[108,774,713],{"class":132},[108,776,716],{"class":114},[108,778,482],{"class":132},[108,780,781],{"class":110,"line":228},[108,782,169],{"emptyLinePlaceholder":168},[108,784,785],{"class":110,"line":233},[108,786,787],{"class":114},"\u002F\u002F Оба удовлетворяют Writer без явного объявления\n",[108,789,790,792,795,797,800,802,804,807,809,811,813,815],{"class":110,"line":238},[108,791,181],{"class":121},[108,793,794],{"class":125}," Save",[108,796,187],{"class":132},[108,798,799],{"class":190},"w",[108,801,609],{"class":125},[108,803,194],{"class":132},[108,805,806],{"class":190},"data",[108,808,626],{"class":132},[108,810,629],{"class":121},[108,812,203],{"class":132},[108,814,710],{"class":121},[108,816,133],{"class":132},[108,818,819,822,825,828,830],{"class":110,"line":244},[108,820,821],{"class":132},"    _, err ",[108,823,824],{"class":121},":=",[108,826,827],{"class":132}," w.",[108,829,692],{"class":125},[108,831,832],{"class":132},"(data)\n",[108,834,836,838],{"class":110,"line":835},14,[108,837,217],{"class":121},[108,839,840],{"class":132}," err\n",[108,842,844],{"class":110,"line":843},15,[108,845,162],{"class":132},[108,847,849],{"class":110,"line":848},16,[108,850,169],{"emptyLinePlaceholder":168},[108,852,854,857,859,861,863,866,869,872],{"class":110,"line":853},17,[108,855,856],{"class":125},"Save",[108,858,187],{"class":132},[108,860,566],{"class":121},[108,862,687],{"class":125},[108,864,865],{"class":132},"{",[108,867,868],{"class":522},"\"\u002Ftmp\u002Fx\"",[108,870,871],{"class":132},"}, data)   ",[108,873,874],{"class":114},"\u002F\u002F ОК\n",[108,876,878,880,882,884,886,889],{"class":110,"line":877},18,[108,879,856],{"class":125},[108,881,187],{"class":132},[108,883,566],{"class":121},[108,885,752],{"class":125},[108,887,888],{"class":132},"{}, data)          ",[108,890,874],{"class":114},[11,892,893,896],{},[15,894,895],{},"Нет implements",": связь между типом и интерфейсом — структурная (по набору методов), не номинальная (по объявлению).",[315,898,900],{"id":899},"интерфейс-на-стороне-потребителя","Интерфейс на стороне потребителя",[99,902,904],{"className":101,"code":903,"language":103,"meta":104,"style":104},"\u002F\u002F ПЛОХО — интерфейс рядом с реализацией\npackage db\n\ntype UserStore interface {  \u002F\u002F зачем? есть только одна реализация\n    Get(id int) User\n}\ntype PostgresStore struct{}\n\n\u002F\u002F ХОРОШО — интерфейс там, где он используется\npackage handler\n\ntype UserGetter interface {  \u002F\u002F минимальный интерфейс для handler\n    Get(id int) User\n}\n\nfunc NewHandler(store UserGetter) *Handler { \u002F* ... *\u002F }\n",[86,905,906,911,918,922,937,954,958,970,974,979,986,990,1004,1018,1022,1026],{"__ignoreMap":104},[108,907,908],{"class":110,"line":111},[108,909,910],{"class":114},"\u002F\u002F ПЛОХО — интерфейс рядом с реализацией\n",[108,912,913,915],{"class":110,"line":118},[108,914,328],{"class":121},[108,916,917],{"class":125}," db\n",[108,919,920],{"class":110,"line":136},[108,921,169],{"emptyLinePlaceholder":168},[108,923,924,926,929,931,934],{"class":110,"line":148},[108,925,122],{"class":121},[108,927,928],{"class":125}," UserStore",[108,930,612],{"class":121},[108,932,933],{"class":132}," {  ",[108,935,936],{"class":114},"\u002F\u002F зачем? есть только одна реализация\n",[108,938,939,942,944,947,949,951],{"class":110,"line":159},[108,940,941],{"class":125},"    Get",[108,943,187],{"class":132},[108,945,946],{"class":190},"id",[108,948,638],{"class":121},[108,950,203],{"class":132},[108,952,953],{"class":125},"User\n",[108,955,956],{"class":110,"line":165},[108,957,162],{"class":132},[108,959,960,962,965,967],{"class":110,"line":172},[108,961,122],{"class":121},[108,963,964],{"class":125}," PostgresStore",[108,966,129],{"class":121},[108,968,969],{"class":132},"{}\n",[108,971,972],{"class":110,"line":178},[108,973,169],{"emptyLinePlaceholder":168},[108,975,976],{"class":110,"line":214},[108,977,978],{"class":114},"\u002F\u002F ХОРОШО — интерфейс там, где он используется\n",[108,980,981,983],{"class":110,"line":228},[108,982,328],{"class":121},[108,984,985],{"class":125}," handler\n",[108,987,988],{"class":110,"line":233},[108,989,169],{"emptyLinePlaceholder":168},[108,991,992,994,997,999,1001],{"class":110,"line":238},[108,993,122],{"class":121},[108,995,996],{"class":125}," UserGetter",[108,998,612],{"class":121},[108,1000,933],{"class":132},[108,1002,1003],{"class":114},"\u002F\u002F минимальный интерфейс для handler\n",[108,1005,1006,1008,1010,1012,1014,1016],{"class":110,"line":244},[108,1007,941],{"class":125},[108,1009,187],{"class":132},[108,1011,946],{"class":190},[108,1013,638],{"class":121},[108,1015,203],{"class":132},[108,1017,953],{"class":125},[108,1019,1020],{"class":110,"line":835},[108,1021,162],{"class":132},[108,1023,1024],{"class":110,"line":843},[108,1025,169],{"emptyLinePlaceholder":168},[108,1027,1028,1030,1033,1035,1038,1040,1042,1044,1047,1049,1051],{"class":110,"line":848},[108,1029,181],{"class":121},[108,1031,1032],{"class":125}," NewHandler",[108,1034,187],{"class":132},[108,1036,1037],{"class":190},"store",[108,1039,996],{"class":125},[108,1041,203],{"class":132},[108,1043,206],{"class":121},[108,1045,1046],{"class":125},"Handler",[108,1048,269],{"class":132},[108,1050,716],{"class":114},[108,1052,482],{"class":132},[11,1054,1055],{},"Потребитель определяет что ему нужно. Реализация ничего не знает об интерфейсе.",[315,1057,1059],{"id":1058},"композиция-интерфейсов","Композиция интерфейсов",[99,1061,1063],{"className":101,"code":1062,"language":103,"meta":104,"style":104},"type Reader interface { Read(p []byte) (int, error) }\ntype Writer interface { Write(p []byte) (int, error) }\ntype ReadWriter interface {\n    Reader\n    Writer\n}\n",[86,1064,1065,1098,1128,1139,1144,1149],{"__ignoreMap":104},[108,1066,1067,1069,1072,1074,1076,1079,1081,1083,1085,1087,1089,1091,1093,1095],{"class":110,"line":111},[108,1068,122],{"class":121},[108,1070,1071],{"class":125}," Reader",[108,1073,612],{"class":121},[108,1075,269],{"class":132},[108,1077,1078],{"class":125},"Read",[108,1080,187],{"class":132},[108,1082,11],{"class":190},[108,1084,626],{"class":132},[108,1086,629],{"class":121},[108,1088,632],{"class":132},[108,1090,705],{"class":121},[108,1092,194],{"class":132},[108,1094,710],{"class":121},[108,1096,1097],{"class":132},") }\n",[108,1099,1100,1102,1104,1106,1108,1110,1112,1114,1116,1118,1120,1122,1124,1126],{"class":110,"line":118},[108,1101,122],{"class":121},[108,1103,609],{"class":125},[108,1105,612],{"class":121},[108,1107,269],{"class":132},[108,1109,692],{"class":125},[108,1111,187],{"class":132},[108,1113,11],{"class":190},[108,1115,626],{"class":132},[108,1117,629],{"class":121},[108,1119,632],{"class":132},[108,1121,705],{"class":121},[108,1123,194],{"class":132},[108,1125,710],{"class":121},[108,1127,1097],{"class":132},[108,1129,1130,1132,1135,1137],{"class":110,"line":136},[108,1131,122],{"class":121},[108,1133,1134],{"class":125}," ReadWriter",[108,1136,612],{"class":121},[108,1138,133],{"class":132},[108,1140,1141],{"class":110,"line":148},[108,1142,1143],{"class":125},"    Reader\n",[108,1145,1146],{"class":110,"line":159},[108,1147,1148],{"class":125},"    Writer\n",[108,1150,1151],{"class":110,"line":165},[108,1152,162],{"class":132},[315,1154,1156],{"id":1155},"проверка-реализации-в-compile-time","Проверка реализации в compile time",[99,1158,1160],{"className":101,"code":1159,"language":103,"meta":104,"style":104},"var _ Writer = (*FileWriter)(nil)  \u002F\u002F ошибка компиляции если не реализует\n",[86,1161,1162],{"__ignoreMap":104},[108,1163,1164,1167,1170,1173,1176,1178,1180,1182,1185,1189,1192],{"class":110,"line":111},[108,1165,1166],{"class":121},"var",[108,1168,1169],{"class":132}," _ ",[108,1171,1172],{"class":125},"Writer",[108,1174,1175],{"class":121}," =",[108,1177,249],{"class":132},[108,1179,206],{"class":121},[108,1181,687],{"class":125},[108,1183,1184],{"class":132},")(",[108,1186,1188],{"class":1187},"sDLfK","nil",[108,1190,1191],{"class":132},")  ",[108,1193,1194],{"class":114},"\u002F\u002F ошибка компиляции если не реализует\n",[315,1196,1198],{"id":1197},"type-switch-pattern-matching","Type switch — \"pattern matching\"",[99,1200,1202],{"className":101,"code":1201,"language":103,"meta":104,"style":104},"func describe(i interface{}) string {\n    switch v := i.(type) {\n    case string:\n        return \"string: \" + v\n    case int:\n        return fmt.Sprintf(\"int: %d\", v)\n    case error:\n        return \"error: \" + v.Error()\n    default:\n        return \"unknown\"\n    }\n}\n",[86,1203,1204,1225,1242,1252,1266,1274,1298,1306,1324,1331,1338,1343],{"__ignoreMap":104},[108,1205,1206,1208,1211,1213,1216,1218,1221,1223],{"class":110,"line":111},[108,1207,181],{"class":121},[108,1209,1210],{"class":125}," describe",[108,1212,187],{"class":132},[108,1214,1215],{"class":190},"i",[108,1217,612],{"class":121},[108,1219,1220],{"class":132},"{}) ",[108,1222,142],{"class":121},[108,1224,133],{"class":132},[108,1226,1227,1230,1233,1235,1238,1240],{"class":110,"line":118},[108,1228,1229],{"class":121},"    switch",[108,1231,1232],{"class":132}," v ",[108,1234,824],{"class":121},[108,1236,1237],{"class":132}," i.(",[108,1239,122],{"class":121},[108,1241,511],{"class":132},[108,1243,1244,1247,1249],{"class":110,"line":136},[108,1245,1246],{"class":121},"    case",[108,1248,200],{"class":121},[108,1250,1251],{"class":132},":\n",[108,1253,1254,1257,1260,1263],{"class":110,"line":148},[108,1255,1256],{"class":121},"        return",[108,1258,1259],{"class":522}," \"string: \"",[108,1261,1262],{"class":121}," +",[108,1264,1265],{"class":132}," v\n",[108,1267,1268,1270,1272],{"class":110,"line":159},[108,1269,1246],{"class":121},[108,1271,638],{"class":121},[108,1273,1251],{"class":132},[108,1275,1276,1278,1281,1284,1286,1289,1292,1295],{"class":110,"line":165},[108,1277,1256],{"class":121},[108,1279,1280],{"class":132}," fmt.",[108,1282,1283],{"class":125},"Sprintf",[108,1285,187],{"class":132},[108,1287,1288],{"class":522},"\"int: ",[108,1290,1291],{"class":1187},"%d",[108,1293,1294],{"class":522},"\"",[108,1296,1297],{"class":132},", v)\n",[108,1299,1300,1302,1304],{"class":110,"line":172},[108,1301,1246],{"class":121},[108,1303,646],{"class":121},[108,1305,1251],{"class":132},[108,1307,1308,1310,1313,1315,1318,1321],{"class":110,"line":178},[108,1309,1256],{"class":121},[108,1311,1312],{"class":522}," \"error: \"",[108,1314,1262],{"class":121},[108,1316,1317],{"class":132}," v.",[108,1319,1320],{"class":125},"Error",[108,1322,1323],{"class":132},"()\n",[108,1325,1326,1329],{"class":110,"line":214},[108,1327,1328],{"class":121},"    default",[108,1330,1251],{"class":132},[108,1332,1333,1335],{"class":110,"line":228},[108,1334,1256],{"class":121},[108,1336,1337],{"class":522}," \"unknown\"\n",[108,1339,1340],{"class":110,"line":233},[108,1341,1342],{"class":132},"    }\n",[108,1344,1345],{"class":110,"line":238},[108,1346,162],{"class":132},[315,1348,1350],{"id":1349},"сравнение-с-javac","Сравнение с Java\u002FC++",[25,1352,1353,1365],{},[28,1354,1355],{},[31,1356,1357,1359,1362],{},[34,1358],{},[34,1360,1361],{},"Java\u002FC++",[34,1363,1364],{},"Go",[42,1366,1367,1378,1389,1400],{},[31,1368,1369,1372,1375],{},[47,1370,1371],{},"Связь",[47,1373,1374],{},"implements\u002Fextends (явная)",[47,1376,1377],{},"Структурная (неявная)",[31,1379,1380,1383,1386],{},[47,1381,1382],{},"Иерархия",[47,1384,1385],{},"Дерево типов",[47,1387,1388],{},"Нет иерархии",[31,1390,1391,1394,1397],{},[47,1392,1393],{},"Размер интерфейса",[47,1395,1396],{},"Часто 10+ методов",[47,1398,1399],{},"1-2 метода (идиома)",[31,1401,1402,1405,1408],{},[47,1403,1404],{},"Где определять",[47,1406,1407],{},"Рядом с реализацией",[47,1409,1410],{},"Рядом с потребителем",[308,1412],{},[285,1414,1415,1418,1421],{},[288,1416,1417],{},"В Go нет наследования. Вместо него — композиция через встраивание (embedding). Встроенные поля\u002Fметоды \"всплывают\" наверх, но это синтаксический сахар, не is-a",[288,1419,1420],{},"Встраивание = has-a с удобным синтаксисом. Нет виртуальных методов, нет super, нет переопределения",[288,1422,1423],{},"Полиморфизм — через интерфейсы, не через иерархию типов",[308,1425],{},[277,1427,1429],{"id":1428},"композиция-и-embedding","Композиция и embedding",[315,1431,1433],{"id":1432},"встраивание-embedding","Встраивание (embedding)",[99,1435,1437],{"className":101,"code":1436,"language":103,"meta":104,"style":104},"package main\n\nimport \"fmt\"\n\ntype Logger struct{}\n\nfunc (l Logger) Log(msg string) { fmt.Println(msg) }\n\ntype Server struct {\n    Logger         \u002F\u002F встраивание — без имени поля\n    Host  string\n}\n\nfunc main() {\n    s := Server{Host: \"localhost\"}\n    s.Log(\"started\")        \u002F\u002F вызов \"всплыл\" — как будто метод Server\n    s.Logger.Log(\"started\") \u002F\u002F эквивалентно — явный вызов\n}\n",[86,1438,1439,1446,1450,1464,1468,1479,1483,1516,1520,1531,1539,1547,1551,1555,1565,1582,1600,1616],{"__ignoreMap":104},[108,1440,1441,1443],{"class":110,"line":111},[108,1442,328],{"class":121},[108,1444,1445],{"class":125}," main\n",[108,1447,1448],{"class":110,"line":118},[108,1449,169],{"emptyLinePlaceholder":168},[108,1451,1452,1455,1458,1461],{"class":110,"line":136},[108,1453,1454],{"class":121},"import",[108,1456,1457],{"class":522}," \"",[108,1459,1460],{"class":125},"fmt",[108,1462,1463],{"class":522},"\"\n",[108,1465,1466],{"class":110,"line":148},[108,1467,169],{"emptyLinePlaceholder":168},[108,1469,1470,1472,1475,1477],{"class":110,"line":159},[108,1471,122],{"class":121},[108,1473,1474],{"class":125}," Logger",[108,1476,129],{"class":121},[108,1478,969],{"class":132},[108,1480,1481],{"class":110,"line":165},[108,1482,169],{"emptyLinePlaceholder":168},[108,1484,1485,1487,1489,1492,1495,1497,1500,1502,1505,1507,1510,1513],{"class":110,"line":172},[108,1486,181],{"class":121},[108,1488,249],{"class":132},[108,1490,1491],{"class":190},"l ",[108,1493,1494],{"class":125},"Logger",[108,1496,203],{"class":132},[108,1498,1499],{"class":125},"Log",[108,1501,187],{"class":132},[108,1503,1504],{"class":190},"msg",[108,1506,200],{"class":121},[108,1508,1509],{"class":132},") { fmt.",[108,1511,1512],{"class":125},"Println",[108,1514,1515],{"class":132},"(msg) }\n",[108,1517,1518],{"class":110,"line":178},[108,1519,169],{"emptyLinePlaceholder":168},[108,1521,1522,1524,1527,1529],{"class":110,"line":214},[108,1523,122],{"class":121},[108,1525,1526],{"class":125}," Server",[108,1528,129],{"class":121},[108,1530,133],{"class":132},[108,1532,1533,1536],{"class":110,"line":228},[108,1534,1535],{"class":125},"    Logger",[108,1537,1538],{"class":114},"         \u002F\u002F встраивание — без имени поля\n",[108,1540,1541,1544],{"class":110,"line":233},[108,1542,1543],{"class":132},"    Host  ",[108,1545,1546],{"class":121},"string\n",[108,1548,1549],{"class":110,"line":238},[108,1550,162],{"class":132},[108,1552,1553],{"class":110,"line":244},[108,1554,169],{"emptyLinePlaceholder":168},[108,1556,1557,1559,1562],{"class":110,"line":835},[108,1558,181],{"class":121},[108,1560,1561],{"class":125}," main",[108,1563,1564],{"class":132},"() {\n",[108,1566,1567,1570,1572,1574,1577,1580],{"class":110,"line":843},[108,1568,1569],{"class":132},"    s ",[108,1571,824],{"class":121},[108,1573,1526],{"class":125},[108,1575,1576],{"class":132},"{Host: ",[108,1578,1579],{"class":522},"\"localhost\"",[108,1581,162],{"class":132},[108,1583,1584,1587,1589,1591,1594,1597],{"class":110,"line":848},[108,1585,1586],{"class":132},"    s.",[108,1588,1499],{"class":125},[108,1590,187],{"class":132},[108,1592,1593],{"class":522},"\"started\"",[108,1595,1596],{"class":132},")        ",[108,1598,1599],{"class":114},"\u002F\u002F вызов \"всплыл\" — как будто метод Server\n",[108,1601,1602,1605,1607,1609,1611,1613],{"class":110,"line":853},[108,1603,1604],{"class":132},"    s.Logger.",[108,1606,1499],{"class":125},[108,1608,187],{"class":132},[108,1610,1593],{"class":522},[108,1612,203],{"class":132},[108,1614,1615],{"class":114},"\u002F\u002F эквивалентно — явный вызов\n",[108,1617,1618],{"class":110,"line":877},[108,1619,162],{"class":132},[11,1621,1622,1623,1626],{},"Компилятор ",[15,1624,1625],{},"не копирует"," методы. Он генерирует обёртку:",[99,1628,1630],{"className":101,"code":1629,"language":103,"meta":104,"style":104},"\u002F\u002F Компилятор неявно создаёт:\nfunc (s Server) Log(msg string) { s.Logger.Log(msg) }\n",[86,1631,1632,1637],{"__ignoreMap":104},[108,1633,1634],{"class":110,"line":111},[108,1635,1636],{"class":114},"\u002F\u002F Компилятор неявно создаёт:\n",[108,1638,1639,1641,1643,1646,1649,1651,1653,1655,1657,1659,1662,1664],{"class":110,"line":118},[108,1640,181],{"class":121},[108,1642,249],{"class":132},[108,1644,1645],{"class":190},"s ",[108,1647,1648],{"class":125},"Server",[108,1650,203],{"class":132},[108,1652,1499],{"class":125},[108,1654,187],{"class":132},[108,1656,1504],{"class":190},[108,1658,200],{"class":121},[108,1660,1661],{"class":132},") { s.Logger.",[108,1663,1499],{"class":125},[108,1665,1515],{"class":132},[315,1667,1669],{"id":1668},"чем-это-не-наследование","Чем это НЕ наследование",[11,1671,1672,1675],{},[15,1673,1674],{},"Нет is-a",": Server не является Logger. Нельзя передать Server туда, где ждут Logger.",[99,1677,1679],{"className":101,"code":1678,"language":103,"meta":104,"style":104},"func useLogger(l Logger) {}\nuseLogger(s)         \u002F\u002F ОШИБКА компиляции\nuseLogger(s.Logger)  \u002F\u002F ОК — явно достаём встроенное поле\n",[86,1680,1681,1698,1709],{"__ignoreMap":104},[108,1682,1683,1685,1688,1690,1693,1695],{"class":110,"line":111},[108,1684,181],{"class":121},[108,1686,1687],{"class":125}," useLogger",[108,1689,187],{"class":132},[108,1691,1692],{"class":190},"l",[108,1694,1474],{"class":125},[108,1696,1697],{"class":132},") {}\n",[108,1699,1700,1703,1706],{"class":110,"line":118},[108,1701,1702],{"class":125},"useLogger",[108,1704,1705],{"class":132},"(s)         ",[108,1707,1708],{"class":114},"\u002F\u002F ОШИБКА компиляции\n",[108,1710,1711,1713,1716],{"class":110,"line":136},[108,1712,1702],{"class":125},[108,1714,1715],{"class":132},"(s.Logger)  ",[108,1717,1718],{"class":114},"\u002F\u002F ОК — явно достаём встроенное поле\n",[11,1720,1721,1724],{},[15,1722,1723],{},"Нет виртуальных методов",": встроенный тип не знает о внешнем.",[99,1726,1728],{"className":101,"code":1727,"language":103,"meta":104,"style":104},"type Base struct{}\nfunc (b Base) Name() string { return \"base\" }\nfunc (b Base) Hello() string { return \"hello from \" + b.Name() }\n\ntype Child struct{ Base }\nfunc (c Child) Name() string { return \"child\" }\n\nc := Child{}\nc.Hello()  \u002F\u002F \"hello from base\" — НЕ \"hello from child\"!\n           \u002F\u002F Base.Hello() вызывает Base.Name(), не Child.Name()\n",[86,1729,1730,1741,1769,1805,1809,1825,1854,1858,1868,1881],{"__ignoreMap":104},[108,1731,1732,1734,1737,1739],{"class":110,"line":111},[108,1733,122],{"class":121},[108,1735,1736],{"class":125}," Base",[108,1738,129],{"class":121},[108,1740,969],{"class":132},[108,1742,1743,1745,1747,1749,1752,1754,1756,1758,1760,1762,1764,1767],{"class":110,"line":118},[108,1744,181],{"class":121},[108,1746,249],{"class":132},[108,1748,747],{"class":190},[108,1750,1751],{"class":125},"Base",[108,1753,203],{"class":132},[108,1755,261],{"class":125},[108,1757,264],{"class":132},[108,1759,142],{"class":121},[108,1761,269],{"class":132},[108,1763,272],{"class":121},[108,1765,1766],{"class":522}," \"base\"",[108,1768,482],{"class":132},[108,1770,1771,1773,1775,1777,1779,1781,1784,1786,1788,1790,1792,1795,1797,1800,1802],{"class":110,"line":136},[108,1772,181],{"class":121},[108,1774,249],{"class":132},[108,1776,747],{"class":190},[108,1778,1751],{"class":125},[108,1780,203],{"class":132},[108,1782,1783],{"class":125},"Hello",[108,1785,264],{"class":132},[108,1787,142],{"class":121},[108,1789,269],{"class":132},[108,1791,272],{"class":121},[108,1793,1794],{"class":522}," \"hello from \"",[108,1796,1262],{"class":121},[108,1798,1799],{"class":132}," b.",[108,1801,261],{"class":125},[108,1803,1804],{"class":132},"() }\n",[108,1806,1807],{"class":110,"line":148},[108,1808,169],{"emptyLinePlaceholder":168},[108,1810,1811,1813,1816,1818,1821,1823],{"class":110,"line":159},[108,1812,122],{"class":121},[108,1814,1815],{"class":125}," Child",[108,1817,129],{"class":121},[108,1819,1820],{"class":132},"{ ",[108,1822,1751],{"class":125},[108,1824,482],{"class":132},[108,1826,1827,1829,1831,1834,1837,1839,1841,1843,1845,1847,1849,1852],{"class":110,"line":165},[108,1828,181],{"class":121},[108,1830,249],{"class":132},[108,1832,1833],{"class":190},"c ",[108,1835,1836],{"class":125},"Child",[108,1838,203],{"class":132},[108,1840,261],{"class":125},[108,1842,264],{"class":132},[108,1844,142],{"class":121},[108,1846,269],{"class":132},[108,1848,272],{"class":121},[108,1850,1851],{"class":522}," \"child\"",[108,1853,482],{"class":132},[108,1855,1856],{"class":110,"line":172},[108,1857,169],{"emptyLinePlaceholder":168},[108,1859,1860,1862,1864,1866],{"class":110,"line":178},[108,1861,1833],{"class":132},[108,1863,824],{"class":121},[108,1865,1815],{"class":125},[108,1867,969],{"class":132},[108,1869,1870,1873,1875,1878],{"class":110,"line":214},[108,1871,1872],{"class":132},"c.",[108,1874,1783],{"class":125},[108,1876,1877],{"class":132},"()  ",[108,1879,1880],{"class":114},"\u002F\u002F \"hello from base\" — НЕ \"hello from child\"!\n",[108,1882,1883],{"class":110,"line":228},[108,1884,1885],{"class":114},"           \u002F\u002F Base.Hello() вызывает Base.Name(), не Child.Name()\n",[11,1887,1888],{},"В Java\u002FC++ было бы \"hello from child\" (виртуальная диспетчеризация). В Go — нет.",[11,1890,1891,1894],{},[15,1892,1893],{},"Нет super",": нельзя вызвать \"родительскую\" версию метода, потому что нет родителя.",[315,1896,1898],{"id":1897},"композиция-без-встраивания","Композиция без встраивания",[99,1900,1902],{"className":101,"code":1901,"language":103,"meta":104,"style":104},"type Server struct {\n    logger Logger   \u002F\u002F обычное поле — методы НЕ всплывают\n    Host   string\n}\n\ns.Log(\"x\")         \u002F\u002F ОШИБКА — нет такого метода\ns.logger.Log(\"x\")  \u002F\u002F ОК — через поле\n",[86,1903,1904,1914,1924,1931,1935,1939,1957],{"__ignoreMap":104},[108,1905,1906,1908,1910,1912],{"class":110,"line":111},[108,1907,122],{"class":121},[108,1909,1526],{"class":125},[108,1911,129],{"class":121},[108,1913,133],{"class":132},[108,1915,1916,1919,1921],{"class":110,"line":118},[108,1917,1918],{"class":132},"    logger ",[108,1920,1494],{"class":125},[108,1922,1923],{"class":114},"   \u002F\u002F обычное поле — методы НЕ всплывают\n",[108,1925,1926,1929],{"class":110,"line":136},[108,1927,1928],{"class":132},"    Host   ",[108,1930,1546],{"class":121},[108,1932,1933],{"class":110,"line":148},[108,1934,162],{"class":132},[108,1936,1937],{"class":110,"line":159},[108,1938,169],{"emptyLinePlaceholder":168},[108,1940,1941,1944,1946,1948,1951,1954],{"class":110,"line":165},[108,1942,1943],{"class":132},"s.",[108,1945,1499],{"class":125},[108,1947,187],{"class":132},[108,1949,1950],{"class":522},"\"x\"",[108,1952,1953],{"class":132},")         ",[108,1955,1956],{"class":114},"\u002F\u002F ОШИБКА — нет такого метода\n",[108,1958,1959,1962,1964,1966,1968,1970],{"class":110,"line":172},[108,1960,1961],{"class":132},"s.logger.",[108,1963,1499],{"class":125},[108,1965,187],{"class":132},[108,1967,1950],{"class":522},[108,1969,1191],{"class":132},[108,1971,1972],{"class":114},"\u002F\u002F ОК — через поле\n",[315,1974,1976],{"id":1975},"когда-что-использовать","Когда что использовать",[11,1978,1979,1982],{},[15,1980,1981],{},"Встраивание",": когда хочешь \"проксировать\" интерфейс (io.ReadWriter встраивает Reader + Writer).",[11,1984,1985,1988],{},[15,1986,1987],{},"Обычное поле",": когда зависимость — деталь реализации, не часть API.",[308,1990],{},[285,1992,1993,1996,2002],{},[288,1994,1995],{},"Конвенция Go: геттер = Name(), НЕ GetName(). Сеттер = SetName(). Геттер без префикса Get",[288,1997,1998,2001],{},[15,1999,2000],{},"Встраивание через указатель"," (*Logger) — методы всплывают так же, но zero value структуры содержит nil (паника при вызове)",[288,2003,2004],{},"Встраивание указателя позволяет нескольким объектам разделять один экземпляр встроенного типа",[308,2006],{},[277,2008,2010],{"id":2009},"именование-и-инкапсуляция","Именование и инкапсуляция",[315,2012,2014],{"id":2013},"конвенция-именования","Конвенция именования",[99,2016,2018],{"className":101,"code":2017,"language":103,"meta":104,"style":104},"\u002F\u002F ПЛОХО — Java-стиль\nfunc (u *User) GetName() string { return u.name }\nfunc (u *User) GetEmail() string { return u.email }\n\n\u002F\u002F ХОРОШО — Go-стиль\nfunc (u *User) Name() string { return u.name }\nfunc (u *User) Email() string { return u.email }\nfunc (u *User) SetEmail(e string) { u.email = e }  \u002F\u002F сеттер — с префиксом Set\n",[86,2019,2020,2025,2052,2080,2084,2089,2115,2141],{"__ignoreMap":104},[108,2021,2022],{"class":110,"line":111},[108,2023,2024],{"class":114},"\u002F\u002F ПЛОХО — Java-стиль\n",[108,2026,2027,2029,2031,2033,2035,2037,2039,2042,2044,2046,2048,2050],{"class":110,"line":118},[108,2028,181],{"class":121},[108,2030,249],{"class":132},[108,2032,252],{"class":190},[108,2034,206],{"class":121},[108,2036,209],{"class":125},[108,2038,203],{"class":132},[108,2040,2041],{"class":125},"GetName",[108,2043,264],{"class":132},[108,2045,142],{"class":121},[108,2047,269],{"class":132},[108,2049,272],{"class":121},[108,2051,275],{"class":132},[108,2053,2054,2056,2058,2060,2062,2064,2066,2069,2071,2073,2075,2077],{"class":110,"line":136},[108,2055,181],{"class":121},[108,2057,249],{"class":132},[108,2059,252],{"class":190},[108,2061,206],{"class":121},[108,2063,209],{"class":125},[108,2065,203],{"class":132},[108,2067,2068],{"class":125},"GetEmail",[108,2070,264],{"class":132},[108,2072,142],{"class":121},[108,2074,269],{"class":132},[108,2076,272],{"class":121},[108,2078,2079],{"class":132}," u.email }\n",[108,2081,2082],{"class":110,"line":148},[108,2083,169],{"emptyLinePlaceholder":168},[108,2085,2086],{"class":110,"line":159},[108,2087,2088],{"class":114},"\u002F\u002F ХОРОШО — Go-стиль\n",[108,2090,2091,2093,2095,2097,2099,2101,2103,2105,2107,2109,2111,2113],{"class":110,"line":165},[108,2092,181],{"class":121},[108,2094,249],{"class":132},[108,2096,252],{"class":190},[108,2098,206],{"class":121},[108,2100,209],{"class":125},[108,2102,203],{"class":132},[108,2104,261],{"class":125},[108,2106,264],{"class":132},[108,2108,142],{"class":121},[108,2110,269],{"class":132},[108,2112,272],{"class":121},[108,2114,275],{"class":132},[108,2116,2117,2119,2121,2123,2125,2127,2129,2131,2133,2135,2137,2139],{"class":110,"line":172},[108,2118,181],{"class":121},[108,2120,249],{"class":132},[108,2122,252],{"class":190},[108,2124,206],{"class":121},[108,2126,209],{"class":125},[108,2128,203],{"class":132},[108,2130,390],{"class":125},[108,2132,264],{"class":132},[108,2134,142],{"class":121},[108,2136,269],{"class":132},[108,2138,272],{"class":121},[108,2140,2079],{"class":132},[108,2142,2143,2145,2147,2149,2151,2153,2155,2158,2160,2163,2165,2168,2170,2173],{"class":110,"line":178},[108,2144,181],{"class":121},[108,2146,249],{"class":132},[108,2148,252],{"class":190},[108,2150,206],{"class":121},[108,2152,209],{"class":125},[108,2154,203],{"class":132},[108,2156,2157],{"class":125},"SetEmail",[108,2159,187],{"class":132},[108,2161,2162],{"class":190},"e",[108,2164,200],{"class":121},[108,2166,2167],{"class":132},") { u.email ",[108,2169,519],{"class":121},[108,2171,2172],{"class":132}," e }  ",[108,2174,2175],{"class":114},"\u002F\u002F сеттер — с префиксом Set\n",[11,2177,2178],{},"Effective Go: \"если поле называется owner, геттер — Owner(), не GetOwner()\".",[315,2180,2182],{"id":2181},"когда-нужны-геттерысеттеры","Когда нужны геттеры\u002Fсеттеры",[11,2184,2185,2188],{},[15,2186,2187],{},"Нужны",": приватное поле, нужна валидация или побочный эффект при изменении.",[99,2190,2192],{"className":101,"code":2191,"language":103,"meta":104,"style":104},"func (a *Account) SetBalance(b int) error {\n    if b \u003C 0 { return errors.New(\"negative balance\") }\n    a.balance = b\n    return nil\n}\n",[86,2193,2194,2226,2257,2267,2274],{"__ignoreMap":104},[108,2195,2196,2198,2200,2203,2205,2208,2210,2213,2215,2218,2220,2222,2224],{"class":110,"line":111},[108,2197,181],{"class":121},[108,2199,249],{"class":132},[108,2201,2202],{"class":190},"a ",[108,2204,206],{"class":121},[108,2206,2207],{"class":125},"Account",[108,2209,203],{"class":132},[108,2211,2212],{"class":125},"SetBalance",[108,2214,187],{"class":132},[108,2216,2217],{"class":190},"b",[108,2219,638],{"class":121},[108,2221,203],{"class":132},[108,2223,710],{"class":121},[108,2225,133],{"class":132},[108,2227,2228,2231,2234,2237,2240,2242,2244,2247,2250,2252,2255],{"class":110,"line":118},[108,2229,2230],{"class":121},"    if",[108,2232,2233],{"class":132}," b ",[108,2235,2236],{"class":121},"\u003C",[108,2238,2239],{"class":1187}," 0",[108,2241,269],{"class":132},[108,2243,272],{"class":121},[108,2245,2246],{"class":132}," errors.",[108,2248,2249],{"class":125},"New",[108,2251,187],{"class":132},[108,2253,2254],{"class":522},"\"negative balance\"",[108,2256,1097],{"class":132},[108,2258,2259,2262,2264],{"class":110,"line":136},[108,2260,2261],{"class":132},"    a.balance ",[108,2263,519],{"class":121},[108,2265,2266],{"class":132}," b\n",[108,2268,2269,2271],{"class":110,"line":148},[108,2270,217],{"class":121},[108,2272,2273],{"class":1187}," nil\n",[108,2275,2276],{"class":110,"line":159},[108,2277,162],{"class":132},[11,2279,2280,2283],{},[15,2281,2282],{},"Не нужны",": если поле можно сделать экспортированным без последствий — сделай публичным. Go не заставляет оборачивать всё в геттеры.",[99,2285,2287],{"className":101,"code":2286,"language":103,"meta":104,"style":104},"type Point struct {\n    X, Y float64  \u002F\u002F публичные поля — норма для простых структур\n}\n",[86,2288,2289,2300,2311],{"__ignoreMap":104},[108,2290,2291,2293,2296,2298],{"class":110,"line":111},[108,2292,122],{"class":121},[108,2294,2295],{"class":125}," Point",[108,2297,129],{"class":121},[108,2299,133],{"class":132},[108,2301,2302,2305,2308],{"class":110,"line":118},[108,2303,2304],{"class":132},"    X, Y ",[108,2306,2307],{"class":121},"float64",[108,2309,2310],{"class":114},"  \u002F\u002F публичные поля — норма для простых структур\n",[108,2312,2313],{"class":110,"line":136},[108,2314,162],{"class":132},[315,2316,2000],{"id":2317},"встраивание-через-указатель",[99,2319,2321],{"className":101,"code":2320,"language":103,"meta":104,"style":104},"package main\n\nimport \"fmt\"\n\ntype Logger struct{ prefix string }\n\nfunc (l *Logger) Log(msg string) { fmt.Println(l.prefix + \": \" + msg) }\n\ntype App struct {\n    *Logger  \u002F\u002F встраивание УКАЗАТЕЛЯ\n}\n\nfunc main() {\n    shared := &Logger{prefix: \"APP\"}\n    a := App{Logger: shared}\n    a.Log(\"started\")\n}\n",[86,2322,2323,2329,2333,2343,2347,2362,2366,2406,2410,2421,2431,2435,2439,2447,2466,2478,2491],{"__ignoreMap":104},[108,2324,2325,2327],{"class":110,"line":111},[108,2326,328],{"class":121},[108,2328,1445],{"class":125},[108,2330,2331],{"class":110,"line":118},[108,2332,169],{"emptyLinePlaceholder":168},[108,2334,2335,2337,2339,2341],{"class":110,"line":136},[108,2336,1454],{"class":121},[108,2338,1457],{"class":522},[108,2340,1460],{"class":125},[108,2342,1463],{"class":522},[108,2344,2345],{"class":110,"line":148},[108,2346,169],{"emptyLinePlaceholder":168},[108,2348,2349,2351,2353,2355,2358,2360],{"class":110,"line":159},[108,2350,122],{"class":121},[108,2352,1474],{"class":125},[108,2354,129],{"class":121},[108,2356,2357],{"class":132},"{ prefix ",[108,2359,142],{"class":121},[108,2361,482],{"class":132},[108,2363,2364],{"class":110,"line":165},[108,2365,169],{"emptyLinePlaceholder":168},[108,2367,2368,2370,2372,2374,2376,2378,2380,2382,2384,2386,2388,2390,2392,2395,2398,2401,2403],{"class":110,"line":172},[108,2369,181],{"class":121},[108,2371,249],{"class":132},[108,2373,1491],{"class":190},[108,2375,206],{"class":121},[108,2377,1494],{"class":125},[108,2379,203],{"class":132},[108,2381,1499],{"class":125},[108,2383,187],{"class":132},[108,2385,1504],{"class":190},[108,2387,200],{"class":121},[108,2389,1509],{"class":132},[108,2391,1512],{"class":125},[108,2393,2394],{"class":132},"(l.prefix ",[108,2396,2397],{"class":121},"+",[108,2399,2400],{"class":522}," \": \"",[108,2402,1262],{"class":121},[108,2404,2405],{"class":132}," msg) }\n",[108,2407,2408],{"class":110,"line":178},[108,2409,169],{"emptyLinePlaceholder":168},[108,2411,2412,2414,2417,2419],{"class":110,"line":214},[108,2413,122],{"class":121},[108,2415,2416],{"class":125}," App",[108,2418,129],{"class":121},[108,2420,133],{"class":132},[108,2422,2423,2426,2428],{"class":110,"line":228},[108,2424,2425],{"class":121},"    *",[108,2427,1494],{"class":125},[108,2429,2430],{"class":114},"  \u002F\u002F встраивание УКАЗАТЕЛЯ\n",[108,2432,2433],{"class":110,"line":233},[108,2434,162],{"class":132},[108,2436,2437],{"class":110,"line":238},[108,2438,169],{"emptyLinePlaceholder":168},[108,2440,2441,2443,2445],{"class":110,"line":244},[108,2442,181],{"class":121},[108,2444,1561],{"class":125},[108,2446,1564],{"class":132},[108,2448,2449,2452,2454,2456,2458,2461,2464],{"class":110,"line":835},[108,2450,2451],{"class":132},"    shared ",[108,2453,824],{"class":121},[108,2455,220],{"class":121},[108,2457,1494],{"class":125},[108,2459,2460],{"class":132},"{prefix: ",[108,2462,2463],{"class":522},"\"APP\"",[108,2465,162],{"class":132},[108,2467,2468,2471,2473,2475],{"class":110,"line":843},[108,2469,2470],{"class":132},"    a ",[108,2472,824],{"class":121},[108,2474,2416],{"class":125},[108,2476,2477],{"class":132},"{Logger: shared}\n",[108,2479,2480,2483,2485,2487,2489],{"class":110,"line":848},[108,2481,2482],{"class":132},"    a.",[108,2484,1499],{"class":125},[108,2486,187],{"class":132},[108,2488,1593],{"class":522},[108,2490,649],{"class":132},[108,2492,2493],{"class":110,"line":853},[108,2494,162],{"class":132},[11,2496,2497,2500],{},[15,2498,2499],{},"Плюс",": несколько App могут разделять один Logger.",[99,2502,2504],{"className":101,"code":2503,"language":103,"meta":104,"style":104},"shared := &Logger{prefix: \"APP\"}\na1 := App{Logger: shared}\na2 := App{Logger: shared}  \u002F\u002F тот же логгер\n",[86,2505,2506,2523,2534],{"__ignoreMap":104},[108,2507,2508,2511,2513,2515,2517,2519,2521],{"class":110,"line":111},[108,2509,2510],{"class":132},"shared ",[108,2512,824],{"class":121},[108,2514,220],{"class":121},[108,2516,1494],{"class":125},[108,2518,2460],{"class":132},[108,2520,2463],{"class":522},[108,2522,162],{"class":132},[108,2524,2525,2528,2530,2532],{"class":110,"line":118},[108,2526,2527],{"class":132},"a1 ",[108,2529,824],{"class":121},[108,2531,2416],{"class":125},[108,2533,2477],{"class":132},[108,2535,2536,2539,2541,2543,2546],{"class":110,"line":136},[108,2537,2538],{"class":132},"a2 ",[108,2540,824],{"class":121},[108,2542,2416],{"class":125},[108,2544,2545],{"class":132},"{Logger: shared}  ",[108,2547,2548],{"class":114},"\u002F\u002F тот же логгер\n",[11,2550,2551,2554],{},[15,2552,2553],{},"Минус",": zero value содержит nil — паника.",[99,2556,2558],{"className":101,"code":2557,"language":103,"meta":104,"style":104},"a := App{}   \u002F\u002F a.Logger == nil\na.Log(\"x\")  \u002F\u002F ПАНИКА: nil pointer dereference\n",[86,2559,2560,2574],{"__ignoreMap":104},[108,2561,2562,2564,2566,2568,2571],{"class":110,"line":111},[108,2563,2202],{"class":132},[108,2565,824],{"class":121},[108,2567,2416],{"class":125},[108,2569,2570],{"class":132},"{}   ",[108,2572,2573],{"class":114},"\u002F\u002F a.Logger == nil\n",[108,2575,2576,2579,2581,2583,2585,2587],{"class":110,"line":118},[108,2577,2578],{"class":132},"a.",[108,2580,1499],{"class":125},[108,2582,187],{"class":132},[108,2584,1950],{"class":522},[108,2586,1191],{"class":132},[108,2588,2589],{"class":114},"\u002F\u002F ПАНИКА: nil pointer dereference\n",[11,2591,2592],{},"При встраивании по значению паники не будет:",[99,2594,2596],{"className":101,"code":2595,"language":103,"meta":104,"style":104},"type App struct {\n    Logger  \u002F\u002F по значению — zero value Logger, не nil\n}\na := App{}\na.Log(\"x\")  \u002F\u002F ОК — вызовется на zero value Logger\n",[86,2597,2598,2608,2615,2619,2629],{"__ignoreMap":104},[108,2599,2600,2602,2604,2606],{"class":110,"line":111},[108,2601,122],{"class":121},[108,2603,2416],{"class":125},[108,2605,129],{"class":121},[108,2607,133],{"class":132},[108,2609,2610,2612],{"class":110,"line":118},[108,2611,1535],{"class":125},[108,2613,2614],{"class":114},"  \u002F\u002F по значению — zero value Logger, не nil\n",[108,2616,2617],{"class":110,"line":136},[108,2618,162],{"class":132},[108,2620,2621,2623,2625,2627],{"class":110,"line":148},[108,2622,2202],{"class":132},[108,2624,824],{"class":121},[108,2626,2416],{"class":125},[108,2628,969],{"class":132},[108,2630,2631,2633,2635,2637,2639,2641],{"class":110,"line":159},[108,2632,2578],{"class":132},[108,2634,1499],{"class":125},[108,2636,187],{"class":132},[108,2638,1950],{"class":522},[108,2640,1191],{"class":132},[108,2642,2643],{"class":114},"\u002F\u002F ОК — вызовется на zero value Logger\n",[277,2645,81],{"id":2646},"конструкторы",[315,2648,318],{"id":2649},"правило-1",[11,2651,2652],{},"Встраивай по значению если zero value встроенного типа работоспособен. Встраивай указатель если нужно разделять экземпляр или если тип работает только через конструктор.",[308,2654],{},[285,2656,2657,2660,2663],{},[288,2658,2659],{},"В Go нет конструкторов. Конвенция: функция NewXxx() возвращает инициализированный объект",[288,2661,2662],{},"Возвращай *struct (конкретный тип), не интерфейс — \"Accept interfaces, return structs\"",[288,2664,2665],{},"Если zero value работоспособен (sync.Mutex, bytes.Buffer) — конструктор не нужен",[308,2667],{},[315,2669,2671],{"id":2670},"базовый-паттерн","Базовый паттерн",[99,2673,2675],{"className":101,"code":2674,"language":103,"meta":104,"style":104},"type Server struct {\n    host string\n    port int\n    log  *log.Logger\n}\n\nfunc NewServer(host string, port int) *Server {\n    return &Server{\n        host: host,\n        port: port,\n        log:  log.Default(),\n    }\n}\n",[86,2676,2677,2687,2694,2702,2718,2722,2726,2755,2766,2771,2776,2787,2791],{"__ignoreMap":104},[108,2678,2679,2681,2683,2685],{"class":110,"line":111},[108,2680,122],{"class":121},[108,2682,1526],{"class":125},[108,2684,129],{"class":121},[108,2686,133],{"class":132},[108,2688,2689,2692],{"class":110,"line":118},[108,2690,2691],{"class":132},"    host ",[108,2693,1546],{"class":121},[108,2695,2696,2699],{"class":110,"line":136},[108,2697,2698],{"class":132},"    port ",[108,2700,2701],{"class":121},"int\n",[108,2703,2704,2707,2709,2712,2715],{"class":110,"line":148},[108,2705,2706],{"class":132},"    log  ",[108,2708,206],{"class":121},[108,2710,2711],{"class":125},"log",[108,2713,2714],{"class":132},".",[108,2716,2717],{"class":125},"Logger\n",[108,2719,2720],{"class":110,"line":159},[108,2721,162],{"class":132},[108,2723,2724],{"class":110,"line":165},[108,2725,169],{"emptyLinePlaceholder":168},[108,2727,2728,2730,2733,2735,2738,2740,2742,2745,2747,2749,2751,2753],{"class":110,"line":172},[108,2729,181],{"class":121},[108,2731,2732],{"class":125}," NewServer",[108,2734,187],{"class":132},[108,2736,2737],{"class":190},"host",[108,2739,200],{"class":121},[108,2741,194],{"class":132},[108,2743,2744],{"class":190},"port",[108,2746,638],{"class":121},[108,2748,203],{"class":132},[108,2750,206],{"class":121},[108,2752,1648],{"class":125},[108,2754,133],{"class":132},[108,2756,2757,2759,2761,2763],{"class":110,"line":178},[108,2758,217],{"class":121},[108,2760,220],{"class":121},[108,2762,1648],{"class":125},[108,2764,2765],{"class":132},"{\n",[108,2767,2768],{"class":110,"line":214},[108,2769,2770],{"class":132},"        host: host,\n",[108,2772,2773],{"class":110,"line":228},[108,2774,2775],{"class":132},"        port: port,\n",[108,2777,2778,2781,2784],{"class":110,"line":233},[108,2779,2780],{"class":132},"        log:  log.",[108,2782,2783],{"class":125},"Default",[108,2785,2786],{"class":132},"(),\n",[108,2788,2789],{"class":110,"line":238},[108,2790,1342],{"class":132},[108,2792,2793],{"class":110,"line":244},[108,2794,162],{"class":132},[315,2796,2798],{"id":2797},"почему-возвращать-struct-а-не-интерфейс","Почему возвращать *struct, а не интерфейс",[99,2800,2802],{"className":101,"code":2801,"language":103,"meta":104,"style":104},"\u002F\u002F ПЛОХО\nfunc NewServer(host string) ServerInterface { return &Server{host: host} }\n\n\u002F\u002F ХОРОШО\nfunc NewServer(host string) *Server { return &Server{host: host} }\n",[86,2803,2804,2809,2837,2841,2846],{"__ignoreMap":104},[108,2805,2806],{"class":110,"line":111},[108,2807,2808],{"class":114},"\u002F\u002F ПЛОХО\n",[108,2810,2811,2813,2815,2817,2819,2821,2823,2826,2828,2830,2832,2834],{"class":110,"line":118},[108,2812,181],{"class":121},[108,2814,2732],{"class":125},[108,2816,187],{"class":132},[108,2818,2737],{"class":190},[108,2820,200],{"class":121},[108,2822,203],{"class":132},[108,2824,2825],{"class":125},"ServerInterface",[108,2827,269],{"class":132},[108,2829,272],{"class":121},[108,2831,220],{"class":121},[108,2833,1648],{"class":125},[108,2835,2836],{"class":132},"{host: host} }\n",[108,2838,2839],{"class":110,"line":136},[108,2840,169],{"emptyLinePlaceholder":168},[108,2842,2843],{"class":110,"line":148},[108,2844,2845],{"class":114},"\u002F\u002F ХОРОШО\n",[108,2847,2848,2850,2852,2854,2856,2858,2860,2862,2864,2866,2868,2870,2872],{"class":110,"line":159},[108,2849,181],{"class":121},[108,2851,2732],{"class":125},[108,2853,187],{"class":132},[108,2855,2737],{"class":190},[108,2857,200],{"class":121},[108,2859,203],{"class":132},[108,2861,206],{"class":121},[108,2863,1648],{"class":125},[108,2865,269],{"class":132},[108,2867,272],{"class":121},[108,2869,220],{"class":121},[108,2871,1648],{"class":125},[108,2873,2836],{"class":132},[11,2875,2876,2879],{},[15,2877,2878],{},"\"Accept interfaces, return structs\"",":",[285,2881,2882,2885,2888],{},[288,2883,2884],{},"Вызывающий код решает, за какой интерфейс держать объект",[288,2886,2887],{},"Конкретный тип даёт доступ ко всем методам, не только к подмножеству интерфейса",[288,2889,2890],{},"Проще тестировать, проще рефакторить",[11,2892,2893],{},"Исключение: когда возвращаемый тип — реально скрытая деталь (unexported struct).",[315,2895,2897],{"id":2896},"functional-options","Functional Options",[11,2899,2900],{},"Для множества необязательных параметров:",[99,2902,2904],{"className":101,"code":2903,"language":103,"meta":104,"style":104},"type Option func(*Server)\n\nfunc WithPort(port int) Option {\n    return func(s *Server) { s.port = port }\n}\n\nfunc WithLogger(l *log.Logger) Option {\n    return func(s *Server) { s.log = l }\n}\n\nfunc NewServer(host string, opts ...Option) *Server {\n    s := &Server{host: host, port: 8080, log: log.Default()}\n    for _, opt := range opts {\n        opt(s)\n    }\n    return s\n}\n\n\u002F\u002F Использование:\ns := NewServer(\"localhost\", WithPort(9090), WithLogger(myLog))\n",[86,2905,2906,2924,2928,2948,2971,2975,2979,3004,3026,3030,3034,3064,3088,3104,3112,3116,3123,3127,3131,3137],{"__ignoreMap":104},[108,2907,2908,2910,2913,2916,2918,2920,2922],{"class":110,"line":111},[108,2909,122],{"class":121},[108,2911,2912],{"class":125}," Option",[108,2914,2915],{"class":121}," func",[108,2917,187],{"class":132},[108,2919,206],{"class":121},[108,2921,1648],{"class":125},[108,2923,649],{"class":132},[108,2925,2926],{"class":110,"line":118},[108,2927,169],{"emptyLinePlaceholder":168},[108,2929,2930,2932,2935,2937,2939,2941,2943,2946],{"class":110,"line":136},[108,2931,181],{"class":121},[108,2933,2934],{"class":125}," WithPort",[108,2936,187],{"class":132},[108,2938,2744],{"class":190},[108,2940,638],{"class":121},[108,2942,203],{"class":132},[108,2944,2945],{"class":125},"Option",[108,2947,133],{"class":132},[108,2949,2950,2952,2954,2956,2959,2961,2963,2966,2968],{"class":110,"line":148},[108,2951,217],{"class":121},[108,2953,2915],{"class":121},[108,2955,187],{"class":132},[108,2957,2958],{"class":190},"s",[108,2960,506],{"class":121},[108,2962,1648],{"class":125},[108,2964,2965],{"class":132},") { s.port ",[108,2967,519],{"class":121},[108,2969,2970],{"class":132}," port }\n",[108,2972,2973],{"class":110,"line":159},[108,2974,162],{"class":132},[108,2976,2977],{"class":110,"line":165},[108,2978,169],{"emptyLinePlaceholder":168},[108,2980,2981,2983,2986,2988,2990,2992,2994,2996,2998,3000,3002],{"class":110,"line":172},[108,2982,181],{"class":121},[108,2984,2985],{"class":125}," WithLogger",[108,2987,187],{"class":132},[108,2989,1692],{"class":190},[108,2991,506],{"class":121},[108,2993,2711],{"class":125},[108,2995,2714],{"class":132},[108,2997,1494],{"class":125},[108,2999,203],{"class":132},[108,3001,2945],{"class":125},[108,3003,133],{"class":132},[108,3005,3006,3008,3010,3012,3014,3016,3018,3021,3023],{"class":110,"line":178},[108,3007,217],{"class":121},[108,3009,2915],{"class":121},[108,3011,187],{"class":132},[108,3013,2958],{"class":190},[108,3015,506],{"class":121},[108,3017,1648],{"class":125},[108,3019,3020],{"class":132},") { s.log ",[108,3022,519],{"class":121},[108,3024,3025],{"class":132}," l }\n",[108,3027,3028],{"class":110,"line":214},[108,3029,162],{"class":132},[108,3031,3032],{"class":110,"line":228},[108,3033,169],{"emptyLinePlaceholder":168},[108,3035,3036,3038,3040,3042,3044,3046,3048,3051,3054,3056,3058,3060,3062],{"class":110,"line":233},[108,3037,181],{"class":121},[108,3039,2732],{"class":125},[108,3041,187],{"class":132},[108,3043,2737],{"class":190},[108,3045,200],{"class":121},[108,3047,194],{"class":132},[108,3049,3050],{"class":190},"opts",[108,3052,3053],{"class":121}," ...",[108,3055,2945],{"class":125},[108,3057,203],{"class":132},[108,3059,206],{"class":121},[108,3061,1648],{"class":125},[108,3063,133],{"class":132},[108,3065,3066,3068,3070,3072,3074,3077,3080,3083,3085],{"class":110,"line":238},[108,3067,1569],{"class":132},[108,3069,824],{"class":121},[108,3071,220],{"class":121},[108,3073,1648],{"class":125},[108,3075,3076],{"class":132},"{host: host, port: ",[108,3078,3079],{"class":1187},"8080",[108,3081,3082],{"class":132},", log: log.",[108,3084,2783],{"class":125},[108,3086,3087],{"class":132},"()}\n",[108,3089,3090,3093,3096,3098,3101],{"class":110,"line":244},[108,3091,3092],{"class":121},"    for",[108,3094,3095],{"class":132}," _, opt ",[108,3097,824],{"class":121},[108,3099,3100],{"class":121}," range",[108,3102,3103],{"class":132}," opts {\n",[108,3105,3106,3109],{"class":110,"line":835},[108,3107,3108],{"class":125},"        opt",[108,3110,3111],{"class":132},"(s)\n",[108,3113,3114],{"class":110,"line":843},[108,3115,1342],{"class":132},[108,3117,3118,3120],{"class":110,"line":848},[108,3119,217],{"class":121},[108,3121,3122],{"class":132}," s\n",[108,3124,3125],{"class":110,"line":853},[108,3126,162],{"class":132},[108,3128,3129],{"class":110,"line":877},[108,3130,169],{"emptyLinePlaceholder":168},[108,3132,3134],{"class":110,"line":3133},19,[108,3135,3136],{"class":114},"\u002F\u002F Использование:\n",[108,3138,3140,3142,3144,3146,3148,3150,3152,3155,3157,3160,3163,3166],{"class":110,"line":3139},20,[108,3141,1645],{"class":132},[108,3143,824],{"class":121},[108,3145,2732],{"class":125},[108,3147,187],{"class":132},[108,3149,1579],{"class":522},[108,3151,194],{"class":132},[108,3153,3154],{"class":125},"WithPort",[108,3156,187],{"class":132},[108,3158,3159],{"class":1187},"9090",[108,3161,3162],{"class":132},"), ",[108,3164,3165],{"class":125},"WithLogger",[108,3167,3168],{"class":132},"(myLog))\n",[315,3170,3172],{"id":3171},"когда-конструктор-не-нужен","Когда конструктор НЕ нужен",[11,3174,3175],{},"Если zero value пригоден к использованию — не пиши конструктор:",[99,3177,3179],{"className":101,"code":3178,"language":103,"meta":104,"style":104},"var mu sync.Mutex      \u002F\u002F готов к использованию\nvar buf bytes.Buffer   \u002F\u002F готов к использованию\nvar wg sync.WaitGroup  \u002F\u002F готов к использованию\n",[86,3180,3181,3199,3217],{"__ignoreMap":104},[108,3182,3183,3185,3188,3191,3193,3196],{"class":110,"line":111},[108,3184,1166],{"class":121},[108,3186,3187],{"class":132}," mu ",[108,3189,3190],{"class":125},"sync",[108,3192,2714],{"class":132},[108,3194,3195],{"class":125},"Mutex",[108,3197,3198],{"class":114},"      \u002F\u002F готов к использованию\n",[108,3200,3201,3203,3206,3209,3211,3214],{"class":110,"line":118},[108,3202,1166],{"class":121},[108,3204,3205],{"class":132}," buf ",[108,3207,3208],{"class":125},"bytes",[108,3210,2714],{"class":132},[108,3212,3213],{"class":125},"Buffer",[108,3215,3216],{"class":114},"   \u002F\u002F готов к использованию\n",[108,3218,3219,3221,3224,3226,3228,3231],{"class":110,"line":136},[108,3220,1166],{"class":121},[108,3222,3223],{"class":132}," wg ",[108,3225,3190],{"class":125},[108,3227,2714],{"class":132},[108,3229,3230],{"class":125},"WaitGroup",[108,3232,3233],{"class":114},"  \u002F\u002F готов к использованию\n",[11,3235,3236],{},"Это идиоматичный Go: проектируй структуры так, чтобы zero value был полезен.",[315,3238,3240],{"id":3239},"валидация","Валидация",[99,3242,3244],{"className":101,"code":3243,"language":103,"meta":104,"style":104},"func NewEmail(raw string) (Email, error) {\n    if !strings.Contains(raw, \"@\") {\n        return \"\", fmt.Errorf(\"invalid email: %s\", raw)\n    }\n    return Email(raw), nil\n}\n",[86,3245,3246,3270,3291,3316,3320,3333],{"__ignoreMap":104},[108,3247,3248,3250,3253,3255,3258,3260,3262,3264,3266,3268],{"class":110,"line":111},[108,3249,181],{"class":121},[108,3251,3252],{"class":125}," NewEmail",[108,3254,187],{"class":132},[108,3256,3257],{"class":190},"raw",[108,3259,200],{"class":121},[108,3261,632],{"class":132},[108,3263,390],{"class":125},[108,3265,194],{"class":132},[108,3267,710],{"class":121},[108,3269,511],{"class":132},[108,3271,3272,3274,3277,3280,3283,3286,3289],{"class":110,"line":118},[108,3273,2230],{"class":121},[108,3275,3276],{"class":121}," !",[108,3278,3279],{"class":132},"strings.",[108,3281,3282],{"class":125},"Contains",[108,3284,3285],{"class":132},"(raw, ",[108,3287,3288],{"class":522},"\"@\"",[108,3290,511],{"class":132},[108,3292,3293,3295,3297,3300,3303,3305,3308,3311,3313],{"class":110,"line":136},[108,3294,1256],{"class":121},[108,3296,523],{"class":522},[108,3298,3299],{"class":132},", fmt.",[108,3301,3302],{"class":125},"Errorf",[108,3304,187],{"class":132},[108,3306,3307],{"class":522},"\"invalid email: ",[108,3309,3310],{"class":1187},"%s",[108,3312,1294],{"class":522},[108,3314,3315],{"class":132},", raw)\n",[108,3317,3318],{"class":110,"line":148},[108,3319,1342],{"class":132},[108,3321,3322,3324,3327,3330],{"class":110,"line":159},[108,3323,217],{"class":121},[108,3325,3326],{"class":125}," Email",[108,3328,3329],{"class":132},"(raw), ",[108,3331,3332],{"class":1187},"nil\n",[108,3334,3335],{"class":110,"line":165},[108,3336,162],{"class":132},[11,3338,3339],{},"Конструктор — единственное место для валидации, т.к. в Go нет конструкторов класса.",[308,3341],{},[285,3343,3344,3353,3359],{},[288,3345,3346,3347,3350,3351,142],{},"Методы можно определять для ",[15,3348,3349],{},"ЛЮБОГО именованного типа",": type MyInt int, type Handler func(), type StringSlice ",[108,3352],{},[288,3354,3355,3358],{},[15,3356,3357],{},"Два ограничения",": тип должен быть определён в том же пакете, и нельзя на интерфейсе",[288,3360,3361],{},"Именованный тип на основе примитива — мощный паттерн: enum'ы, валидация, Stringer, sort.Interface",[308,3363],{},[277,3365,3367],{"id":3366},"методы-на-пользовательских-типах","Методы на пользовательских типах",[315,3369,3371],{"id":3370},"методы-на-примитивах","Методы на примитивах",[99,3373,3375],{"className":101,"code":3374,"language":103,"meta":104,"style":104},"package main\n\nimport \"fmt\"\n\ntype Celsius float64\ntype Fahrenheit float64\n\nfunc (c Celsius) ToFahrenheit() Fahrenheit {\n    return Fahrenheit(c*9\u002F5 + 32)\n}\n\nfunc (c Celsius) String() string {\n    return fmt.Sprintf(\"%.1f°C\", c)\n}\n\nfunc main() {\n    temp := Celsius(36.6)\n    fmt.Println(temp)                \u002F\u002F \"36.6°C\" (Stringer)\n    fmt.Printf(\"%.2f\\n\", temp.ToFahrenheit()) \u002F\u002F 97.88\n}\n",[86,3376,3377,3383,3387,3397,3401,3411,3420,3424,3447,3474,3478,3482,3503,3524,3528,3532,3540,3556,3569,3596],{"__ignoreMap":104},[108,3378,3379,3381],{"class":110,"line":111},[108,3380,328],{"class":121},[108,3382,1445],{"class":125},[108,3384,3385],{"class":110,"line":118},[108,3386,169],{"emptyLinePlaceholder":168},[108,3388,3389,3391,3393,3395],{"class":110,"line":136},[108,3390,1454],{"class":121},[108,3392,1457],{"class":522},[108,3394,1460],{"class":125},[108,3396,1463],{"class":522},[108,3398,3399],{"class":110,"line":148},[108,3400,169],{"emptyLinePlaceholder":168},[108,3402,3403,3405,3408],{"class":110,"line":159},[108,3404,122],{"class":121},[108,3406,3407],{"class":125}," Celsius",[108,3409,3410],{"class":121}," float64\n",[108,3412,3413,3415,3418],{"class":110,"line":165},[108,3414,122],{"class":121},[108,3416,3417],{"class":125}," Fahrenheit",[108,3419,3410],{"class":121},[108,3421,3422],{"class":110,"line":172},[108,3423,169],{"emptyLinePlaceholder":168},[108,3425,3426,3428,3430,3432,3435,3437,3440,3442,3445],{"class":110,"line":178},[108,3427,181],{"class":121},[108,3429,249],{"class":132},[108,3431,1833],{"class":190},[108,3433,3434],{"class":125},"Celsius",[108,3436,203],{"class":132},[108,3438,3439],{"class":125},"ToFahrenheit",[108,3441,264],{"class":132},[108,3443,3444],{"class":125},"Fahrenheit",[108,3446,133],{"class":132},[108,3448,3449,3451,3453,3456,3458,3461,3464,3467,3469,3472],{"class":110,"line":214},[108,3450,217],{"class":121},[108,3452,3417],{"class":125},[108,3454,3455],{"class":132},"(c",[108,3457,206],{"class":121},[108,3459,3460],{"class":1187},"9",[108,3462,3463],{"class":121},"\u002F",[108,3465,3466],{"class":1187},"5",[108,3468,1262],{"class":121},[108,3470,3471],{"class":1187}," 32",[108,3473,649],{"class":132},[108,3475,3476],{"class":110,"line":228},[108,3477,162],{"class":132},[108,3479,3480],{"class":110,"line":233},[108,3481,169],{"emptyLinePlaceholder":168},[108,3483,3484,3486,3488,3490,3492,3494,3497,3499,3501],{"class":110,"line":238},[108,3485,181],{"class":121},[108,3487,249],{"class":132},[108,3489,1833],{"class":190},[108,3491,3434],{"class":125},[108,3493,203],{"class":132},[108,3495,3496],{"class":125},"String",[108,3498,264],{"class":132},[108,3500,142],{"class":121},[108,3502,133],{"class":132},[108,3504,3505,3507,3509,3511,3513,3515,3518,3521],{"class":110,"line":244},[108,3506,217],{"class":121},[108,3508,1280],{"class":132},[108,3510,1283],{"class":125},[108,3512,187],{"class":132},[108,3514,1294],{"class":522},[108,3516,3517],{"class":1187},"%.1f",[108,3519,3520],{"class":522},"°C\"",[108,3522,3523],{"class":132},", c)\n",[108,3525,3526],{"class":110,"line":835},[108,3527,162],{"class":132},[108,3529,3530],{"class":110,"line":843},[108,3531,169],{"emptyLinePlaceholder":168},[108,3533,3534,3536,3538],{"class":110,"line":848},[108,3535,181],{"class":121},[108,3537,1561],{"class":125},[108,3539,1564],{"class":132},[108,3541,3542,3545,3547,3549,3551,3554],{"class":110,"line":853},[108,3543,3544],{"class":132},"    temp ",[108,3546,824],{"class":121},[108,3548,3407],{"class":125},[108,3550,187],{"class":132},[108,3552,3553],{"class":1187},"36.6",[108,3555,649],{"class":132},[108,3557,3558,3561,3563,3566],{"class":110,"line":877},[108,3559,3560],{"class":132},"    fmt.",[108,3562,1512],{"class":125},[108,3564,3565],{"class":132},"(temp)                ",[108,3567,3568],{"class":114},"\u002F\u002F \"36.6°C\" (Stringer)\n",[108,3570,3571,3573,3576,3578,3580,3583,3585,3588,3590,3593],{"class":110,"line":3133},[108,3572,3560],{"class":132},[108,3574,3575],{"class":125},"Printf",[108,3577,187],{"class":132},[108,3579,1294],{"class":522},[108,3581,3582],{"class":1187},"%.2f\\n",[108,3584,1294],{"class":522},[108,3586,3587],{"class":132},", temp.",[108,3589,3439],{"class":125},[108,3591,3592],{"class":132},"()) ",[108,3594,3595],{"class":114},"\u002F\u002F 97.88\n",[108,3597,3598],{"class":110,"line":3139},[108,3599,162],{"class":132},[315,3601,3603],{"id":3602},"методы-на-функциональных-типах","Методы на функциональных типах",[99,3605,3607],{"className":101,"code":3606,"language":103,"meta":104,"style":104},"type HandlerFunc func(w http.ResponseWriter, r *http.Request)\n\nfunc (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n    f(w, r)  \u002F\u002F функция сама себя вызывает через метод\n}\n",[86,3608,3609,3647,3651,3691,3702],{"__ignoreMap":104},[108,3610,3611,3613,3616,3618,3620,3622,3625,3627,3630,3632,3635,3637,3640,3642,3645],{"class":110,"line":111},[108,3612,122],{"class":121},[108,3614,3615],{"class":125}," HandlerFunc",[108,3617,2915],{"class":121},[108,3619,187],{"class":132},[108,3621,799],{"class":190},[108,3623,3624],{"class":125}," http",[108,3626,2714],{"class":132},[108,3628,3629],{"class":125},"ResponseWriter",[108,3631,194],{"class":132},[108,3633,3634],{"class":190},"r",[108,3636,506],{"class":121},[108,3638,3639],{"class":125},"http",[108,3641,2714],{"class":132},[108,3643,3644],{"class":125},"Request",[108,3646,649],{"class":132},[108,3648,3649],{"class":110,"line":118},[108,3650,169],{"emptyLinePlaceholder":168},[108,3652,3653,3655,3657,3659,3662,3664,3667,3669,3671,3673,3675,3677,3679,3681,3683,3685,3687,3689],{"class":110,"line":136},[108,3654,181],{"class":121},[108,3656,249],{"class":132},[108,3658,682],{"class":190},[108,3660,3661],{"class":125},"HandlerFunc",[108,3663,203],{"class":132},[108,3665,3666],{"class":125},"ServeHTTP",[108,3668,187],{"class":132},[108,3670,799],{"class":190},[108,3672,3624],{"class":125},[108,3674,2714],{"class":132},[108,3676,3629],{"class":125},[108,3678,194],{"class":132},[108,3680,3634],{"class":190},[108,3682,506],{"class":121},[108,3684,3639],{"class":125},[108,3686,2714],{"class":132},[108,3688,3644],{"class":125},[108,3690,511],{"class":132},[108,3692,3693,3696,3699],{"class":110,"line":148},[108,3694,3695],{"class":125},"    f",[108,3697,3698],{"class":132},"(w, r)  ",[108,3700,3701],{"class":114},"\u002F\u002F функция сама себя вызывает через метод\n",[108,3703,3704],{"class":110,"line":159},[108,3705,162],{"class":132},[11,3707,3708,3709,3712],{},"Это реальный паттерн из ",[86,3710,3711],{},"net\u002Fhttp",". HandlerFunc — функция, но реализует интерфейс Handler.",[315,3714,3716],{"id":3715},"методы-на-слайсах","Методы на слайсах",[99,3718,3720],{"className":101,"code":3719,"language":103,"meta":104,"style":104},"type IntSlice []int\n\nfunc (s IntSlice) Len() int           { return len(s) }\nfunc (s IntSlice) Less(i, j int) bool { return s[i] \u003C s[j] }\nfunc (s IntSlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }\n\n\u002F\u002F Теперь sort.Sort(IntSlice(mySlice)) работает\n",[86,3721,3722,3733,3737,3768,3811,3844,3848],{"__ignoreMap":104},[108,3723,3724,3726,3729,3731],{"class":110,"line":111},[108,3725,122],{"class":121},[108,3727,3728],{"class":125}," IntSlice",[108,3730,626],{"class":132},[108,3732,2701],{"class":121},[108,3734,3735],{"class":110,"line":118},[108,3736,169],{"emptyLinePlaceholder":168},[108,3738,3739,3741,3743,3745,3748,3750,3753,3755,3757,3760,3762,3765],{"class":110,"line":136},[108,3740,181],{"class":121},[108,3742,249],{"class":132},[108,3744,1645],{"class":190},[108,3746,3747],{"class":125},"IntSlice",[108,3749,203],{"class":132},[108,3751,3752],{"class":125},"Len",[108,3754,264],{"class":132},[108,3756,705],{"class":121},[108,3758,3759],{"class":132},"           { ",[108,3761,272],{"class":121},[108,3763,3764],{"class":125}," len",[108,3766,3767],{"class":132},"(s) }\n",[108,3769,3770,3772,3774,3776,3778,3780,3783,3785,3787,3789,3792,3794,3796,3799,3801,3803,3806,3808],{"class":110,"line":148},[108,3771,181],{"class":121},[108,3773,249],{"class":132},[108,3775,1645],{"class":190},[108,3777,3747],{"class":125},[108,3779,203],{"class":132},[108,3781,3782],{"class":125},"Less",[108,3784,187],{"class":132},[108,3786,1215],{"class":190},[108,3788,194],{"class":132},[108,3790,3791],{"class":190},"j",[108,3793,638],{"class":121},[108,3795,203],{"class":132},[108,3797,3798],{"class":121},"bool",[108,3800,269],{"class":132},[108,3802,272],{"class":121},[108,3804,3805],{"class":132}," s[i] ",[108,3807,2236],{"class":121},[108,3809,3810],{"class":132}," s[j] }\n",[108,3812,3813,3815,3817,3819,3821,3823,3826,3828,3830,3832,3834,3836,3839,3841],{"class":110,"line":159},[108,3814,181],{"class":121},[108,3816,249],{"class":132},[108,3818,1645],{"class":190},[108,3820,3747],{"class":125},[108,3822,203],{"class":132},[108,3824,3825],{"class":125},"Swap",[108,3827,187],{"class":132},[108,3829,1215],{"class":190},[108,3831,194],{"class":132},[108,3833,3791],{"class":190},[108,3835,638],{"class":121},[108,3837,3838],{"class":132},")      { s[i], s[j] ",[108,3840,519],{"class":121},[108,3842,3843],{"class":132}," s[j], s[i] }\n",[108,3845,3846],{"class":110,"line":165},[108,3847,169],{"emptyLinePlaceholder":168},[108,3849,3850],{"class":110,"line":172},[108,3851,3852],{"class":114},"\u002F\u002F Теперь sort.Sort(IntSlice(mySlice)) работает\n",[315,3854,3357],{"id":3855},"два-ограничения",[11,3857,3858,3861],{},[15,3859,3860],{},"1. Тот же пакет",": нельзя добавить метод к чужому типу.",[99,3863,3865],{"className":101,"code":3864,"language":103,"meta":104,"style":104},"func (s string) Reverse() string {}  \u002F\u002F ОШИБКА: string определён не здесь\n",[86,3866,3867],{"__ignoreMap":104},[108,3868,3869,3871,3873,3875,3877,3879,3882,3884,3886,3888],{"class":110,"line":111},[108,3870,181],{"class":121},[108,3872,249],{"class":132},[108,3874,1645],{"class":190},[108,3876,142],{"class":121},[108,3878,203],{"class":132},[108,3880,3881],{"class":125},"Reverse",[108,3883,264],{"class":132},[108,3885,142],{"class":121},[108,3887,436],{"class":132},[108,3889,3890],{"class":114},"\u002F\u002F ОШИБКА: string определён не здесь\n",[11,3892,3893],{},"Решение — обёртка:",[99,3895,3897],{"className":101,"code":3896,"language":103,"meta":104,"style":104},"type MyString string\nfunc (s MyString) Reverse() string { \u002F* ... *\u002F }\n",[86,3898,3899,3909],{"__ignoreMap":104},[108,3900,3901,3903,3906],{"class":110,"line":111},[108,3902,122],{"class":121},[108,3904,3905],{"class":125}," MyString",[108,3907,3908],{"class":121}," string\n",[108,3910,3911,3913,3915,3917,3920,3922,3924,3926,3928,3930,3932],{"class":110,"line":118},[108,3912,181],{"class":121},[108,3914,249],{"class":132},[108,3916,1645],{"class":190},[108,3918,3919],{"class":125},"MyString",[108,3921,203],{"class":132},[108,3923,3881],{"class":125},[108,3925,264],{"class":132},[108,3927,142],{"class":121},[108,3929,269],{"class":132},[108,3931,716],{"class":114},[108,3933,482],{"class":132},[11,3935,3936,3939],{},[15,3937,3938],{},"2. Нельзя на интерфейсе",": интерфейс описывает поведение, не имеет реализации.",[99,3941,3943],{"className":101,"code":3942,"language":103,"meta":104,"style":104},"func (r io.Reader) Count() int {}  \u002F\u002F ОШИБКА\n",[86,3944,3945],{"__ignoreMap":104},[108,3946,3947,3949,3951,3954,3957,3959,3962,3964,3967,3969,3971,3973],{"class":110,"line":111},[108,3948,181],{"class":121},[108,3950,249],{"class":132},[108,3952,3953],{"class":190},"r ",[108,3955,3956],{"class":125},"io",[108,3958,2714],{"class":132},[108,3960,3961],{"class":125},"Reader",[108,3963,203],{"class":132},[108,3965,3966],{"class":125},"Count",[108,3968,264],{"class":132},[108,3970,705],{"class":121},[108,3972,436],{"class":132},[108,3974,3975],{"class":114},"\u002F\u002F ОШИБКА\n",[277,3977,3979],{"id":3978},"enum-и-операторы","Enum и операторы",[315,3981,3983],{"id":3982},"enum-паттерн","Enum-паттерн",[99,3985,3987],{"className":101,"code":3986,"language":103,"meta":104,"style":104},"type Status int\n\nconst (\n    StatusPending Status = iota\n    StatusActive\n    StatusClosed\n)\n\nfunc (s Status) String() string {\n    switch s {\n    case StatusPending: return \"pending\"\n    case StatusActive:  return \"active\"\n    case StatusClosed:  return \"closed\"\n    default:            return \"unknown\"\n    }\n}\n\nfunc (s Status) IsTerminal() bool {\n    return s == StatusClosed\n}\n",[86,3988,3989,3999,4003,4011,4023,4028,4033,4037,4041,4062,4069,4081,4093,4105,4116,4120,4124,4128,4149,4162],{"__ignoreMap":104},[108,3990,3991,3993,3996],{"class":110,"line":111},[108,3992,122],{"class":121},[108,3994,3995],{"class":125}," Status",[108,3997,3998],{"class":121}," int\n",[108,4000,4001],{"class":110,"line":118},[108,4002,169],{"emptyLinePlaceholder":168},[108,4004,4005,4008],{"class":110,"line":136},[108,4006,4007],{"class":121},"const",[108,4009,4010],{"class":132}," (\n",[108,4012,4013,4016,4018,4020],{"class":110,"line":148},[108,4014,4015],{"class":1187},"    StatusPending",[108,4017,3995],{"class":125},[108,4019,1175],{"class":121},[108,4021,4022],{"class":1187}," iota\n",[108,4024,4025],{"class":110,"line":159},[108,4026,4027],{"class":1187},"    StatusActive\n",[108,4029,4030],{"class":110,"line":165},[108,4031,4032],{"class":1187},"    StatusClosed\n",[108,4034,4035],{"class":110,"line":172},[108,4036,649],{"class":132},[108,4038,4039],{"class":110,"line":178},[108,4040,169],{"emptyLinePlaceholder":168},[108,4042,4043,4045,4047,4049,4052,4054,4056,4058,4060],{"class":110,"line":214},[108,4044,181],{"class":121},[108,4046,249],{"class":132},[108,4048,1645],{"class":190},[108,4050,4051],{"class":125},"Status",[108,4053,203],{"class":132},[108,4055,3496],{"class":125},[108,4057,264],{"class":132},[108,4059,142],{"class":121},[108,4061,133],{"class":132},[108,4063,4064,4066],{"class":110,"line":228},[108,4065,1229],{"class":121},[108,4067,4068],{"class":132}," s {\n",[108,4070,4071,4073,4076,4078],{"class":110,"line":233},[108,4072,1246],{"class":121},[108,4074,4075],{"class":132}," StatusPending: ",[108,4077,272],{"class":121},[108,4079,4080],{"class":522}," \"pending\"\n",[108,4082,4083,4085,4088,4090],{"class":110,"line":238},[108,4084,1246],{"class":121},[108,4086,4087],{"class":132}," StatusActive:  ",[108,4089,272],{"class":121},[108,4091,4092],{"class":522}," \"active\"\n",[108,4094,4095,4097,4100,4102],{"class":110,"line":244},[108,4096,1246],{"class":121},[108,4098,4099],{"class":132}," StatusClosed:  ",[108,4101,272],{"class":121},[108,4103,4104],{"class":522}," \"closed\"\n",[108,4106,4107,4109,4112,4114],{"class":110,"line":835},[108,4108,1328],{"class":121},[108,4110,4111],{"class":132},":            ",[108,4113,272],{"class":121},[108,4115,1337],{"class":522},[108,4117,4118],{"class":110,"line":843},[108,4119,1342],{"class":132},[108,4121,4122],{"class":110,"line":848},[108,4123,162],{"class":132},[108,4125,4126],{"class":110,"line":853},[108,4127,169],{"emptyLinePlaceholder":168},[108,4129,4130,4132,4134,4136,4138,4140,4143,4145,4147],{"class":110,"line":877},[108,4131,181],{"class":121},[108,4133,249],{"class":132},[108,4135,1645],{"class":190},[108,4137,4051],{"class":125},[108,4139,203],{"class":132},[108,4141,4142],{"class":125},"IsTerminal",[108,4144,264],{"class":132},[108,4146,3798],{"class":121},[108,4148,133],{"class":132},[108,4150,4151,4153,4156,4159],{"class":110,"line":3133},[108,4152,217],{"class":121},[108,4154,4155],{"class":132}," s ",[108,4157,4158],{"class":121},"==",[108,4160,4161],{"class":132}," StatusClosed\n",[108,4163,4164],{"class":110,"line":3139},[108,4165,162],{"class":132},[308,4167],{},[285,4169,4170,4173,4176],{},[288,4171,4172],{},"В Go нет перегрузки методов (method overloading) и нет перегрузки операторов. Одно имя = одна функция",[288,4174,4175],{},"Причина: простота. Перегрузка усложняет чтение кода (какая версия вызвана?) и ломает имплицитные интерфейсы",[288,4177,4178],{},"Альтернативы: разные имена, variadic args, interface{}\u002Fany, functional options",[308,4180],{},[315,4182,4184],{"id":4183},"что-не-работает","Что не работает",[99,4186,4188],{"className":101,"code":4187,"language":103,"meta":104,"style":104},"\u002F\u002F ОШИБКА КОМПИЛЯЦИИ — нельзя два метода с одним именем\nfunc (s *Server) Start() error { \u002F* ... *\u002F }\nfunc (s *Server) Start(port int) error { \u002F* ... *\u002F }\n",[86,4189,4190,4195,4222],{"__ignoreMap":104},[108,4191,4192],{"class":110,"line":111},[108,4193,4194],{"class":114},"\u002F\u002F ОШИБКА КОМПИЛЯЦИИ — нельзя два метода с одним именем\n",[108,4196,4197,4199,4201,4203,4205,4207,4209,4212,4214,4216,4218,4220],{"class":110,"line":118},[108,4198,181],{"class":121},[108,4200,249],{"class":132},[108,4202,1645],{"class":190},[108,4204,206],{"class":121},[108,4206,1648],{"class":125},[108,4208,203],{"class":132},[108,4210,4211],{"class":125},"Start",[108,4213,264],{"class":132},[108,4215,710],{"class":121},[108,4217,269],{"class":132},[108,4219,716],{"class":114},[108,4221,482],{"class":132},[108,4223,4224,4226,4228,4230,4232,4234,4236,4238,4240,4242,4244,4246,4248,4250,4252],{"class":110,"line":136},[108,4225,181],{"class":121},[108,4227,249],{"class":132},[108,4229,1645],{"class":190},[108,4231,206],{"class":121},[108,4233,1648],{"class":125},[108,4235,203],{"class":132},[108,4237,4211],{"class":125},[108,4239,187],{"class":132},[108,4241,2744],{"class":190},[108,4243,638],{"class":121},[108,4245,203],{"class":132},[108,4247,710],{"class":121},[108,4249,269],{"class":132},[108,4251,716],{"class":114},[108,4253,482],{"class":132},[99,4255,4257],{"className":101,"code":4256,"language":103,"meta":104,"style":104},"\u002F\u002F ОШИБКА — нет перегрузки операторов\nfunc (v Vector) +(other Vector) Vector { \u002F* ... *\u002F }\n",[86,4258,4259,4264],{"__ignoreMap":104},[108,4260,4261],{"class":110,"line":111},[108,4262,4263],{"class":114},"\u002F\u002F ОШИБКА — нет перегрузки операторов\n",[108,4265,4266,4268,4270,4273,4276,4278,4280,4282,4285,4288,4290,4292,4294,4296],{"class":110,"line":118},[108,4267,181],{"class":121},[108,4269,249],{"class":132},[108,4271,4272],{"class":190},"v ",[108,4274,4275],{"class":125},"Vector",[108,4277,203],{"class":132},[108,4279,2397],{"class":121},[108,4281,187],{"class":132},[108,4283,4284],{"class":190},"other",[108,4286,4287],{"class":125}," Vector",[108,4289,203],{"class":132},[108,4291,4275],{"class":125},[108,4293,269],{"class":132},[108,4295,716],{"class":114},[108,4297,482],{"class":132},[315,4299,4301],{"id":4300},"почему-так","Почему так",[11,4303,4304,4307,4308,4311],{},[15,4305,4306],{},"1. Простота чтения",": увидел ",[86,4309,4310],{},"s.Start()"," — точно знаешь какая функция вызвана. Нет неоднозначности.",[11,4313,4314,2879],{},[15,4315,4316],{},"2. Имплицитные интерфейсы сломались бы",[99,4318,4320],{"className":101,"code":4319,"language":103,"meta":104,"style":104},"type Starter interface {\n    Start() error\n}\n",[86,4321,4322,4333,4343],{"__ignoreMap":104},[108,4323,4324,4326,4329,4331],{"class":110,"line":111},[108,4325,122],{"class":121},[108,4327,4328],{"class":125}," Starter",[108,4330,612],{"class":121},[108,4332,133],{"class":132},[108,4334,4335,4338,4340],{"class":110,"line":118},[108,4336,4337],{"class":125},"    Start",[108,4339,264],{"class":132},[108,4341,4342],{"class":121},"error\n",[108,4344,4345],{"class":110,"line":136},[108,4346,162],{"class":132},[11,4348,4349],{},"Если бы у Server было два метода Start (с разными сигнатурами) — какой из них \"реализует\" интерфейс? Структурная типизация требует однозначности.",[11,4351,4352,4355],{},[15,4353,4354],{},"3. Философия Go",": \"There should be one — and preferably only one — obvious way to do it.\" Одно имя → одно поведение.",[315,4357,4359],{"id":4358},"альтернативы","Альтернативы",[11,4361,4362,4365],{},[15,4363,4364],{},"Разные имена"," (самый идиоматичный):",[99,4367,4369],{"className":101,"code":4368,"language":103,"meta":104,"style":104},"func (s *Server) Start() error { return s.StartOnPort(8080) }\nfunc (s *Server) StartOnPort(port int) error { \u002F* ... *\u002F }\n",[86,4370,4371,4407],{"__ignoreMap":104},[108,4372,4373,4375,4377,4379,4381,4383,4385,4387,4389,4391,4393,4395,4398,4401,4403,4405],{"class":110,"line":111},[108,4374,181],{"class":121},[108,4376,249],{"class":132},[108,4378,1645],{"class":190},[108,4380,206],{"class":121},[108,4382,1648],{"class":125},[108,4384,203],{"class":132},[108,4386,4211],{"class":125},[108,4388,264],{"class":132},[108,4390,710],{"class":121},[108,4392,269],{"class":132},[108,4394,272],{"class":121},[108,4396,4397],{"class":132}," s.",[108,4399,4400],{"class":125},"StartOnPort",[108,4402,187],{"class":132},[108,4404,3079],{"class":1187},[108,4406,1097],{"class":132},[108,4408,4409,4411,4413,4415,4417,4419,4421,4423,4425,4427,4429,4431,4433,4435,4437],{"class":110,"line":118},[108,4410,181],{"class":121},[108,4412,249],{"class":132},[108,4414,1645],{"class":190},[108,4416,206],{"class":121},[108,4418,1648],{"class":125},[108,4420,203],{"class":132},[108,4422,4400],{"class":125},[108,4424,187],{"class":132},[108,4426,2744],{"class":190},[108,4428,638],{"class":121},[108,4430,203],{"class":132},[108,4432,710],{"class":121},[108,4434,269],{"class":132},[108,4436,716],{"class":114},[108,4438,482],{"class":132},[11,4440,4441,2879],{},[15,4442,4443],{},"Variadic arguments",[99,4445,4447],{"className":101,"code":4446,"language":103,"meta":104,"style":104},"func (s *Server) Start(opts ...int) error {\n    port := 8080\n    if len(opts) > 0 { port = opts[0] }\n    \u002F\u002F ...\n}\n",[86,4448,4449,4478,4487,4515,4520],{"__ignoreMap":104},[108,4450,4451,4453,4455,4457,4459,4461,4463,4465,4467,4469,4472,4474,4476],{"class":110,"line":111},[108,4452,181],{"class":121},[108,4454,249],{"class":132},[108,4456,1645],{"class":190},[108,4458,206],{"class":121},[108,4460,1648],{"class":125},[108,4462,203],{"class":132},[108,4464,4211],{"class":125},[108,4466,187],{"class":132},[108,4468,3050],{"class":190},[108,4470,4471],{"class":121}," ...int",[108,4473,203],{"class":132},[108,4475,710],{"class":121},[108,4477,133],{"class":132},[108,4479,4480,4482,4484],{"class":110,"line":118},[108,4481,2698],{"class":132},[108,4483,824],{"class":121},[108,4485,4486],{"class":1187}," 8080\n",[108,4488,4489,4491,4493,4496,4499,4501,4504,4506,4509,4512],{"class":110,"line":136},[108,4490,2230],{"class":121},[108,4492,3764],{"class":125},[108,4494,4495],{"class":132},"(opts) ",[108,4497,4498],{"class":121},">",[108,4500,2239],{"class":1187},[108,4502,4503],{"class":132}," { port ",[108,4505,519],{"class":121},[108,4507,4508],{"class":132}," opts[",[108,4510,4511],{"class":1187},"0",[108,4513,4514],{"class":132},"] }\n",[108,4516,4517],{"class":110,"line":148},[108,4518,4519],{"class":114},"    \u002F\u002F ...\n",[108,4521,4522],{"class":110,"line":159},[108,4523,162],{"class":132},[11,4525,4526,4529],{},[15,4527,4528],{},"Functional options"," (для сложных случаев):",[99,4531,4533],{"className":101,"code":4532,"language":103,"meta":104,"style":104},"func (s *Server) Start(opts ...Option) error { \u002F* ... *\u002F }\n",[86,4534,4535],{"__ignoreMap":104},[108,4536,4537,4539,4541,4543,4545,4547,4549,4551,4553,4555,4557,4559,4561,4563,4565,4567],{"class":110,"line":111},[108,4538,181],{"class":121},[108,4540,249],{"class":132},[108,4542,1645],{"class":190},[108,4544,206],{"class":121},[108,4546,1648],{"class":125},[108,4548,203],{"class":132},[108,4550,4211],{"class":125},[108,4552,187],{"class":132},[108,4554,3050],{"class":190},[108,4556,3053],{"class":121},[108,4558,2945],{"class":125},[108,4560,203],{"class":132},[108,4562,710],{"class":121},[108,4564,269],{"class":132},[108,4566,716],{"class":114},[108,4568,482],{"class":132},[11,4570,4571,4574],{},[15,4572,4573],{},"any \u002F interface{}"," (крайний случай, теряем типобезопасность):",[99,4576,4578],{"className":101,"code":4577,"language":103,"meta":104,"style":104},"\u002F\u002F именно так устроен стандартный Print из пакета fmt\nfunc Print(args ...interface{}) { \u002F* реализация *\u002F }\n",[86,4579,4580,4585],{"__ignoreMap":104},[108,4581,4582],{"class":110,"line":111},[108,4583,4584],{"class":114},"\u002F\u002F именно так устроен стандартный Print из пакета fmt\n",[108,4586,4587,4589,4592,4594,4597,4600,4603,4606],{"class":110,"line":118},[108,4588,181],{"class":121},[108,4590,4591],{"class":125}," Print",[108,4593,187],{"class":132},[108,4595,4596],{"class":190},"args",[108,4598,4599],{"class":121}," ...interface",[108,4601,4602],{"class":132},"{}) { ",[108,4604,4605],{"class":114},"\u002F* реализация *\u002F",[108,4607,482],{"class":132},[315,4609,4611],{"id":4610},"а-перегрузка-операторов","А перегрузка операторов?",[11,4613,4614,4615,4618,4619,4621,4622,4624],{},"Go не позволяет ",[86,4616,4617],{},"a + b"," для кастомных типов. Причина: ",[86,4620,2397],{}," должен быть предсказуемым. Если видишь ",[86,4623,4617],{}," — это числа или строки, не вызов произвольного кода с побочными эффектами.",[11,4626,4627],{},"Альтернатива: метод.",[99,4629,4631],{"className":101,"code":4630,"language":103,"meta":104,"style":104},"result := vec1.Add(vec2)  \u002F\u002F явно и читаемо\n",[86,4632,4633],{"__ignoreMap":104},[108,4634,4635,4638,4640,4643,4646,4649],{"class":110,"line":111},[108,4636,4637],{"class":132},"result ",[108,4639,824],{"class":121},[108,4641,4642],{"class":132}," vec1.",[108,4644,4645],{"class":125},"Add",[108,4647,4648],{"class":132},"(vec2)  ",[108,4650,4651],{"class":114},"\u002F\u002F явно и читаемо\n",[308,4653],{},[285,4655,4656,4659,4662],{},[288,4657,4658],{},"Нельзя добавить метод к типу из другого пакета напрямую. Компилятор запретит",[288,4660,4661],{},"Два способа: обёртка (type MyType OriginalType) или композиция (struct с встраиванием)",[288,4663,4664],{},"Обёртка — новый тип, теряет методы оригинала. Композиция — сохраняет методы через embedding",[308,4666],{},[277,4668,4670],{"id":4669},"адаптация-чужих-типов","Адаптация чужих типов",[315,4672,4674],{"id":4673},"проблема","Проблема",[99,4676,4678],{"className":101,"code":4677,"language":103,"meta":104,"style":104},"import \"net\u002Fhttp\"\n\n\u002F\u002F ОШИБКА: cannot define new methods on non-local type http.Request\nfunc (r *http.Request) GetUserID() int { \u002F* ... *\u002F }\n",[86,4679,4680,4690,4694,4699],{"__ignoreMap":104},[108,4681,4682,4684,4686,4688],{"class":110,"line":111},[108,4683,1454],{"class":121},[108,4685,1457],{"class":522},[108,4687,3711],{"class":125},[108,4689,1463],{"class":522},[108,4691,4692],{"class":110,"line":118},[108,4693,169],{"emptyLinePlaceholder":168},[108,4695,4696],{"class":110,"line":136},[108,4697,4698],{"class":114},"\u002F\u002F ОШИБКА: cannot define new methods on non-local type http.Request\n",[108,4700,4701,4703,4705,4707,4709,4711,4713,4715,4717,4720,4722,4724,4726,4728],{"class":110,"line":148},[108,4702,181],{"class":121},[108,4704,249],{"class":132},[108,4706,3953],{"class":190},[108,4708,206],{"class":121},[108,4710,3639],{"class":125},[108,4712,2714],{"class":132},[108,4714,3644],{"class":125},[108,4716,203],{"class":132},[108,4718,4719],{"class":125},"GetUserID",[108,4721,264],{"class":132},[108,4723,705],{"class":121},[108,4725,269],{"class":132},[108,4727,716],{"class":114},[108,4729,482],{"class":132},[315,4731,4733],{"id":4732},"способ-1-обёртка-type-alias-на-основе","Способ 1: Обёртка (type alias на основе)",[99,4735,4737],{"className":101,"code":4736,"language":103,"meta":104,"style":104},"type Headers http.Header\n\nfunc (h Headers) GetAuth() string {\n    return http.Header(h).Get(\"Authorization\")  \u002F\u002F приведение типа\n}\n",[86,4738,4739,4753,4757,4780,4806],{"__ignoreMap":104},[108,4740,4741,4743,4746,4748,4750],{"class":110,"line":111},[108,4742,122],{"class":121},[108,4744,4745],{"class":125}," Headers",[108,4747,3624],{"class":125},[108,4749,2714],{"class":132},[108,4751,4752],{"class":125},"Header\n",[108,4754,4755],{"class":110,"line":118},[108,4756,169],{"emptyLinePlaceholder":168},[108,4758,4759,4761,4763,4766,4769,4771,4774,4776,4778],{"class":110,"line":136},[108,4760,181],{"class":121},[108,4762,249],{"class":132},[108,4764,4765],{"class":190},"h ",[108,4767,4768],{"class":125},"Headers",[108,4770,203],{"class":132},[108,4772,4773],{"class":125},"GetAuth",[108,4775,264],{"class":132},[108,4777,142],{"class":121},[108,4779,133],{"class":132},[108,4781,4782,4784,4787,4790,4793,4796,4798,4801,4803],{"class":110,"line":148},[108,4783,217],{"class":121},[108,4785,4786],{"class":132}," http.",[108,4788,4789],{"class":125},"Header",[108,4791,4792],{"class":132},"(h).",[108,4794,4795],{"class":125},"Get",[108,4797,187],{"class":132},[108,4799,4800],{"class":522},"\"Authorization\"",[108,4802,1191],{"class":132},[108,4804,4805],{"class":114},"\u002F\u002F приведение типа\n",[108,4807,4808],{"class":110,"line":159},[108,4809,162],{"class":132},[11,4811,4812,4814],{},[15,4813,2499],{},": простой новый тип.",[11,4816,4817,4819],{},[15,4818,2553],{},": Headers — это НЕ http.Header. Методы оригинала потеряны.",[99,4821,4823],{"className":101,"code":4822,"language":103,"meta":104,"style":104},"var h Headers\nh.Get(\"X-Custom\")  \u002F\u002F ОШИБКА — у Headers нет метода Get\nhttp.Header(h).Get(\"X-Custom\")  \u002F\u002F ОК — через приведение\n",[86,4824,4825,4835,4852],{"__ignoreMap":104},[108,4826,4827,4829,4832],{"class":110,"line":111},[108,4828,1166],{"class":121},[108,4830,4831],{"class":132}," h ",[108,4833,4834],{"class":125},"Headers\n",[108,4836,4837,4840,4842,4844,4847,4849],{"class":110,"line":118},[108,4838,4839],{"class":132},"h.",[108,4841,4795],{"class":125},[108,4843,187],{"class":132},[108,4845,4846],{"class":522},"\"X-Custom\"",[108,4848,1191],{"class":132},[108,4850,4851],{"class":114},"\u002F\u002F ОШИБКА — у Headers нет метода Get\n",[108,4853,4854,4857,4859,4861,4863,4865,4867,4869],{"class":110,"line":136},[108,4855,4856],{"class":132},"http.",[108,4858,4789],{"class":125},[108,4860,4792],{"class":132},[108,4862,4795],{"class":125},[108,4864,187],{"class":132},[108,4866,4846],{"class":522},[108,4868,1191],{"class":132},[108,4870,4871],{"class":114},"\u002F\u002F ОК — через приведение\n",[315,4873,4875],{"id":4874},"способ-2-композиция-embedding","Способ 2: Композиция (embedding)",[99,4877,4879],{"className":101,"code":4878,"language":103,"meta":104,"style":104},"type RichRequest struct {\n    *http.Request  \u002F\u002F встраивание — все методы Request доступны\n}\n\nfunc (r *RichRequest) UserID() int {\n    \u002F\u002F используем методы оригинала напрямую\n    id, _ := strconv.Atoi(r.Header.Get(\"X-User-ID\"))\n    return id\n}\n\n\u002F\u002F Все оригинальные методы работают:\nrr := &RichRequest{Request: req}\nrr.FormValue(\"name\")  \u002F\u002F метод http.Request — доступен\nrr.UserID()            \u002F\u002F наш новый метод\n",[86,4880,4881,4892,4905,4909,4913,4937,4942,4968,4975,4979,4983,4988,5002,5020],{"__ignoreMap":104},[108,4882,4883,4885,4888,4890],{"class":110,"line":111},[108,4884,122],{"class":121},[108,4886,4887],{"class":125}," RichRequest",[108,4889,129],{"class":121},[108,4891,133],{"class":132},[108,4893,4894,4896,4898,4900,4902],{"class":110,"line":118},[108,4895,2425],{"class":121},[108,4897,3639],{"class":125},[108,4899,2714],{"class":132},[108,4901,3644],{"class":125},[108,4903,4904],{"class":114},"  \u002F\u002F встраивание — все методы Request доступны\n",[108,4906,4907],{"class":110,"line":136},[108,4908,162],{"class":132},[108,4910,4911],{"class":110,"line":148},[108,4912,169],{"emptyLinePlaceholder":168},[108,4914,4915,4917,4919,4921,4923,4926,4928,4931,4933,4935],{"class":110,"line":159},[108,4916,181],{"class":121},[108,4918,249],{"class":132},[108,4920,3953],{"class":190},[108,4922,206],{"class":121},[108,4924,4925],{"class":125},"RichRequest",[108,4927,203],{"class":132},[108,4929,4930],{"class":125},"UserID",[108,4932,264],{"class":132},[108,4934,705],{"class":121},[108,4936,133],{"class":132},[108,4938,4939],{"class":110,"line":165},[108,4940,4941],{"class":114},"    \u002F\u002F используем методы оригинала напрямую\n",[108,4943,4944,4947,4949,4952,4955,4958,4960,4962,4965],{"class":110,"line":172},[108,4945,4946],{"class":132},"    id, _ ",[108,4948,824],{"class":121},[108,4950,4951],{"class":132}," strconv.",[108,4953,4954],{"class":125},"Atoi",[108,4956,4957],{"class":132},"(r.Header.",[108,4959,4795],{"class":125},[108,4961,187],{"class":132},[108,4963,4964],{"class":522},"\"X-User-ID\"",[108,4966,4967],{"class":132},"))\n",[108,4969,4970,4972],{"class":110,"line":178},[108,4971,217],{"class":121},[108,4973,4974],{"class":132}," id\n",[108,4976,4977],{"class":110,"line":214},[108,4978,162],{"class":132},[108,4980,4981],{"class":110,"line":228},[108,4982,169],{"emptyLinePlaceholder":168},[108,4984,4985],{"class":110,"line":233},[108,4986,4987],{"class":114},"\u002F\u002F Все оригинальные методы работают:\n",[108,4989,4990,4993,4995,4997,4999],{"class":110,"line":238},[108,4991,4992],{"class":132},"rr ",[108,4994,824],{"class":121},[108,4996,220],{"class":121},[108,4998,4925],{"class":125},[108,5000,5001],{"class":132},"{Request: req}\n",[108,5003,5004,5007,5010,5012,5015,5017],{"class":110,"line":244},[108,5005,5006],{"class":132},"rr.",[108,5008,5009],{"class":125},"FormValue",[108,5011,187],{"class":132},[108,5013,5014],{"class":522},"\"name\"",[108,5016,1191],{"class":132},[108,5018,5019],{"class":114},"\u002F\u002F метод http.Request — доступен\n",[108,5021,5022,5024,5026,5029],{"class":110,"line":835},[108,5023,5006],{"class":132},[108,5025,4930],{"class":125},[108,5027,5028],{"class":132},"()            ",[108,5030,5031],{"class":114},"\u002F\u002F наш новый метод\n",[11,5033,5034,5036],{},[15,5035,2499],{},": сохраняет все методы оригинала + добавляет новые.",[11,5038,5039,5041],{},[15,5040,2553],{},": это другой тип, нельзя передать где ждут *http.Request (но можно достать через rr.Request).",[315,5043,5045],{"id":5044},"способ-3-интерфейс-обёртка","Способ 3: Интерфейс-обёртка",[99,5047,5049],{"className":101,"code":5048,"language":103,"meta":104,"style":104},"type ReadCounter struct {\n    io.Reader\n    count int64\n}\n\nfunc (rc *ReadCounter) Read(p []byte) (int, error) {\n    n, err := rc.Reader.Read(p)  \u002F\u002F делегируем оригиналу\n    rc.count += int64(n)         \u002F\u002F добавляем поведение\n    return n, err\n}\n\n\u002F\u002F ReadCounter реализует io.Reader — можно использовать везде\n",[86,5050,5051,5062,5072,5080,5084,5088,5124,5142,5159,5166,5170,5174],{"__ignoreMap":104},[108,5052,5053,5055,5058,5060],{"class":110,"line":111},[108,5054,122],{"class":121},[108,5056,5057],{"class":125}," ReadCounter",[108,5059,129],{"class":121},[108,5061,133],{"class":132},[108,5063,5064,5067,5069],{"class":110,"line":118},[108,5065,5066],{"class":125},"    io",[108,5068,2714],{"class":132},[108,5070,5071],{"class":125},"Reader\n",[108,5073,5074,5077],{"class":110,"line":136},[108,5075,5076],{"class":132},"    count ",[108,5078,5079],{"class":121},"int64\n",[108,5081,5082],{"class":110,"line":148},[108,5083,162],{"class":132},[108,5085,5086],{"class":110,"line":159},[108,5087,169],{"emptyLinePlaceholder":168},[108,5089,5090,5092,5094,5097,5099,5102,5104,5106,5108,5110,5112,5114,5116,5118,5120,5122],{"class":110,"line":165},[108,5091,181],{"class":121},[108,5093,249],{"class":132},[108,5095,5096],{"class":190},"rc ",[108,5098,206],{"class":121},[108,5100,5101],{"class":125},"ReadCounter",[108,5103,203],{"class":132},[108,5105,1078],{"class":125},[108,5107,187],{"class":132},[108,5109,11],{"class":190},[108,5111,626],{"class":132},[108,5113,629],{"class":121},[108,5115,632],{"class":132},[108,5117,705],{"class":121},[108,5119,194],{"class":132},[108,5121,710],{"class":121},[108,5123,511],{"class":132},[108,5125,5126,5129,5131,5134,5136,5139],{"class":110,"line":172},[108,5127,5128],{"class":132},"    n, err ",[108,5130,824],{"class":121},[108,5132,5133],{"class":132}," rc.Reader.",[108,5135,1078],{"class":125},[108,5137,5138],{"class":132},"(p)  ",[108,5140,5141],{"class":114},"\u002F\u002F делегируем оригиналу\n",[108,5143,5144,5147,5150,5153,5156],{"class":110,"line":178},[108,5145,5146],{"class":132},"    rc.count ",[108,5148,5149],{"class":121},"+=",[108,5151,5152],{"class":121}," int64",[108,5154,5155],{"class":132},"(n)         ",[108,5157,5158],{"class":114},"\u002F\u002F добавляем поведение\n",[108,5160,5161,5163],{"class":110,"line":214},[108,5162,217],{"class":121},[108,5164,5165],{"class":132}," n, err\n",[108,5167,5168],{"class":110,"line":228},[108,5169,162],{"class":132},[108,5171,5172],{"class":110,"line":233},[108,5173,169],{"emptyLinePlaceholder":168},[108,5175,5176],{"class":110,"line":238},[108,5177,5178],{"class":114},"\u002F\u002F ReadCounter реализует io.Reader — можно использовать везде\n",[11,5180,5181,5182,5185],{},"Это паттерн ",[15,5183,5184],{},"декоратор",": оборачиваем интерфейс, добавляем функциональность.",[315,5187,5189],{"id":5188},"сводка","Сводка",[25,5191,5192,5208],{},[28,5193,5194],{},[31,5195,5196,5199,5202,5205],{},[34,5197,5198],{},"Способ",[34,5200,5201],{},"Методы оригинала",[34,5203,5204],{},"Новые методы",[34,5206,5207],{},"Совместимость типов",[42,5209,5210,5224,5237],{},[31,5211,5212,5215,5218,5221],{},[47,5213,5214],{},"type X OrigType",[47,5216,5217],{},"Потеряны",[47,5219,5220],{},"Да",[47,5222,5223],{},"Приведение типа",[31,5225,5226,5229,5232,5234],{},[47,5227,5228],{},"struct + embedding",[47,5230,5231],{},"Всплывают",[47,5233,5220],{},[47,5235,5236],{},"Через поле",[31,5238,5239,5242,5245,5248],{},[47,5240,5241],{},"Интерфейс-обёртка",[47,5243,5244],{},"Делегируются",[47,5246,5247],{},"Да (override)",[47,5249,5250],{},"По интерфейсу",[308,5252],{},[285,5254,5255,5261,5264],{},[288,5256,5257,5260],{},[15,5258,5259],{},"Shadowing",": если внешняя структура объявляет поле\u002Fметод с тем же именем что у встроенной — внешнее \"затеняет\" внутреннее",[288,5262,5263],{},"Затенённое поле\u002Fметод не удаляется — доступно через явное имя встроенного типа",[288,5265,5266],{},"При двух встроенных типах с одинаковым методом — ambiguous, компилятор требует явный вызов",[308,5268],{},[277,5270,5272],{"id":5271},"shadowing-и-конфликты-embedding","Shadowing и конфликты embedding",[315,5274,5276],{"id":5275},"shadowing-поля","Shadowing поля",[99,5278,5280],{"className":101,"code":5279,"language":103,"meta":104,"style":104},"package main\n\nimport \"fmt\"\n\ntype Base struct {\n    Name string\n}\n\ntype Extended struct {\n    Base\n    Name string \u002F\u002F затеняет Base.Name\n}\n\nfunc main() {\n    e := Extended{\n        Base: Base{Name: \"base\"},\n        Name: \"extended\",\n    }\n\n    fmt.Println(e.Name)      \u002F\u002F \"extended\" — внешнее поле\n    fmt.Println(e.Base.Name) \u002F\u002F \"base\"     — всё ещё доступно\n}\n",[86,5281,5282,5288,5292,5302,5306,5316,5323,5327,5331,5342,5347,5356,5360,5364,5372,5383,5399,5410,5414,5418,5430,5443],{"__ignoreMap":104},[108,5283,5284,5286],{"class":110,"line":111},[108,5285,328],{"class":121},[108,5287,1445],{"class":125},[108,5289,5290],{"class":110,"line":118},[108,5291,169],{"emptyLinePlaceholder":168},[108,5293,5294,5296,5298,5300],{"class":110,"line":136},[108,5295,1454],{"class":121},[108,5297,1457],{"class":522},[108,5299,1460],{"class":125},[108,5301,1463],{"class":522},[108,5303,5304],{"class":110,"line":148},[108,5305,169],{"emptyLinePlaceholder":168},[108,5307,5308,5310,5312,5314],{"class":110,"line":159},[108,5309,122],{"class":121},[108,5311,1736],{"class":125},[108,5313,129],{"class":121},[108,5315,133],{"class":132},[108,5317,5318,5321],{"class":110,"line":165},[108,5319,5320],{"class":132},"    Name ",[108,5322,1546],{"class":121},[108,5324,5325],{"class":110,"line":172},[108,5326,162],{"class":132},[108,5328,5329],{"class":110,"line":178},[108,5330,169],{"emptyLinePlaceholder":168},[108,5332,5333,5335,5338,5340],{"class":110,"line":214},[108,5334,122],{"class":121},[108,5336,5337],{"class":125}," Extended",[108,5339,129],{"class":121},[108,5341,133],{"class":132},[108,5343,5344],{"class":110,"line":228},[108,5345,5346],{"class":125},"    Base\n",[108,5348,5349,5351,5353],{"class":110,"line":233},[108,5350,5320],{"class":132},[108,5352,142],{"class":121},[108,5354,5355],{"class":114}," \u002F\u002F затеняет Base.Name\n",[108,5357,5358],{"class":110,"line":238},[108,5359,162],{"class":132},[108,5361,5362],{"class":110,"line":244},[108,5363,169],{"emptyLinePlaceholder":168},[108,5365,5366,5368,5370],{"class":110,"line":835},[108,5367,181],{"class":121},[108,5369,1561],{"class":125},[108,5371,1564],{"class":132},[108,5373,5374,5377,5379,5381],{"class":110,"line":843},[108,5375,5376],{"class":132},"    e ",[108,5378,824],{"class":121},[108,5380,5337],{"class":125},[108,5382,2765],{"class":132},[108,5384,5385,5388,5390,5393,5396],{"class":110,"line":848},[108,5386,5387],{"class":132},"        Base: ",[108,5389,1751],{"class":125},[108,5391,5392],{"class":132},"{Name: ",[108,5394,5395],{"class":522},"\"base\"",[108,5397,5398],{"class":132},"},\n",[108,5400,5401,5404,5407],{"class":110,"line":853},[108,5402,5403],{"class":132},"        Name: ",[108,5405,5406],{"class":522},"\"extended\"",[108,5408,5409],{"class":132},",\n",[108,5411,5412],{"class":110,"line":877},[108,5413,1342],{"class":132},[108,5415,5416],{"class":110,"line":3133},[108,5417,169],{"emptyLinePlaceholder":168},[108,5419,5420,5422,5424,5427],{"class":110,"line":3139},[108,5421,3560],{"class":132},[108,5423,1512],{"class":125},[108,5425,5426],{"class":132},"(e.Name)      ",[108,5428,5429],{"class":114},"\u002F\u002F \"extended\" — внешнее поле\n",[108,5431,5433,5435,5437,5440],{"class":110,"line":5432},21,[108,5434,3560],{"class":132},[108,5436,1512],{"class":125},[108,5438,5439],{"class":132},"(e.Base.Name) ",[108,5441,5442],{"class":114},"\u002F\u002F \"base\"     — всё ещё доступно\n",[108,5444,5446],{"class":110,"line":5445},22,[108,5447,162],{"class":132},[315,5449,5451],{"id":5450},"shadowing-метода","Shadowing метода",[99,5453,5455],{"className":101,"code":5454,"language":103,"meta":104,"style":104},"package main\n\nimport \"fmt\"\n\ntype Logger struct{}\n\nfunc (Logger) Log() { fmt.Println(\"Logger.Log\") }\n\ntype App struct{ Logger }\n\nfunc (App) Log() { fmt.Println(\"App.Log\") } \u002F\u002F затеняет Logger.Log\n\nfunc main() {\n    a := App{}\n    a.Log()        \u002F\u002F \"App.Log\"\n    a.Logger.Log() \u002F\u002F \"Logger.Log\" — явный доступ\n}\n",[86,5456,5457,5463,5467,5477,5481,5491,5495,5519,5523,5537,5541,5569,5573,5581,5591,5603,5615],{"__ignoreMap":104},[108,5458,5459,5461],{"class":110,"line":111},[108,5460,328],{"class":121},[108,5462,1445],{"class":125},[108,5464,5465],{"class":110,"line":118},[108,5466,169],{"emptyLinePlaceholder":168},[108,5468,5469,5471,5473,5475],{"class":110,"line":136},[108,5470,1454],{"class":121},[108,5472,1457],{"class":522},[108,5474,1460],{"class":125},[108,5476,1463],{"class":522},[108,5478,5479],{"class":110,"line":148},[108,5480,169],{"emptyLinePlaceholder":168},[108,5482,5483,5485,5487,5489],{"class":110,"line":159},[108,5484,122],{"class":121},[108,5486,1474],{"class":125},[108,5488,129],{"class":121},[108,5490,969],{"class":132},[108,5492,5493],{"class":110,"line":165},[108,5494,169],{"emptyLinePlaceholder":168},[108,5496,5497,5499,5501,5503,5505,5507,5510,5512,5514,5517],{"class":110,"line":172},[108,5498,181],{"class":121},[108,5500,249],{"class":132},[108,5502,1494],{"class":125},[108,5504,203],{"class":132},[108,5506,1499],{"class":125},[108,5508,5509],{"class":132},"() { fmt.",[108,5511,1512],{"class":125},[108,5513,187],{"class":132},[108,5515,5516],{"class":522},"\"Logger.Log\"",[108,5518,1097],{"class":132},[108,5520,5521],{"class":110,"line":178},[108,5522,169],{"emptyLinePlaceholder":168},[108,5524,5525,5527,5529,5531,5533,5535],{"class":110,"line":214},[108,5526,122],{"class":121},[108,5528,2416],{"class":125},[108,5530,129],{"class":121},[108,5532,1820],{"class":132},[108,5534,1494],{"class":125},[108,5536,482],{"class":132},[108,5538,5539],{"class":110,"line":228},[108,5540,169],{"emptyLinePlaceholder":168},[108,5542,5543,5545,5547,5550,5552,5554,5556,5558,5560,5563,5566],{"class":110,"line":233},[108,5544,181],{"class":121},[108,5546,249],{"class":132},[108,5548,5549],{"class":125},"App",[108,5551,203],{"class":132},[108,5553,1499],{"class":125},[108,5555,5509],{"class":132},[108,5557,1512],{"class":125},[108,5559,187],{"class":132},[108,5561,5562],{"class":522},"\"App.Log\"",[108,5564,5565],{"class":132},") } ",[108,5567,5568],{"class":114},"\u002F\u002F затеняет Logger.Log\n",[108,5570,5571],{"class":110,"line":238},[108,5572,169],{"emptyLinePlaceholder":168},[108,5574,5575,5577,5579],{"class":110,"line":244},[108,5576,181],{"class":121},[108,5578,1561],{"class":125},[108,5580,1564],{"class":132},[108,5582,5583,5585,5587,5589],{"class":110,"line":835},[108,5584,2470],{"class":132},[108,5586,824],{"class":121},[108,5588,2416],{"class":125},[108,5590,969],{"class":132},[108,5592,5593,5595,5597,5600],{"class":110,"line":843},[108,5594,2482],{"class":132},[108,5596,1499],{"class":125},[108,5598,5599],{"class":132},"()        ",[108,5601,5602],{"class":114},"\u002F\u002F \"App.Log\"\n",[108,5604,5605,5608,5610,5612],{"class":110,"line":848},[108,5606,5607],{"class":132},"    a.Logger.",[108,5609,1499],{"class":125},[108,5611,264],{"class":132},[108,5613,5614],{"class":114},"\u002F\u002F \"Logger.Log\" — явный доступ\n",[108,5616,5617],{"class":110,"line":853},[108,5618,162],{"class":132},[11,5620,5621],{},"Это НЕ переопределение (override). Base не знает о затенении, виртуальной диспетчеризации нет.",[315,5623,5625],{"id":5624},"конфликт-двух-встроенных-типов","Конфликт двух встроенных типов",[99,5627,5629],{"className":101,"code":5628,"language":103,"meta":104,"style":104},"package main\n\nimport \"fmt\"\n\ntype A struct{}\n\nfunc (A) Hello() { fmt.Println(\"A\") }\n\ntype B struct{}\n\nfunc (B) Hello() { fmt.Println(\"B\") }\n\ntype C struct {\n    A\n    B\n}\n\nfunc main() {\n    c := C{}\n    \u002F\u002F c.Hello() — ОШИБКА компиляции: ambiguous selector c.Hello\n    c.A.Hello() \u002F\u002F ОК — \"A\"\n    c.B.Hello() \u002F\u002F ОК — \"B\"\n}\n",[86,5630,5631,5637,5641,5651,5655,5666,5670,5694,5698,5709,5713,5737,5741,5752,5757,5762,5766,5770,5778,5789,5794,5806,5818],{"__ignoreMap":104},[108,5632,5633,5635],{"class":110,"line":111},[108,5634,328],{"class":121},[108,5636,1445],{"class":125},[108,5638,5639],{"class":110,"line":118},[108,5640,169],{"emptyLinePlaceholder":168},[108,5642,5643,5645,5647,5649],{"class":110,"line":136},[108,5644,1454],{"class":121},[108,5646,1457],{"class":522},[108,5648,1460],{"class":125},[108,5650,1463],{"class":522},[108,5652,5653],{"class":110,"line":148},[108,5654,169],{"emptyLinePlaceholder":168},[108,5656,5657,5659,5662,5664],{"class":110,"line":159},[108,5658,122],{"class":121},[108,5660,5661],{"class":125}," A",[108,5663,129],{"class":121},[108,5665,969],{"class":132},[108,5667,5668],{"class":110,"line":165},[108,5669,169],{"emptyLinePlaceholder":168},[108,5671,5672,5674,5676,5679,5681,5683,5685,5687,5689,5692],{"class":110,"line":172},[108,5673,181],{"class":121},[108,5675,249],{"class":132},[108,5677,5678],{"class":125},"A",[108,5680,203],{"class":132},[108,5682,1783],{"class":125},[108,5684,5509],{"class":132},[108,5686,1512],{"class":125},[108,5688,187],{"class":132},[108,5690,5691],{"class":522},"\"A\"",[108,5693,1097],{"class":132},[108,5695,5696],{"class":110,"line":178},[108,5697,169],{"emptyLinePlaceholder":168},[108,5699,5700,5702,5705,5707],{"class":110,"line":214},[108,5701,122],{"class":121},[108,5703,5704],{"class":125}," B",[108,5706,129],{"class":121},[108,5708,969],{"class":132},[108,5710,5711],{"class":110,"line":228},[108,5712,169],{"emptyLinePlaceholder":168},[108,5714,5715,5717,5719,5722,5724,5726,5728,5730,5732,5735],{"class":110,"line":233},[108,5716,181],{"class":121},[108,5718,249],{"class":132},[108,5720,5721],{"class":125},"B",[108,5723,203],{"class":132},[108,5725,1783],{"class":125},[108,5727,5509],{"class":132},[108,5729,1512],{"class":125},[108,5731,187],{"class":132},[108,5733,5734],{"class":522},"\"B\"",[108,5736,1097],{"class":132},[108,5738,5739],{"class":110,"line":238},[108,5740,169],{"emptyLinePlaceholder":168},[108,5742,5743,5745,5748,5750],{"class":110,"line":244},[108,5744,122],{"class":121},[108,5746,5747],{"class":125}," C",[108,5749,129],{"class":121},[108,5751,133],{"class":132},[108,5753,5754],{"class":110,"line":835},[108,5755,5756],{"class":125},"    A\n",[108,5758,5759],{"class":110,"line":843},[108,5760,5761],{"class":125},"    B\n",[108,5763,5764],{"class":110,"line":848},[108,5765,162],{"class":132},[108,5767,5768],{"class":110,"line":853},[108,5769,169],{"emptyLinePlaceholder":168},[108,5771,5772,5774,5776],{"class":110,"line":877},[108,5773,181],{"class":121},[108,5775,1561],{"class":125},[108,5777,1564],{"class":132},[108,5779,5780,5783,5785,5787],{"class":110,"line":3133},[108,5781,5782],{"class":132},"    c ",[108,5784,824],{"class":121},[108,5786,5747],{"class":125},[108,5788,969],{"class":132},[108,5790,5791],{"class":110,"line":3139},[108,5792,5793],{"class":114},"    \u002F\u002F c.Hello() — ОШИБКА компиляции: ambiguous selector c.Hello\n",[108,5795,5796,5799,5801,5803],{"class":110,"line":5432},[108,5797,5798],{"class":132},"    c.A.",[108,5800,1783],{"class":125},[108,5802,264],{"class":132},[108,5804,5805],{"class":114},"\u002F\u002F ОК — \"A\"\n",[108,5807,5808,5811,5813,5815],{"class":110,"line":5445},[108,5809,5810],{"class":132},"    c.B.",[108,5812,1783],{"class":125},[108,5814,264],{"class":132},[108,5816,5817],{"class":114},"\u002F\u002F ОК — \"B\"\n",[108,5819,5821],{"class":110,"line":5820},23,[108,5822,162],{"class":132},[11,5824,5825,5828],{},[15,5826,5827],{},"Решение конфликта",": либо явный вызов, либо определи свой метод Hello на C (он затенит оба).",[99,5830,5832],{"className":101,"code":5831,"language":103,"meta":104,"style":104},"func (c C) Hello() { c.A.Hello() }  \u002F\u002F явный выбор\n",[86,5833,5834],{"__ignoreMap":104},[108,5835,5836,5838,5840,5842,5845,5847,5849,5852,5854,5857],{"class":110,"line":111},[108,5837,181],{"class":121},[108,5839,249],{"class":132},[108,5841,1833],{"class":190},[108,5843,5844],{"class":125},"C",[108,5846,203],{"class":132},[108,5848,1783],{"class":125},[108,5850,5851],{"class":132},"() { c.A.",[108,5853,1783],{"class":125},[108,5855,5856],{"class":132},"() }  ",[108,5858,5859],{"class":114},"\u002F\u002F явный выбор\n",[315,5861,5863],{"id":5862},"правило-приоритета","Правило приоритета",[11,5865,5866],{},"Компилятор ищет метод\u002Fполе по \"глубине\": сначала на самом типе, потом на встроенных типах глубины 1, потом глубины 2 и т.д. Одинаковая глубина + одинаковое имя = ошибка компиляции.",[308,5868],{},[11,5870,5871],{},"Каждый модуль (пакет, структура, функция) отвечает за одну вещь. Одна причина для изменения.",[99,5873,5875],{"className":101,"code":5874,"language":103,"meta":104,"style":104},"\u002F\u002F ❌ Плохо: UserService делает всё\ntype UserService struct{ db *sql.DB }\n\nfunc (s *UserService) Create(u User) error { ... }\nfunc (s *UserService) SendEmail(to, body string) error { ... }\nfunc (s *UserService) GenerateReport() ([]byte, error) { ... }\n\u002F\u002F Изменения в email-логике ломают UserService\n\n\u002F\u002F ✅ Хорошо: каждый сервис — одна ответственность\ntype UserService struct{ db *sql.DB }\nfunc (s *UserService) Create(u User) error { ... }\n\ntype EmailService struct{ smtp *SMTPClient }\nfunc (s *EmailService) Send(to, body string) error { ... }\n\ntype ReportService struct{ db *sql.DB }\nfunc (s *ReportService) Generate() ([]byte, error) { ... }\n",[86,5876,5877,5882,5906,5910,5945,5984,6016,6021,6025,6030,6050,6082,6086,6105,6143,6147,6168],{"__ignoreMap":104},[108,5878,5879],{"class":110,"line":111},[108,5880,5881],{"class":114},"\u002F\u002F ❌ Плохо: UserService делает всё\n",[108,5883,5884,5886,5889,5891,5894,5896,5899,5901,5904],{"class":110,"line":118},[108,5885,122],{"class":121},[108,5887,5888],{"class":125}," UserService",[108,5890,129],{"class":121},[108,5892,5893],{"class":132},"{ db ",[108,5895,206],{"class":121},[108,5897,5898],{"class":125},"sql",[108,5900,2714],{"class":132},[108,5902,5903],{"class":125},"DB",[108,5905,482],{"class":132},[108,5907,5908],{"class":110,"line":136},[108,5909,169],{"emptyLinePlaceholder":168},[108,5911,5912,5914,5916,5918,5920,5923,5925,5928,5930,5932,5934,5936,5938,5940,5943],{"class":110,"line":148},[108,5913,181],{"class":121},[108,5915,249],{"class":132},[108,5917,1645],{"class":190},[108,5919,206],{"class":121},[108,5921,5922],{"class":125},"UserService",[108,5924,203],{"class":132},[108,5926,5927],{"class":125},"Create",[108,5929,187],{"class":132},[108,5931,503],{"class":190},[108,5933,126],{"class":125},[108,5935,203],{"class":132},[108,5937,710],{"class":121},[108,5939,269],{"class":132},[108,5941,5942],{"class":121},"...",[108,5944,482],{"class":132},[108,5946,5947,5949,5951,5953,5955,5957,5959,5962,5964,5967,5969,5972,5974,5976,5978,5980,5982],{"class":110,"line":159},[108,5948,181],{"class":121},[108,5950,249],{"class":132},[108,5952,1645],{"class":190},[108,5954,206],{"class":121},[108,5956,5922],{"class":125},[108,5958,203],{"class":132},[108,5960,5961],{"class":125},"SendEmail",[108,5963,187],{"class":132},[108,5965,5966],{"class":190},"to",[108,5968,194],{"class":132},[108,5970,5971],{"class":190},"body",[108,5973,200],{"class":121},[108,5975,203],{"class":132},[108,5977,710],{"class":121},[108,5979,269],{"class":132},[108,5981,5942],{"class":121},[108,5983,482],{"class":132},[108,5985,5986,5988,5990,5992,5994,5996,5998,6001,6004,6006,6008,6010,6012,6014],{"class":110,"line":165},[108,5987,181],{"class":121},[108,5989,249],{"class":132},[108,5991,1645],{"class":190},[108,5993,206],{"class":121},[108,5995,5922],{"class":125},[108,5997,203],{"class":132},[108,5999,6000],{"class":125},"GenerateReport",[108,6002,6003],{"class":132},"() ([]",[108,6005,629],{"class":121},[108,6007,194],{"class":132},[108,6009,710],{"class":121},[108,6011,713],{"class":132},[108,6013,5942],{"class":121},[108,6015,482],{"class":132},[108,6017,6018],{"class":110,"line":172},[108,6019,6020],{"class":114},"\u002F\u002F Изменения в email-логике ломают UserService\n",[108,6022,6023],{"class":110,"line":178},[108,6024,169],{"emptyLinePlaceholder":168},[108,6026,6027],{"class":110,"line":214},[108,6028,6029],{"class":114},"\u002F\u002F ✅ Хорошо: каждый сервис — одна ответственность\n",[108,6031,6032,6034,6036,6038,6040,6042,6044,6046,6048],{"class":110,"line":228},[108,6033,122],{"class":121},[108,6035,5888],{"class":125},[108,6037,129],{"class":121},[108,6039,5893],{"class":132},[108,6041,206],{"class":121},[108,6043,5898],{"class":125},[108,6045,2714],{"class":132},[108,6047,5903],{"class":125},[108,6049,482],{"class":132},[108,6051,6052,6054,6056,6058,6060,6062,6064,6066,6068,6070,6072,6074,6076,6078,6080],{"class":110,"line":233},[108,6053,181],{"class":121},[108,6055,249],{"class":132},[108,6057,1645],{"class":190},[108,6059,206],{"class":121},[108,6061,5922],{"class":125},[108,6063,203],{"class":132},[108,6065,5927],{"class":125},[108,6067,187],{"class":132},[108,6069,503],{"class":190},[108,6071,126],{"class":125},[108,6073,203],{"class":132},[108,6075,710],{"class":121},[108,6077,269],{"class":132},[108,6079,5942],{"class":121},[108,6081,482],{"class":132},[108,6083,6084],{"class":110,"line":238},[108,6085,169],{"emptyLinePlaceholder":168},[108,6087,6088,6090,6093,6095,6098,6100,6103],{"class":110,"line":244},[108,6089,122],{"class":121},[108,6091,6092],{"class":125}," EmailService",[108,6094,129],{"class":121},[108,6096,6097],{"class":132},"{ smtp ",[108,6099,206],{"class":121},[108,6101,6102],{"class":125},"SMTPClient",[108,6104,482],{"class":132},[108,6106,6107,6109,6111,6113,6115,6118,6120,6123,6125,6127,6129,6131,6133,6135,6137,6139,6141],{"class":110,"line":835},[108,6108,181],{"class":121},[108,6110,249],{"class":132},[108,6112,1645],{"class":190},[108,6114,206],{"class":121},[108,6116,6117],{"class":125},"EmailService",[108,6119,203],{"class":132},[108,6121,6122],{"class":125},"Send",[108,6124,187],{"class":132},[108,6126,5966],{"class":190},[108,6128,194],{"class":132},[108,6130,5971],{"class":190},[108,6132,200],{"class":121},[108,6134,203],{"class":132},[108,6136,710],{"class":121},[108,6138,269],{"class":132},[108,6140,5942],{"class":121},[108,6142,482],{"class":132},[108,6144,6145],{"class":110,"line":843},[108,6146,169],{"emptyLinePlaceholder":168},[108,6148,6149,6151,6154,6156,6158,6160,6162,6164,6166],{"class":110,"line":848},[108,6150,122],{"class":121},[108,6152,6153],{"class":125}," ReportService",[108,6155,129],{"class":121},[108,6157,5893],{"class":132},[108,6159,206],{"class":121},[108,6161,5898],{"class":125},[108,6163,2714],{"class":132},[108,6165,5903],{"class":125},[108,6167,482],{"class":132},[108,6169,6170,6172,6174,6176,6178,6181,6183,6186,6188,6190,6192,6194,6196,6198],{"class":110,"line":853},[108,6171,181],{"class":121},[108,6173,249],{"class":132},[108,6175,1645],{"class":190},[108,6177,206],{"class":121},[108,6179,6180],{"class":125},"ReportService",[108,6182,203],{"class":132},[108,6184,6185],{"class":125},"Generate",[108,6187,6003],{"class":132},[108,6189,629],{"class":121},[108,6191,194],{"class":132},[108,6193,710],{"class":121},[108,6195,713],{"class":132},[108,6197,5942],{"class":121},[108,6199,482],{"class":132},[11,6201,6202],{},[15,6203,6204],{},"Как понять что нарушается:",[285,6206,6207,6210,6213],{},[288,6208,6209],{},"Структура имеет методы из разных доменов (юзеры + email + отчёты)",[288,6211,6212],{},"При изменении одной фичи приходится менять несвязанный код",[288,6214,6215],{},"Название сервиса слишком общее (Manager, Handler, Utils)",[308,6217],{},[11,6219,6220],{},"Открыт для расширения, закрыт для модификации. Добавляем новое поведение не меняя существующий код.",[99,6222,6224],{"className":101,"code":6223,"language":103,"meta":104,"style":104},"\u002F\u002F ❌ Плохо: добавление нового типа уведомления — правим существующую функцию\nfunc Notify(method string, msg string) {\n    if method == \"email\" {\n        sendEmail(msg)\n    } else if method == \"sms\" {\n        sendSMS(msg)\n    }\n    \u002F\u002F добавить telegram? правим эту функцию\n}\n\n\u002F\u002F ✅ Хорошо: новый тип — новая структура, старый код не трогаем\ntype Notifier interface {\n    Notify(msg string) error\n}\n\ntype EmailNotifier struct{ ... }\nfunc (e *EmailNotifier) Notify(msg string) error { ... }\n\ntype SMSNotifier struct{ ... }\nfunc (s *SMSNotifier) Notify(msg string) error { ... }\n\n\u002F\u002F Добавить telegram — просто новая структура:\ntype TelegramNotifier struct{ ... }\nfunc (t *TelegramNotifier) Notify(msg string) error { ... }\n\n\u002F\u002F Существующий код не меняется:\nfunc Send(n Notifier, msg string) error {\n    return n.Notify(msg)\n}\n",[86,6225,6226,6231,6253,6267,6275,6295,6302,6306,6311,6315,6319,6324,6335,6350,6354,6358,6373,6408,6412,6427,6460,6464,6469,6484,6519,6524,6530,6556,6568],{"__ignoreMap":104},[108,6227,6228],{"class":110,"line":111},[108,6229,6230],{"class":114},"\u002F\u002F ❌ Плохо: добавление нового типа уведомления — правим существующую функцию\n",[108,6232,6233,6235,6238,6240,6243,6245,6247,6249,6251],{"class":110,"line":118},[108,6234,181],{"class":121},[108,6236,6237],{"class":125}," Notify",[108,6239,187],{"class":132},[108,6241,6242],{"class":190},"method",[108,6244,200],{"class":121},[108,6246,194],{"class":132},[108,6248,1504],{"class":190},[108,6250,200],{"class":121},[108,6252,511],{"class":132},[108,6254,6255,6257,6260,6262,6265],{"class":110,"line":136},[108,6256,2230],{"class":121},[108,6258,6259],{"class":132}," method ",[108,6261,4158],{"class":121},[108,6263,6264],{"class":522}," \"email\"",[108,6266,133],{"class":132},[108,6268,6269,6272],{"class":110,"line":148},[108,6270,6271],{"class":125},"        sendEmail",[108,6273,6274],{"class":132},"(msg)\n",[108,6276,6277,6280,6283,6286,6288,6290,6293],{"class":110,"line":159},[108,6278,6279],{"class":132},"    } ",[108,6281,6282],{"class":121},"else",[108,6284,6285],{"class":121}," if",[108,6287,6259],{"class":132},[108,6289,4158],{"class":121},[108,6291,6292],{"class":522}," \"sms\"",[108,6294,133],{"class":132},[108,6296,6297,6300],{"class":110,"line":165},[108,6298,6299],{"class":125},"        sendSMS",[108,6301,6274],{"class":132},[108,6303,6304],{"class":110,"line":172},[108,6305,1342],{"class":132},[108,6307,6308],{"class":110,"line":178},[108,6309,6310],{"class":114},"    \u002F\u002F добавить telegram? правим эту функцию\n",[108,6312,6313],{"class":110,"line":214},[108,6314,162],{"class":132},[108,6316,6317],{"class":110,"line":228},[108,6318,169],{"emptyLinePlaceholder":168},[108,6320,6321],{"class":110,"line":233},[108,6322,6323],{"class":114},"\u002F\u002F ✅ Хорошо: новый тип — новая структура, старый код не трогаем\n",[108,6325,6326,6328,6331,6333],{"class":110,"line":238},[108,6327,122],{"class":121},[108,6329,6330],{"class":125}," Notifier",[108,6332,612],{"class":121},[108,6334,133],{"class":132},[108,6336,6337,6340,6342,6344,6346,6348],{"class":110,"line":244},[108,6338,6339],{"class":125},"    Notify",[108,6341,187],{"class":132},[108,6343,1504],{"class":190},[108,6345,200],{"class":121},[108,6347,203],{"class":132},[108,6349,4342],{"class":121},[108,6351,6352],{"class":110,"line":835},[108,6353,162],{"class":132},[108,6355,6356],{"class":110,"line":843},[108,6357,169],{"emptyLinePlaceholder":168},[108,6359,6360,6362,6365,6367,6369,6371],{"class":110,"line":848},[108,6361,122],{"class":121},[108,6363,6364],{"class":125}," EmailNotifier",[108,6366,129],{"class":121},[108,6368,1820],{"class":132},[108,6370,5942],{"class":121},[108,6372,482],{"class":132},[108,6374,6375,6377,6379,6382,6384,6387,6389,6392,6394,6396,6398,6400,6402,6404,6406],{"class":110,"line":853},[108,6376,181],{"class":121},[108,6378,249],{"class":132},[108,6380,6381],{"class":190},"e ",[108,6383,206],{"class":121},[108,6385,6386],{"class":125},"EmailNotifier",[108,6388,203],{"class":132},[108,6390,6391],{"class":125},"Notify",[108,6393,187],{"class":132},[108,6395,1504],{"class":190},[108,6397,200],{"class":121},[108,6399,203],{"class":132},[108,6401,710],{"class":121},[108,6403,269],{"class":132},[108,6405,5942],{"class":121},[108,6407,482],{"class":132},[108,6409,6410],{"class":110,"line":877},[108,6411,169],{"emptyLinePlaceholder":168},[108,6413,6414,6416,6419,6421,6423,6425],{"class":110,"line":3133},[108,6415,122],{"class":121},[108,6417,6418],{"class":125}," SMSNotifier",[108,6420,129],{"class":121},[108,6422,1820],{"class":132},[108,6424,5942],{"class":121},[108,6426,482],{"class":132},[108,6428,6429,6431,6433,6435,6437,6440,6442,6444,6446,6448,6450,6452,6454,6456,6458],{"class":110,"line":3139},[108,6430,181],{"class":121},[108,6432,249],{"class":132},[108,6434,1645],{"class":190},[108,6436,206],{"class":121},[108,6438,6439],{"class":125},"SMSNotifier",[108,6441,203],{"class":132},[108,6443,6391],{"class":125},[108,6445,187],{"class":132},[108,6447,1504],{"class":190},[108,6449,200],{"class":121},[108,6451,203],{"class":132},[108,6453,710],{"class":121},[108,6455,269],{"class":132},[108,6457,5942],{"class":121},[108,6459,482],{"class":132},[108,6461,6462],{"class":110,"line":5432},[108,6463,169],{"emptyLinePlaceholder":168},[108,6465,6466],{"class":110,"line":5445},[108,6467,6468],{"class":114},"\u002F\u002F Добавить telegram — просто новая структура:\n",[108,6470,6471,6473,6476,6478,6480,6482],{"class":110,"line":5820},[108,6472,122],{"class":121},[108,6474,6475],{"class":125}," TelegramNotifier",[108,6477,129],{"class":121},[108,6479,1820],{"class":132},[108,6481,5942],{"class":121},[108,6483,482],{"class":132},[108,6485,6487,6489,6491,6494,6496,6499,6501,6503,6505,6507,6509,6511,6513,6515,6517],{"class":110,"line":6486},24,[108,6488,181],{"class":121},[108,6490,249],{"class":132},[108,6492,6493],{"class":190},"t ",[108,6495,206],{"class":121},[108,6497,6498],{"class":125},"TelegramNotifier",[108,6500,203],{"class":132},[108,6502,6391],{"class":125},[108,6504,187],{"class":132},[108,6506,1504],{"class":190},[108,6508,200],{"class":121},[108,6510,203],{"class":132},[108,6512,710],{"class":121},[108,6514,269],{"class":132},[108,6516,5942],{"class":121},[108,6518,482],{"class":132},[108,6520,6522],{"class":110,"line":6521},25,[108,6523,169],{"emptyLinePlaceholder":168},[108,6525,6527],{"class":110,"line":6526},26,[108,6528,6529],{"class":114},"\u002F\u002F Существующий код не меняется:\n",[108,6531,6533,6535,6538,6540,6542,6544,6546,6548,6550,6552,6554],{"class":110,"line":6532},27,[108,6534,181],{"class":121},[108,6536,6537],{"class":125}," Send",[108,6539,187],{"class":132},[108,6541,635],{"class":190},[108,6543,6330],{"class":125},[108,6545,194],{"class":132},[108,6547,1504],{"class":190},[108,6549,200],{"class":121},[108,6551,203],{"class":132},[108,6553,710],{"class":121},[108,6555,133],{"class":132},[108,6557,6559,6561,6564,6566],{"class":110,"line":6558},28,[108,6560,217],{"class":121},[108,6562,6563],{"class":132}," n.",[108,6565,6391],{"class":125},[108,6567,6274],{"class":132},[108,6569,6571],{"class":110,"line":6570},29,[108,6572,162],{"class":132},[308,6574],{},[11,6576,6577],{},"Любая реализация интерфейса должна быть взаимозаменяема без поломки логики.\nЕсли функция работает с интерфейсом, подставь любую реализацию — программа должна остаться корректной.",[99,6579,6581],{"className":101,"code":6580,"language":103,"meta":104,"style":104},"type Storage interface {\n    Save(data []byte) error\n    Load(key string) ([]byte, error)\n}\n\ntype FileStorage struct{ ... }\ntype S3Storage struct{ ... }\ntype RedisStorage struct{ ... }\n\n\u002F\u002F Все три можно подставить — поведение корректно\nfunc ProcessData(s Storage) error {\n    err := s.Save(data)   \u002F\u002F работает одинаково с любой реализацией\n    return err\n}\n",[86,6582,6583,6594,6611,6634,6638,6642,6657,6672,6687,6691,6696,6715,6732,6738],{"__ignoreMap":104},[108,6584,6585,6587,6590,6592],{"class":110,"line":111},[108,6586,122],{"class":121},[108,6588,6589],{"class":125}," Storage",[108,6591,612],{"class":121},[108,6593,133],{"class":132},[108,6595,6596,6599,6601,6603,6605,6607,6609],{"class":110,"line":118},[108,6597,6598],{"class":125},"    Save",[108,6600,187],{"class":132},[108,6602,806],{"class":190},[108,6604,626],{"class":132},[108,6606,629],{"class":121},[108,6608,203],{"class":132},[108,6610,4342],{"class":121},[108,6612,6613,6616,6618,6621,6623,6626,6628,6630,6632],{"class":110,"line":136},[108,6614,6615],{"class":125},"    Load",[108,6617,187],{"class":132},[108,6619,6620],{"class":190},"key",[108,6622,200],{"class":121},[108,6624,6625],{"class":132},") ([]",[108,6627,629],{"class":121},[108,6629,194],{"class":132},[108,6631,710],{"class":121},[108,6633,649],{"class":132},[108,6635,6636],{"class":110,"line":148},[108,6637,162],{"class":132},[108,6639,6640],{"class":110,"line":159},[108,6641,169],{"emptyLinePlaceholder":168},[108,6643,6644,6646,6649,6651,6653,6655],{"class":110,"line":165},[108,6645,122],{"class":121},[108,6647,6648],{"class":125}," FileStorage",[108,6650,129],{"class":121},[108,6652,1820],{"class":132},[108,6654,5942],{"class":121},[108,6656,482],{"class":132},[108,6658,6659,6661,6664,6666,6668,6670],{"class":110,"line":172},[108,6660,122],{"class":121},[108,6662,6663],{"class":125}," S3Storage",[108,6665,129],{"class":121},[108,6667,1820],{"class":132},[108,6669,5942],{"class":121},[108,6671,482],{"class":132},[108,6673,6674,6676,6679,6681,6683,6685],{"class":110,"line":178},[108,6675,122],{"class":121},[108,6677,6678],{"class":125}," RedisStorage",[108,6680,129],{"class":121},[108,6682,1820],{"class":132},[108,6684,5942],{"class":121},[108,6686,482],{"class":132},[108,6688,6689],{"class":110,"line":214},[108,6690,169],{"emptyLinePlaceholder":168},[108,6692,6693],{"class":110,"line":228},[108,6694,6695],{"class":114},"\u002F\u002F Все три можно подставить — поведение корректно\n",[108,6697,6698,6700,6703,6705,6707,6709,6711,6713],{"class":110,"line":233},[108,6699,181],{"class":121},[108,6701,6702],{"class":125}," ProcessData",[108,6704,187],{"class":132},[108,6706,2958],{"class":190},[108,6708,6589],{"class":125},[108,6710,203],{"class":132},[108,6712,710],{"class":121},[108,6714,133],{"class":132},[108,6716,6717,6720,6722,6724,6726,6729],{"class":110,"line":238},[108,6718,6719],{"class":132},"    err ",[108,6721,824],{"class":121},[108,6723,4397],{"class":132},[108,6725,856],{"class":125},[108,6727,6728],{"class":132},"(data)   ",[108,6730,6731],{"class":114},"\u002F\u002F работает одинаково с любой реализацией\n",[108,6733,6734,6736],{"class":110,"line":244},[108,6735,217],{"class":121},[108,6737,840],{"class":132},[108,6739,6740],{"class":110,"line":835},[108,6741,162],{"class":132},[11,6743,6744],{},[15,6745,6746],{},"Нарушение — реализация меняет контракт:",[99,6748,6750],{"className":101,"code":6749,"language":103,"meta":104,"style":104},"\u002F\u002F ❌ Нарушение: одна реализация паникует вместо ошибки\nfunc (r *BrokenStorage) Save(data []byte) error {\n    panic(\"not implemented\") \u002F\u002F вызывающий код ожидает error, а не panic\n}\n\n\u002F\u002F ❌ Нарушение: Save молча ничего не делает\nfunc (r *NoopStorage) Save(data []byte) error {\n    return nil \u002F\u002F данные не сохранены, но ошибки нет — обманываем вызывающий код\n}\n",[86,6751,6752,6757,6788,6803,6807,6811,6816,6847,6857],{"__ignoreMap":104},[108,6753,6754],{"class":110,"line":111},[108,6755,6756],{"class":114},"\u002F\u002F ❌ Нарушение: одна реализация паникует вместо ошибки\n",[108,6758,6759,6761,6763,6765,6767,6770,6772,6774,6776,6778,6780,6782,6784,6786],{"class":110,"line":118},[108,6760,181],{"class":121},[108,6762,249],{"class":132},[108,6764,3953],{"class":190},[108,6766,206],{"class":121},[108,6768,6769],{"class":125},"BrokenStorage",[108,6771,203],{"class":132},[108,6773,856],{"class":125},[108,6775,187],{"class":132},[108,6777,806],{"class":190},[108,6779,626],{"class":132},[108,6781,629],{"class":121},[108,6783,203],{"class":132},[108,6785,710],{"class":121},[108,6787,133],{"class":132},[108,6789,6790,6793,6795,6798,6800],{"class":110,"line":136},[108,6791,6792],{"class":125},"    panic",[108,6794,187],{"class":132},[108,6796,6797],{"class":522},"\"not implemented\"",[108,6799,203],{"class":132},[108,6801,6802],{"class":114},"\u002F\u002F вызывающий код ожидает error, а не panic\n",[108,6804,6805],{"class":110,"line":148},[108,6806,162],{"class":132},[108,6808,6809],{"class":110,"line":159},[108,6810,169],{"emptyLinePlaceholder":168},[108,6812,6813],{"class":110,"line":165},[108,6814,6815],{"class":114},"\u002F\u002F ❌ Нарушение: Save молча ничего не делает\n",[108,6817,6818,6820,6822,6824,6826,6829,6831,6833,6835,6837,6839,6841,6843,6845],{"class":110,"line":172},[108,6819,181],{"class":121},[108,6821,249],{"class":132},[108,6823,3953],{"class":190},[108,6825,206],{"class":121},[108,6827,6828],{"class":125},"NoopStorage",[108,6830,203],{"class":132},[108,6832,856],{"class":125},[108,6834,187],{"class":132},[108,6836,806],{"class":190},[108,6838,626],{"class":132},[108,6840,629],{"class":121},[108,6842,203],{"class":132},[108,6844,710],{"class":121},[108,6846,133],{"class":132},[108,6848,6849,6851,6854],{"class":110,"line":178},[108,6850,217],{"class":121},[108,6852,6853],{"class":1187}," nil",[108,6855,6856],{"class":114}," \u002F\u002F данные не сохранены, но ошибки нет — обманываем вызывающий код\n",[108,6858,6859],{"class":110,"line":214},[108,6860,162],{"class":132},[277,6862,6864,6867],{"id":6863},"правило-если-функция-работает-с-интерфейсом-подставь-любую-реализацию-программа-должна-остаться-корректной",[15,6865,6866],{},"Правило:"," если функция работает с интерфейсом, подставь любую реализацию — программа должна остаться корректной.",[11,6869,6870],{},"Много маленьких интерфейсов лучше одного большого. Потребитель зависит только от того что ему нужно.",[99,6872,6874],{"className":101,"code":6873,"language":103,"meta":104,"style":104},"\u002F\u002F ❌ Плохо: God-interface\ntype Storage interface {\n    Save(data []byte) error\n    Load(key string) ([]byte, error)\n    Delete(key string) error\n    List() ([]string, error)\n    Watch(key string) \u003C-chan Event\n    Backup() error\n}\n\n\u002F\u002F Функции которой нужно только читать — вынуждена зависеть от Backup, Watch...\n\n\u002F\u002F ✅ Хорошо: маленькие интерфейсы\ntype Reader interface {\n    Load(key string) ([]byte, error)\n}\n\ntype Writer interface {\n    Save(data []byte) error\n}\n\ntype Deleter interface {\n    Delete(key string) error\n}\n\n\u002F\u002F Каждая функция берёт только то что нужно\nfunc ProcessData(r Reader) error { ... }     \u002F\u002F нужно только чтение\nfunc SaveReport(w Writer) error { ... }       \u002F\u002F нужна только запись\nfunc Cleanup(d Deleter) error { ... }         \u002F\u002F нужно только удаление\n\n\u002F\u002F Если нужно и то и другое — композиция\ntype ReadWriter interface {\n    Reader\n    Writer\n}\n",[86,6875,6876,6881,6891,6907,6927,6942,6957,6976,6985,6989,6993,6998,7002,7007,7017,7037,7041,7045,7055,7071,7075,7079,7090,7104,7108,7112,7117,7143,7170,7198,7203,7209,7220,7225,7230],{"__ignoreMap":104},[108,6877,6878],{"class":110,"line":111},[108,6879,6880],{"class":114},"\u002F\u002F ❌ Плохо: God-interface\n",[108,6882,6883,6885,6887,6889],{"class":110,"line":118},[108,6884,122],{"class":121},[108,6886,6589],{"class":125},[108,6888,612],{"class":121},[108,6890,133],{"class":132},[108,6892,6893,6895,6897,6899,6901,6903,6905],{"class":110,"line":136},[108,6894,6598],{"class":125},[108,6896,187],{"class":132},[108,6898,806],{"class":190},[108,6900,626],{"class":132},[108,6902,629],{"class":121},[108,6904,203],{"class":132},[108,6906,4342],{"class":121},[108,6908,6909,6911,6913,6915,6917,6919,6921,6923,6925],{"class":110,"line":148},[108,6910,6615],{"class":125},[108,6912,187],{"class":132},[108,6914,6620],{"class":190},[108,6916,200],{"class":121},[108,6918,6625],{"class":132},[108,6920,629],{"class":121},[108,6922,194],{"class":132},[108,6924,710],{"class":121},[108,6926,649],{"class":132},[108,6928,6929,6932,6934,6936,6938,6940],{"class":110,"line":159},[108,6930,6931],{"class":125},"    Delete",[108,6933,187],{"class":132},[108,6935,6620],{"class":190},[108,6937,200],{"class":121},[108,6939,203],{"class":132},[108,6941,4342],{"class":121},[108,6943,6944,6947,6949,6951,6953,6955],{"class":110,"line":165},[108,6945,6946],{"class":125},"    List",[108,6948,6003],{"class":132},[108,6950,142],{"class":121},[108,6952,194],{"class":132},[108,6954,710],{"class":121},[108,6956,649],{"class":132},[108,6958,6959,6962,6964,6966,6968,6970,6973],{"class":110,"line":172},[108,6960,6961],{"class":125},"    Watch",[108,6963,187],{"class":132},[108,6965,6620],{"class":190},[108,6967,200],{"class":121},[108,6969,203],{"class":132},[108,6971,6972],{"class":121},"\u003C-chan",[108,6974,6975],{"class":125}," Event\n",[108,6977,6978,6981,6983],{"class":110,"line":178},[108,6979,6980],{"class":125},"    Backup",[108,6982,264],{"class":132},[108,6984,4342],{"class":121},[108,6986,6987],{"class":110,"line":214},[108,6988,162],{"class":132},[108,6990,6991],{"class":110,"line":228},[108,6992,169],{"emptyLinePlaceholder":168},[108,6994,6995],{"class":110,"line":233},[108,6996,6997],{"class":114},"\u002F\u002F Функции которой нужно только читать — вынуждена зависеть от Backup, Watch...\n",[108,6999,7000],{"class":110,"line":238},[108,7001,169],{"emptyLinePlaceholder":168},[108,7003,7004],{"class":110,"line":244},[108,7005,7006],{"class":114},"\u002F\u002F ✅ Хорошо: маленькие интерфейсы\n",[108,7008,7009,7011,7013,7015],{"class":110,"line":835},[108,7010,122],{"class":121},[108,7012,1071],{"class":125},[108,7014,612],{"class":121},[108,7016,133],{"class":132},[108,7018,7019,7021,7023,7025,7027,7029,7031,7033,7035],{"class":110,"line":843},[108,7020,6615],{"class":125},[108,7022,187],{"class":132},[108,7024,6620],{"class":190},[108,7026,200],{"class":121},[108,7028,6625],{"class":132},[108,7030,629],{"class":121},[108,7032,194],{"class":132},[108,7034,710],{"class":121},[108,7036,649],{"class":132},[108,7038,7039],{"class":110,"line":848},[108,7040,162],{"class":132},[108,7042,7043],{"class":110,"line":853},[108,7044,169],{"emptyLinePlaceholder":168},[108,7046,7047,7049,7051,7053],{"class":110,"line":877},[108,7048,122],{"class":121},[108,7050,609],{"class":125},[108,7052,612],{"class":121},[108,7054,133],{"class":132},[108,7056,7057,7059,7061,7063,7065,7067,7069],{"class":110,"line":3133},[108,7058,6598],{"class":125},[108,7060,187],{"class":132},[108,7062,806],{"class":190},[108,7064,626],{"class":132},[108,7066,629],{"class":121},[108,7068,203],{"class":132},[108,7070,4342],{"class":121},[108,7072,7073],{"class":110,"line":3139},[108,7074,162],{"class":132},[108,7076,7077],{"class":110,"line":5432},[108,7078,169],{"emptyLinePlaceholder":168},[108,7080,7081,7083,7086,7088],{"class":110,"line":5445},[108,7082,122],{"class":121},[108,7084,7085],{"class":125}," Deleter",[108,7087,612],{"class":121},[108,7089,133],{"class":132},[108,7091,7092,7094,7096,7098,7100,7102],{"class":110,"line":5820},[108,7093,6931],{"class":125},[108,7095,187],{"class":132},[108,7097,6620],{"class":190},[108,7099,200],{"class":121},[108,7101,203],{"class":132},[108,7103,4342],{"class":121},[108,7105,7106],{"class":110,"line":6486},[108,7107,162],{"class":132},[108,7109,7110],{"class":110,"line":6521},[108,7111,169],{"emptyLinePlaceholder":168},[108,7113,7114],{"class":110,"line":6526},[108,7115,7116],{"class":114},"\u002F\u002F Каждая функция берёт только то что нужно\n",[108,7118,7119,7121,7123,7125,7127,7129,7131,7133,7135,7137,7140],{"class":110,"line":6532},[108,7120,181],{"class":121},[108,7122,6702],{"class":125},[108,7124,187],{"class":132},[108,7126,3634],{"class":190},[108,7128,1071],{"class":125},[108,7130,203],{"class":132},[108,7132,710],{"class":121},[108,7134,269],{"class":132},[108,7136,5942],{"class":121},[108,7138,7139],{"class":132}," }     ",[108,7141,7142],{"class":114},"\u002F\u002F нужно только чтение\n",[108,7144,7145,7147,7150,7152,7154,7156,7158,7160,7162,7164,7167],{"class":110,"line":6558},[108,7146,181],{"class":121},[108,7148,7149],{"class":125}," SaveReport",[108,7151,187],{"class":132},[108,7153,799],{"class":190},[108,7155,609],{"class":125},[108,7157,203],{"class":132},[108,7159,710],{"class":121},[108,7161,269],{"class":132},[108,7163,5942],{"class":121},[108,7165,7166],{"class":132}," }       ",[108,7168,7169],{"class":114},"\u002F\u002F нужна только запись\n",[108,7171,7172,7174,7177,7179,7182,7184,7186,7188,7190,7192,7195],{"class":110,"line":6570},[108,7173,181],{"class":121},[108,7175,7176],{"class":125}," Cleanup",[108,7178,187],{"class":132},[108,7180,7181],{"class":190},"d",[108,7183,7085],{"class":125},[108,7185,203],{"class":132},[108,7187,710],{"class":121},[108,7189,269],{"class":132},[108,7191,5942],{"class":121},[108,7193,7194],{"class":132}," }         ",[108,7196,7197],{"class":114},"\u002F\u002F нужно только удаление\n",[108,7199,7201],{"class":110,"line":7200},30,[108,7202,169],{"emptyLinePlaceholder":168},[108,7204,7206],{"class":110,"line":7205},31,[108,7207,7208],{"class":114},"\u002F\u002F Если нужно и то и другое — композиция\n",[108,7210,7212,7214,7216,7218],{"class":110,"line":7211},32,[108,7213,122],{"class":121},[108,7215,1134],{"class":125},[108,7217,612],{"class":121},[108,7219,133],{"class":132},[108,7221,7223],{"class":110,"line":7222},33,[108,7224,1143],{"class":125},[108,7226,7228],{"class":110,"line":7227},34,[108,7229,1148],{"class":125},[108,7231,7233],{"class":110,"line":7232},35,[108,7234,162],{"class":132},[277,7236,7238,7239,7242,7243,7242,7246,7249],{"id":7237},"стандартная-библиотека-go-образец-ioreader-1-метод-iowriter-1-метод-ioreadwriter-композиция","Стандартная библиотека Go — образец: ",[86,7240,7241],{},"io.Reader"," (1 метод), ",[86,7244,7245],{},"io.Writer",[86,7247,7248],{},"io.ReadWriter"," (композиция).",[11,7251,7252],{},"Зависим от интерфейса (абстракции), не от конкретного типа. Зависимости передаём снаружи через конструктор.",[11,7254,7255,7258],{},[15,7256,7257],{},"Общее правило:"," всё что делает I\u002FO → за интерфейс. Чистая логика → конкретные типы.",[99,7260,7262],{"className":101,"code":7261,"language":103,"meta":104,"style":104},"\u002F\u002F ❌ Плохо: сервис создаёт зависимость сам, привязан к PostgreSQL\ntype OrderService struct {\n    db *pgx.Pool\n}\nfunc NewOrderService() *OrderService {\n    pool, _ := pgx.Connect(...)  \u002F\u002F жёсткая зависимость\n    return &OrderService{db: pool}\n}\n\n\u002F\u002F ✅ Хорошо: сервис принимает интерфейс\ntype OrderRepo interface {\n    Save(order Order) error\n    GetByID(id string) (*Order, error)\n}\n\ntype OrderService struct {\n    repo OrderRepo  \u002F\u002F зависит от интерфейса\n}\n\nfunc NewOrderService(repo OrderRepo) *OrderService {\n    return &OrderService{repo: repo}\n}\n",[86,7263,7264,7269,7280,7295,7299,7315,7337,7348,7352,7356,7361,7372,7388,7412,7416,7420,7430,7441,7445,7449,7470,7481],{"__ignoreMap":104},[108,7265,7266],{"class":110,"line":111},[108,7267,7268],{"class":114},"\u002F\u002F ❌ Плохо: сервис создаёт зависимость сам, привязан к PostgreSQL\n",[108,7270,7271,7273,7276,7278],{"class":110,"line":118},[108,7272,122],{"class":121},[108,7274,7275],{"class":125}," OrderService",[108,7277,129],{"class":121},[108,7279,133],{"class":132},[108,7281,7282,7285,7287,7290,7292],{"class":110,"line":136},[108,7283,7284],{"class":132},"    db ",[108,7286,206],{"class":121},[108,7288,7289],{"class":125},"pgx",[108,7291,2714],{"class":132},[108,7293,7294],{"class":125},"Pool\n",[108,7296,7297],{"class":110,"line":148},[108,7298,162],{"class":132},[108,7300,7301,7303,7306,7308,7310,7313],{"class":110,"line":159},[108,7302,181],{"class":121},[108,7304,7305],{"class":125}," NewOrderService",[108,7307,264],{"class":132},[108,7309,206],{"class":121},[108,7311,7312],{"class":125},"OrderService",[108,7314,133],{"class":132},[108,7316,7317,7320,7322,7325,7328,7330,7332,7334],{"class":110,"line":165},[108,7318,7319],{"class":132},"    pool, _ ",[108,7321,824],{"class":121},[108,7323,7324],{"class":132}," pgx.",[108,7326,7327],{"class":125},"Connect",[108,7329,187],{"class":132},[108,7331,5942],{"class":121},[108,7333,1191],{"class":132},[108,7335,7336],{"class":114},"\u002F\u002F жёсткая зависимость\n",[108,7338,7339,7341,7343,7345],{"class":110,"line":172},[108,7340,217],{"class":121},[108,7342,220],{"class":121},[108,7344,7312],{"class":125},[108,7346,7347],{"class":132},"{db: pool}\n",[108,7349,7350],{"class":110,"line":178},[108,7351,162],{"class":132},[108,7353,7354],{"class":110,"line":214},[108,7355,169],{"emptyLinePlaceholder":168},[108,7357,7358],{"class":110,"line":228},[108,7359,7360],{"class":114},"\u002F\u002F ✅ Хорошо: сервис принимает интерфейс\n",[108,7362,7363,7365,7368,7370],{"class":110,"line":233},[108,7364,122],{"class":121},[108,7366,7367],{"class":125}," OrderRepo",[108,7369,612],{"class":121},[108,7371,133],{"class":132},[108,7373,7374,7376,7378,7381,7384,7386],{"class":110,"line":238},[108,7375,6598],{"class":125},[108,7377,187],{"class":132},[108,7379,7380],{"class":190},"order",[108,7382,7383],{"class":125}," Order",[108,7385,203],{"class":132},[108,7387,4342],{"class":121},[108,7389,7390,7393,7395,7397,7399,7401,7403,7406,7408,7410],{"class":110,"line":244},[108,7391,7392],{"class":125},"    GetByID",[108,7394,187],{"class":132},[108,7396,946],{"class":190},[108,7398,200],{"class":121},[108,7400,632],{"class":132},[108,7402,206],{"class":121},[108,7404,7405],{"class":125},"Order",[108,7407,194],{"class":132},[108,7409,710],{"class":121},[108,7411,649],{"class":132},[108,7413,7414],{"class":110,"line":835},[108,7415,162],{"class":132},[108,7417,7418],{"class":110,"line":843},[108,7419,169],{"emptyLinePlaceholder":168},[108,7421,7422,7424,7426,7428],{"class":110,"line":848},[108,7423,122],{"class":121},[108,7425,7275],{"class":125},[108,7427,129],{"class":121},[108,7429,133],{"class":132},[108,7431,7432,7435,7438],{"class":110,"line":853},[108,7433,7434],{"class":132},"    repo ",[108,7436,7437],{"class":125},"OrderRepo",[108,7439,7440],{"class":114},"  \u002F\u002F зависит от интерфейса\n",[108,7442,7443],{"class":110,"line":877},[108,7444,162],{"class":132},[108,7446,7447],{"class":110,"line":3133},[108,7448,169],{"emptyLinePlaceholder":168},[108,7450,7451,7453,7455,7457,7460,7462,7464,7466,7468],{"class":110,"line":3139},[108,7452,181],{"class":121},[108,7454,7305],{"class":125},[108,7456,187],{"class":132},[108,7458,7459],{"class":190},"repo",[108,7461,7367],{"class":125},[108,7463,203],{"class":132},[108,7465,206],{"class":121},[108,7467,7312],{"class":125},[108,7469,133],{"class":132},[108,7471,7472,7474,7476,7478],{"class":110,"line":5432},[108,7473,217],{"class":121},[108,7475,220],{"class":121},[108,7477,7312],{"class":125},[108,7479,7480],{"class":132},"{repo: repo}\n",[108,7482,7483],{"class":110,"line":5445},[108,7484,162],{"class":132},[11,7486,7487],{},[15,7488,7489],{},"main собирает зависимости:",[99,7491,7493],{"className":101,"code":7492,"language":103,"meta":104,"style":104},"func main() {\n    db := connectPostgres()\n    repo := postgres.NewOrderRepo(db)    \u002F\u002F конкретная реализация\n    service := NewOrderService(repo)      \u002F\u002F передаём через конструктор\n    handler := NewHandler(service)\n    http.ListenAndServe(\":8080\", handler)\n}\n",[86,7494,7495,7503,7514,7532,7547,7559,7575],{"__ignoreMap":104},[108,7496,7497,7499,7501],{"class":110,"line":111},[108,7498,181],{"class":121},[108,7500,1561],{"class":125},[108,7502,1564],{"class":132},[108,7504,7505,7507,7509,7512],{"class":110,"line":118},[108,7506,7284],{"class":132},[108,7508,824],{"class":121},[108,7510,7511],{"class":125}," connectPostgres",[108,7513,1323],{"class":132},[108,7515,7516,7518,7520,7523,7526,7529],{"class":110,"line":136},[108,7517,7434],{"class":132},[108,7519,824],{"class":121},[108,7521,7522],{"class":132}," postgres.",[108,7524,7525],{"class":125},"NewOrderRepo",[108,7527,7528],{"class":132},"(db)    ",[108,7530,7531],{"class":114},"\u002F\u002F конкретная реализация\n",[108,7533,7534,7537,7539,7541,7544],{"class":110,"line":148},[108,7535,7536],{"class":132},"    service ",[108,7538,824],{"class":121},[108,7540,7305],{"class":125},[108,7542,7543],{"class":132},"(repo)      ",[108,7545,7546],{"class":114},"\u002F\u002F передаём через конструктор\n",[108,7548,7549,7552,7554,7556],{"class":110,"line":159},[108,7550,7551],{"class":132},"    handler ",[108,7553,824],{"class":121},[108,7555,1032],{"class":125},[108,7557,7558],{"class":132},"(service)\n",[108,7560,7561,7564,7567,7569,7572],{"class":110,"line":165},[108,7562,7563],{"class":132},"    http.",[108,7565,7566],{"class":125},"ListenAndServe",[108,7568,187],{"class":132},[108,7570,7571],{"class":522},"\":8080\"",[108,7573,7574],{"class":132},", handler)\n",[108,7576,7577],{"class":110,"line":172},[108,7578,162],{"class":132},[11,7580,7581],{},[15,7582,7583],{},"Что закрывать за интерфейсом:",[285,7585,7586,7589,7592,7595,7598],{},[288,7587,7588],{},"БД (repo) — основной кандидат",[288,7590,7591],{},"Внешние API (биржи, платёжки, SMS)",[288,7593,7594],{},"Кэш (Redis)",[288,7596,7597],{},"Очереди (Kafka)",[288,7599,7600],{},"Файловое хранилище (S3)",[11,7602,7603],{},[15,7604,7605],{},"В тестах:",[99,7607,7609],{"className":101,"code":7608,"language":103,"meta":104,"style":104},"type MockRepo struct{}\nfunc (m *MockRepo) Save(order Order) error { return nil }\nfunc (m *MockRepo) GetByID(id string) (*Order, error) {\n    return &Order{ID: id, Status: \"active\"}, nil\n}\n\nfunc TestOrderService(t *testing.T) {\n    service := NewOrderService(&MockRepo{}) \u002F\u002F подменили БД на мок\n    \u002F\u002F тестируем бизнес-логику без реальной БД\n}\n",[86,7610,7611,7622,7658,7693,7712,7716,7720,7744,7763,7768],{"__ignoreMap":104},[108,7612,7613,7615,7618,7620],{"class":110,"line":111},[108,7614,122],{"class":121},[108,7616,7617],{"class":125}," MockRepo",[108,7619,129],{"class":121},[108,7621,969],{"class":132},[108,7623,7624,7626,7628,7631,7633,7636,7638,7640,7642,7644,7646,7648,7650,7652,7654,7656],{"class":110,"line":118},[108,7625,181],{"class":121},[108,7627,249],{"class":132},[108,7629,7630],{"class":190},"m ",[108,7632,206],{"class":121},[108,7634,7635],{"class":125},"MockRepo",[108,7637,203],{"class":132},[108,7639,856],{"class":125},[108,7641,187],{"class":132},[108,7643,7380],{"class":190},[108,7645,7383],{"class":125},[108,7647,203],{"class":132},[108,7649,710],{"class":121},[108,7651,269],{"class":132},[108,7653,272],{"class":121},[108,7655,6853],{"class":1187},[108,7657,482],{"class":132},[108,7659,7660,7662,7664,7666,7668,7670,7672,7675,7677,7679,7681,7683,7685,7687,7689,7691],{"class":110,"line":136},[108,7661,181],{"class":121},[108,7663,249],{"class":132},[108,7665,7630],{"class":190},[108,7667,206],{"class":121},[108,7669,7635],{"class":125},[108,7671,203],{"class":132},[108,7673,7674],{"class":125},"GetByID",[108,7676,187],{"class":132},[108,7678,946],{"class":190},[108,7680,200],{"class":121},[108,7682,632],{"class":132},[108,7684,206],{"class":121},[108,7686,7405],{"class":125},[108,7688,194],{"class":132},[108,7690,710],{"class":121},[108,7692,511],{"class":132},[108,7694,7695,7697,7699,7701,7704,7707,7710],{"class":110,"line":148},[108,7696,217],{"class":121},[108,7698,220],{"class":121},[108,7700,7405],{"class":125},[108,7702,7703],{"class":132},"{ID: id, Status: ",[108,7705,7706],{"class":522},"\"active\"",[108,7708,7709],{"class":132},"}, ",[108,7711,3332],{"class":1187},[108,7713,7714],{"class":110,"line":159},[108,7715,162],{"class":132},[108,7717,7718],{"class":110,"line":165},[108,7719,169],{"emptyLinePlaceholder":168},[108,7721,7722,7724,7727,7729,7732,7734,7737,7739,7742],{"class":110,"line":172},[108,7723,181],{"class":121},[108,7725,7726],{"class":125}," TestOrderService",[108,7728,187],{"class":132},[108,7730,7731],{"class":190},"t",[108,7733,506],{"class":121},[108,7735,7736],{"class":125},"testing",[108,7738,2714],{"class":132},[108,7740,7741],{"class":125},"T",[108,7743,511],{"class":132},[108,7745,7746,7748,7750,7752,7754,7756,7758,7760],{"class":110,"line":178},[108,7747,7536],{"class":132},[108,7749,824],{"class":121},[108,7751,7305],{"class":125},[108,7753,187],{"class":132},[108,7755,566],{"class":121},[108,7757,7635],{"class":125},[108,7759,1220],{"class":132},[108,7761,7762],{"class":114},"\u002F\u002F подменили БД на мок\n",[108,7764,7765],{"class":110,"line":214},[108,7766,7767],{"class":114},"    \u002F\u002F тестируем бизнес-логику без реальной БД\n",[108,7769,7770],{"class":110,"line":228},[108,7771,162],{"class":132},[308,7773],{},[277,7775,7777],{"id":7776},"практика","Практика",[7779,7780,7784,7787,7807],"quiz",{"answer":7781,"id":7782,"xp":7783},"2","advanced-oop-q1","10",[11,7785,7786],{},"Где в Go чаще всего стоит объявлять интерфейс?",[7788,7789,7790],"template",{"v-slot:options":104},[285,7791,7792,7795,7798,7804],{},[288,7793,7794],{},"В пакете реализации, чтобы все потребители импортировали один большой контракт",[288,7796,7797],{},"В пакете потребителя, рядом с функцией или типом, которому нужна абстракция",[288,7799,7800,7801],{},"В отдельном глобальном пакете ",[86,7802,7803],{},"interfaces",[288,7805,7806],{},"Интерфейсы лучше не использовать вообще",[7788,7808,7809],{"v-slot:explanation":104},[11,7810,7811],{},"В Go интерфейс обычно принадлежит потребителю: он описывает минимальное поведение, которое нужно конкретному коду. Это помогает избегать больших абстракций, циклических зависимостей и интерфейсов \"на всякий случай\".",[7813,7814,7818,7821,7993],"predict",{"answer":7815,"id":7816,"xp":7817},"hi Go","advanced-oop-p1","15",[11,7819,7820],{},"Что выведет программа?",[7788,7822,7823],{"v-slot:code":104},[99,7824,7826],{"className":101,"code":7825,"language":103,"meta":104,"style":104},"package main\n\nimport \"fmt\"\n\ntype Greeter interface {\n    Greet() string\n}\n\ntype User struct {\n    Name string\n}\n\nfunc (u User) Greet() string {\n    return \"hi \" + u.Name\n}\n\nfunc main() {\n    var g Greeter = User{Name: \"Go\"}\n    fmt.Println(g.Greet())\n}\n",[86,7827,7828,7834,7838,7848,7852,7863,7872,7876,7880,7890,7896,7900,7904,7925,7937,7941,7945,7953,7975,7989],{"__ignoreMap":104},[108,7829,7830,7832],{"class":110,"line":111},[108,7831,328],{"class":121},[108,7833,1445],{"class":125},[108,7835,7836],{"class":110,"line":118},[108,7837,169],{"emptyLinePlaceholder":168},[108,7839,7840,7842,7844,7846],{"class":110,"line":136},[108,7841,1454],{"class":121},[108,7843,1457],{"class":522},[108,7845,1460],{"class":125},[108,7847,1463],{"class":522},[108,7849,7850],{"class":110,"line":148},[108,7851,169],{"emptyLinePlaceholder":168},[108,7853,7854,7856,7859,7861],{"class":110,"line":159},[108,7855,122],{"class":121},[108,7857,7858],{"class":125}," Greeter",[108,7860,612],{"class":121},[108,7862,133],{"class":132},[108,7864,7865,7868,7870],{"class":110,"line":165},[108,7866,7867],{"class":125},"    Greet",[108,7869,264],{"class":132},[108,7871,1546],{"class":121},[108,7873,7874],{"class":110,"line":172},[108,7875,162],{"class":132},[108,7877,7878],{"class":110,"line":178},[108,7879,169],{"emptyLinePlaceholder":168},[108,7881,7882,7884,7886,7888],{"class":110,"line":214},[108,7883,122],{"class":121},[108,7885,126],{"class":125},[108,7887,129],{"class":121},[108,7889,133],{"class":132},[108,7891,7892,7894],{"class":110,"line":228},[108,7893,5320],{"class":132},[108,7895,1546],{"class":121},[108,7897,7898],{"class":110,"line":233},[108,7899,162],{"class":132},[108,7901,7902],{"class":110,"line":238},[108,7903,169],{"emptyLinePlaceholder":168},[108,7905,7906,7908,7910,7912,7914,7916,7919,7921,7923],{"class":110,"line":244},[108,7907,181],{"class":121},[108,7909,249],{"class":132},[108,7911,252],{"class":190},[108,7913,209],{"class":125},[108,7915,203],{"class":132},[108,7917,7918],{"class":125},"Greet",[108,7920,264],{"class":132},[108,7922,142],{"class":121},[108,7924,133],{"class":132},[108,7926,7927,7929,7932,7934],{"class":110,"line":835},[108,7928,217],{"class":121},[108,7930,7931],{"class":522}," \"hi \"",[108,7933,1262],{"class":121},[108,7935,7936],{"class":132}," u.Name\n",[108,7938,7939],{"class":110,"line":843},[108,7940,162],{"class":132},[108,7942,7943],{"class":110,"line":848},[108,7944,169],{"emptyLinePlaceholder":168},[108,7946,7947,7949,7951],{"class":110,"line":853},[108,7948,181],{"class":121},[108,7950,1561],{"class":125},[108,7952,1564],{"class":132},[108,7954,7955,7958,7961,7964,7966,7968,7970,7973],{"class":110,"line":877},[108,7956,7957],{"class":121},"    var",[108,7959,7960],{"class":132}," g ",[108,7962,7963],{"class":125},"Greeter",[108,7965,1175],{"class":121},[108,7967,126],{"class":125},[108,7969,5392],{"class":132},[108,7971,7972],{"class":522},"\"Go\"",[108,7974,162],{"class":132},[108,7976,7977,7979,7981,7984,7986],{"class":110,"line":3133},[108,7978,3560],{"class":132},[108,7980,1512],{"class":125},[108,7982,7983],{"class":132},"(g.",[108,7985,7918],{"class":125},[108,7987,7988],{"class":132},"())\n",[108,7990,7991],{"class":110,"line":3139},[108,7992,162],{"class":132},[7788,7994,7995],{"v-slot:hint":104},[11,7996,7997,7999,8000,8002,8003,2714],{},[86,7998,209],{}," не объявляет, что реализует ",[86,8001,7963],{},". В Go соответствие интерфейсу неявное: достаточно иметь метод ",[86,8004,8005],{},"Greet() string",[8007,8008,8012,8025,8304],"code-task",{"expected":8009,"id":8010,"xp":8011},"sent: order-42","advanced-oop-ct1","20",[11,8013,8014,8015,8017,8018,8021,8022,2714],{},"Собери сервис через dependency injection: ",[86,8016,7312],{}," должен получать ",[86,8019,8020],{},"Notifier"," снаружи и использовать его в методе ",[86,8023,8024],{},"Complete",[7788,8026,8027],{"v-slot:template":104},[99,8028,8030],{"className":101,"code":8029,"language":103,"meta":104,"style":104},"package main\n\nimport \"fmt\"\n\ntype Notifier interface {\n    Send(message string) error\n}\n\ntype OrderService struct {\n}\n\nfunc NewOrderService(notifier Notifier) *OrderService {\n    return &OrderService{}\n}\n\nfunc (s *OrderService) Complete(orderID string) error {\n    return nil\n}\n\ntype FakeNotifier struct{}\n\nfunc (FakeNotifier) Send(message string) error {\n    fmt.Println(\"sent:\", message)\n    return nil\n}\n\nfunc main() {\n    service := NewOrderService(FakeNotifier{})\n    _ = service.Complete(\"order-42\")\n}\n",[86,8031,8032,8038,8042,8052,8056,8066,8082,8086,8090,8100,8104,8108,8129,8139,8143,8147,8176,8182,8186,8190,8201,8205,8230,8244,8250,8254,8258,8266,8281,8300],{"__ignoreMap":104},[108,8033,8034,8036],{"class":110,"line":111},[108,8035,328],{"class":121},[108,8037,1445],{"class":125},[108,8039,8040],{"class":110,"line":118},[108,8041,169],{"emptyLinePlaceholder":168},[108,8043,8044,8046,8048,8050],{"class":110,"line":136},[108,8045,1454],{"class":121},[108,8047,1457],{"class":522},[108,8049,1460],{"class":125},[108,8051,1463],{"class":522},[108,8053,8054],{"class":110,"line":148},[108,8055,169],{"emptyLinePlaceholder":168},[108,8057,8058,8060,8062,8064],{"class":110,"line":159},[108,8059,122],{"class":121},[108,8061,6330],{"class":125},[108,8063,612],{"class":121},[108,8065,133],{"class":132},[108,8067,8068,8071,8073,8076,8078,8080],{"class":110,"line":165},[108,8069,8070],{"class":125},"    Send",[108,8072,187],{"class":132},[108,8074,8075],{"class":190},"message",[108,8077,200],{"class":121},[108,8079,203],{"class":132},[108,8081,4342],{"class":121},[108,8083,8084],{"class":110,"line":172},[108,8085,162],{"class":132},[108,8087,8088],{"class":110,"line":178},[108,8089,169],{"emptyLinePlaceholder":168},[108,8091,8092,8094,8096,8098],{"class":110,"line":214},[108,8093,122],{"class":121},[108,8095,7275],{"class":125},[108,8097,129],{"class":121},[108,8099,133],{"class":132},[108,8101,8102],{"class":110,"line":228},[108,8103,162],{"class":132},[108,8105,8106],{"class":110,"line":233},[108,8107,169],{"emptyLinePlaceholder":168},[108,8109,8110,8112,8114,8116,8119,8121,8123,8125,8127],{"class":110,"line":238},[108,8111,181],{"class":121},[108,8113,7305],{"class":125},[108,8115,187],{"class":132},[108,8117,8118],{"class":190},"notifier",[108,8120,6330],{"class":125},[108,8122,203],{"class":132},[108,8124,206],{"class":121},[108,8126,7312],{"class":125},[108,8128,133],{"class":132},[108,8130,8131,8133,8135,8137],{"class":110,"line":244},[108,8132,217],{"class":121},[108,8134,220],{"class":121},[108,8136,7312],{"class":125},[108,8138,969],{"class":132},[108,8140,8141],{"class":110,"line":835},[108,8142,162],{"class":132},[108,8144,8145],{"class":110,"line":843},[108,8146,169],{"emptyLinePlaceholder":168},[108,8148,8149,8151,8153,8155,8157,8159,8161,8163,8165,8168,8170,8172,8174],{"class":110,"line":848},[108,8150,181],{"class":121},[108,8152,249],{"class":132},[108,8154,1645],{"class":190},[108,8156,206],{"class":121},[108,8158,7312],{"class":125},[108,8160,203],{"class":132},[108,8162,8024],{"class":125},[108,8164,187],{"class":132},[108,8166,8167],{"class":190},"orderID",[108,8169,200],{"class":121},[108,8171,203],{"class":132},[108,8173,710],{"class":121},[108,8175,133],{"class":132},[108,8177,8178,8180],{"class":110,"line":853},[108,8179,217],{"class":121},[108,8181,2273],{"class":1187},[108,8183,8184],{"class":110,"line":877},[108,8185,162],{"class":132},[108,8187,8188],{"class":110,"line":3133},[108,8189,169],{"emptyLinePlaceholder":168},[108,8191,8192,8194,8197,8199],{"class":110,"line":3139},[108,8193,122],{"class":121},[108,8195,8196],{"class":125}," FakeNotifier",[108,8198,129],{"class":121},[108,8200,969],{"class":132},[108,8202,8203],{"class":110,"line":5432},[108,8204,169],{"emptyLinePlaceholder":168},[108,8206,8207,8209,8211,8214,8216,8218,8220,8222,8224,8226,8228],{"class":110,"line":5445},[108,8208,181],{"class":121},[108,8210,249],{"class":132},[108,8212,8213],{"class":125},"FakeNotifier",[108,8215,203],{"class":132},[108,8217,6122],{"class":125},[108,8219,187],{"class":132},[108,8221,8075],{"class":190},[108,8223,200],{"class":121},[108,8225,203],{"class":132},[108,8227,710],{"class":121},[108,8229,133],{"class":132},[108,8231,8232,8234,8236,8238,8241],{"class":110,"line":5820},[108,8233,3560],{"class":132},[108,8235,1512],{"class":125},[108,8237,187],{"class":132},[108,8239,8240],{"class":522},"\"sent:\"",[108,8242,8243],{"class":132},", message)\n",[108,8245,8246,8248],{"class":110,"line":6486},[108,8247,217],{"class":121},[108,8249,2273],{"class":1187},[108,8251,8252],{"class":110,"line":6521},[108,8253,162],{"class":132},[108,8255,8256],{"class":110,"line":6526},[108,8257,169],{"emptyLinePlaceholder":168},[108,8259,8260,8262,8264],{"class":110,"line":6532},[108,8261,181],{"class":121},[108,8263,1561],{"class":125},[108,8265,1564],{"class":132},[108,8267,8268,8270,8272,8274,8276,8278],{"class":110,"line":6558},[108,8269,7536],{"class":132},[108,8271,824],{"class":121},[108,8273,7305],{"class":125},[108,8275,187],{"class":132},[108,8277,8213],{"class":125},[108,8279,8280],{"class":132},"{})\n",[108,8282,8283,8286,8288,8291,8293,8295,8298],{"class":110,"line":6570},[108,8284,8285],{"class":132},"    _ ",[108,8287,519],{"class":121},[108,8289,8290],{"class":132}," service.",[108,8292,8024],{"class":125},[108,8294,187],{"class":132},[108,8296,8297],{"class":522},"\"order-42\"",[108,8299,649],{"class":132},[108,8301,8302],{"class":110,"line":7200},[108,8303,162],{"class":132},[7788,8305,8306],{"v-slot:hints":104},[285,8307,8308,8317,8320],{},[288,8309,8310,8311,8314,8315],{},"Добавь поле ",[86,8312,8313],{},"notifier Notifier"," в ",[86,8316,7312],{},[288,8318,8319],{},"В конструкторе сохрани зависимость в структуру",[288,8321,8322,8323,8325,8326],{},"В ",[86,8324,8024],{}," вызови ",[86,8327,8328],{},"s.notifier.Send(orderID)",[8330,8331,8332],"style",{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}",{"title":104,"searchDepth":118,"depth":118,"links":8334},[8335,8337,8343,8351,8357,8362,8370,8376,8383,8390,8396,8398,8400],{"id":279,"depth":118,"text":8336},"Что отвечать на собесе: Go поддерживает ООП-концепции, но реализует их иначе. Вместо иерархий наследования — композиция и маленькие интерфейсы. Это by design, не ограничение.",{"id":312,"depth":118,"text":313,"children":8338},[8339,8340,8341,8342],{"id":317,"depth":136,"text":318},{"id":442,"depth":136,"text":443},{"id":539,"depth":136,"text":540},{"id":546,"depth":136,"text":547},{"id":593,"depth":118,"text":97,"children":8344},[8345,8346,8347,8348,8349,8350],{"id":596,"depth":136,"text":597},{"id":899,"depth":136,"text":900},{"id":1058,"depth":136,"text":1059},{"id":1155,"depth":136,"text":1156},{"id":1197,"depth":136,"text":1198},{"id":1349,"depth":136,"text":1350},{"id":1428,"depth":118,"text":1429,"children":8352},[8353,8354,8355,8356],{"id":1432,"depth":136,"text":1433},{"id":1668,"depth":136,"text":1669},{"id":1897,"depth":136,"text":1898},{"id":1975,"depth":136,"text":1976},{"id":2009,"depth":118,"text":2010,"children":8358},[8359,8360,8361],{"id":2013,"depth":136,"text":2014},{"id":2181,"depth":136,"text":2182},{"id":2317,"depth":136,"text":2000},{"id":2646,"depth":118,"text":81,"children":8363},[8364,8365,8366,8367,8368,8369],{"id":2649,"depth":136,"text":318},{"id":2670,"depth":136,"text":2671},{"id":2797,"depth":136,"text":2798},{"id":2896,"depth":136,"text":2897},{"id":3171,"depth":136,"text":3172},{"id":3239,"depth":136,"text":3240},{"id":3366,"depth":118,"text":3367,"children":8371},[8372,8373,8374,8375],{"id":3370,"depth":136,"text":3371},{"id":3602,"depth":136,"text":3603},{"id":3715,"depth":136,"text":3716},{"id":3855,"depth":136,"text":3357},{"id":3978,"depth":118,"text":3979,"children":8377},[8378,8379,8380,8381,8382],{"id":3982,"depth":136,"text":3983},{"id":4183,"depth":136,"text":4184},{"id":4300,"depth":136,"text":4301},{"id":4358,"depth":136,"text":4359},{"id":4610,"depth":136,"text":4611},{"id":4669,"depth":118,"text":4670,"children":8384},[8385,8386,8387,8388,8389],{"id":4673,"depth":136,"text":4674},{"id":4732,"depth":136,"text":4733},{"id":4874,"depth":136,"text":4875},{"id":5044,"depth":136,"text":5045},{"id":5188,"depth":136,"text":5189},{"id":5271,"depth":118,"text":5272,"children":8391},[8392,8393,8394,8395],{"id":5275,"depth":136,"text":5276},{"id":5450,"depth":136,"text":5451},{"id":5624,"depth":136,"text":5625},{"id":5862,"depth":136,"text":5863},{"id":6863,"depth":118,"text":8397},"Правило: если функция работает с интерфейсом, подставь любую реализацию — программа должна остаться корректной.",{"id":7237,"depth":118,"text":8399},"Стандартная библиотека Go — образец: io.Reader (1 метод), io.Writer (1 метод), io.ReadWriter (композиция).",{"id":7776,"depth":118,"text":7777},1781458320548]