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