Search
Write a publication
Pull to refresh
-1
0.1
Дмитрий @qrKot

Энтузиаст

Send message

Ребят, ну вы чо. Просто невалидная жсонка же:

Data/consumer/rank - тут вложенная структура с полем sub.

А дальше вот эта штука {"code":"C"} вообще родительского узла не имеет. Т.е. rank должен либо массивом быть, либо структурой с полями sub и code

Я углубился в суть. Суть вашей проблемы в 2 словах: "Go не Python". Это если коротко.

Решение еще проще: не пишите на Go как на Python'е.

Вы рисуете какие-то странные примеры с GetPersonListDTO, на которых демонстрируете билд запроса к БД и сериализацию/десериализацию в JSON.

Сразу возникает вопрос, почему сигнатура метода репозитория выглядит вот так:
func (r *Repository) GetPersonList(ctx context.Context, dto GetPersonListDTO) []Person

Почему не так вот, например:

type PersonRepository interface {
  GetPersonListByName(ctx context.Context, name string) ([]Person, error)
  GetPersonListByNameAndPosition(ctx context.Context, name string, position string) ([]Person, error)
}

Зачем эти приседания с мутными dto'шками? Не, в Java/C#/Python понятно, зачем оно. Там ООП и всякие LINQ/EntityFramework'и/Streams и т.д. В Go всего этого нет и, вероятно, не будет в обозримом будущем. Зачем "городить огород"?

Еще более непонятно, зачем типу, описывающему запрос к БД уметь сериализоваться/десериализоваться в JSON. Тем более криво.

Вот у вас только что OptionalValue[int]{value: 0, defined: true, valid: true}, а вот мы его замаршаллили/анмаршаллили, и оно уже OptionalValue[int]{value:0, defined: false, valid: false}. Ну и зачем городили-то?

Если вы утверждаете, что ваш тип сериализацию умеет, то свою семантику при сериализации/десериализации он терять не должен.

Теперь по поводу defined/valid полей. У вас в каких случаях nil-value defined? Только если вы его сами ручками задали - все верно же? А valid оно когда? Правильно, в том же случае. А теперь объясните мне, в каком случае у вас поле определено, но не валидно, а в каких валидно, но не определено.

Короче, заранее извиняюсь, но решение - дичь, порожденная ООП головного мозга.

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

const Defined int8 = 1

В таких конструкциях лучше таки беззнаковые типы использовать.

  1. int8 заполненный единицами в бинарном представлении - это -128 (выглядит странно, да?), а uint8 с тем же содержимым - 255.

  2. С uint'ом открывается путь к грязным фокусам со сравнением. Например `mark > 7` вернет true, если есть хоть 1 взведенный флаг кроме первых трех. С int8 сравнение сфейлится, если будет взведен 8-й.

Mark += Valid

Вот тут однозначно Mark = Mark|Valid.

mark := 4 [00000100]
fmt.Println(mark + 5) // 9 [00001001]
fmt.Println(mark|5)   // 5 [00000101]

Вы вместо того, чтобы флаг взвести, суммируете числа. Чревато побочками, если в исходном значении не 0.

type bitmap uint8
const (
  defined bitmap = 1 << iota // 1 [00000001]
  valid                      // 2 [00000010]
  accepted                   // 4 [00000100]
  // дальше само инкрементить поразрядно будет
)

var mark = 5 // [00000101] - взведены 1 и 3 флаги
// Простые сравнения на наличие одного флага.
// В этом кейсе следующие 3 строки семантически идентичны
fmt.Println( mark & defined != 0 ) // true
fmt.Println( mark & defined > 0 ) // true
fmt.Println( mark & defined == defined ) // true
// И эти 3 сравнивают одно и то же
fmt.Println( mark & valid != 0 ) // false
fmt.Println( mark & valid > 0 ) // false
fmt.Println( mark & valid == valid ) // false

// Гораздо интереснее сравнение с несколькими флагами
// Проверяем, что все указанные флаги взведены - `mark & flags == flags`
fmt.Println(mark & (defined|valid) == defined|valid) // true, т.к. ОБА флага взведены
fmt.Println(mark & (defined|accepted) == defined|accepted) // false, т.к. взведен только ОДИН флаг.

отовые решения - знал бы я о них раньше - не писал бы свою либу

И эти люди...

"ну либо вы мне сейчас расскажете, зачем инициализировать переменную невалидным значением" - ощущение, что Вы не пишете продакшн код

Ну т.е. не расскажете...

Лучше бы предложили свое решение проблемы касаемо запросов в БД

sqlc

Или как Вы узнаете, поле было заполнено nil-ом или нет?

Зачем?

Ощущение, что Вы на go пишете просто посредственные вещи без каких-то паттернов, DRY, SOLID и прочее

Ну вот про SOLID: буковка S говорит о том, что в реализации паттерна Optional одно из полей valid/defined - лишнее.

type Optional[T any] struct {
	Value T
	IsSet bool
}

Все, готовый Optional. Всяческие OptionalBool и OptionalInt - не нужны, тем более в том виде, который вы использовали.

OptionalInt = OptionalType[*int]

Вот это вы зачем написали? Зачем тут указатель на int? Из принципа "что-то у меня оперативки многовато" или "а давайте посмотрим, насколько говенный у нас сборщик мусора"?

Просто вопрос: сколько памяти и где занимает ваш OptionalInt?

type Person struct { 
   Name string       
   Position *string   
}

position1 :=  "Junior"
person1 := Pesron{Position: "Junior"} // ТУТ НЕ СКОМПИЛИРУЕТСЯ
position2 :=  ""
person2 := Pesron{Position: ""}  // И ТУТ ТОЖЕ
person3 := Pesron{}

Ну блин, ну код-то свой запустить можно попробовать разок?

Ну и "библиотеку" посмотрел:

OptionalInt   = OptionalType[*int]
OptionalBool = OptionalType[*bool]

Кхм... bool-значение, занимающее 16 байт в стеке и еще 8 в куче. А может, ну его, этот ваш паттерн Optional (ну либо уже просто возьмите существующие реализации, и не парьте мозг). Ну и да, раздельные поля defined/valid нафиг не нужны (ну либо вы мне сейчас расскажете, зачем инициализировать переменную невалидным значением).

Да блин куда уж точнее: "проектов по управлению балансами банковских счетов"

Управление балансами (читай, изменение циферки на счету) банковских счетов.

Ну ок, сократим формулировка: банковские транзакции.

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

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

Не суть есть важно, БД это, диск, лог, очередь - вообще по барабану, это копия, с которой надо так или иначе синхронизироваться. Вы можете брать значение из памяти и отливать в хранилище или спрашивать из хранилища значение и класть в память - вопрос только в том, кто "источник истины". Суть от этого не изменится, для работающего приложения значение в хранилище будет внешним.

Причем, тут уже без разницы, ФП/императивный подход. Надеяться, что ФП магическим образом само как-то решит за вас проблему консистентности данных - какая-то наивность. В случае любой программы есть данные в памяти, и данные за ее пределами.

Не пользуйтесь, разрешаю

Я, откровенно говоря, C++-style ООП тоже не пользуюсь, процедурным языком кормлюсь

Ну т.е. половина в своих суждениях опирается на определение ООП и какую-то литературу по ООП-парадигме, а вторая - открывает любой текст, но видит перед собой ФП-методичку.

То, что ФП в качестве побочного эффекта дает потокобезопасность, совершенно не значит, что ООП должно давать те же либо сходные гарантии. Список гарантий ООП нужно искать в литературе, посвященной ООП, а не в ФП-откровениях.

Сначала появилось ФП, потом процедурная парадигма (плавно переходящая в структурную), потом ООП, и только потом - многопоточные системы. Вопрос о том, что делать с многопоточностью младше любой из этих трех парадигм. И они дали разные ответы на этот вопрос

ФП: у нас нет разделяемого состояния, можете не заботиться о потокобезопасности (пока не понадобится shared state/shared resourse, но с ними из без потокобезопасности больно было).

ООП: вот вам инструмент, вы его знаете, он с вами давно, называется "инкапсуляция". А вот новые, называются "примитивы синхронизации". Защитите себя сами, вы на это и программисты.

С хрена ли тут какие-то гарантии дополнительные у ООП появиться должны были - решительно непонятно.

Вот это ваша цитата, ответ на которую вы сейчас цитируете.

Но ООП и не помогает с инкапсуляцией в смысле потокобезопасности. Скорее, мешает.

Теперь таки "конечно, помогает".

Вы там определитесь уже, что ли. Так помогает или мешает? Люди волнуются!

из определения

Покажите мне уже это определение, на которое вы ссылаетесь. Я ни одного подходящего не видел.

цель инкапсуляции - защита инвариантов

Нету у нее такой цели.

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

Я вам сейчас страшное скажу: в определении ФП тоже ничего про потокобезопасность нет. Оно вообще не для того придумывалось, и не те проблемы решало. Потокобезопасность - это следствие из ограничений, в которых существует функциональное программирование (вы не можете сломать разделяемое состояние, если у вас его нет).

И вы, ссылаясь на следствие из парадигмы ФП, утверждаете, что оно же должно быть заложено в определении ООП? Вы в своем уме вообще?

любая с конечной согласованностью подойдет

Буду краток: нет.

и событийная туда же

Еще раз: нет.

банки" бывают с разными требованиями

И еще раз: нет.

Вы просто, опираясь на то, что ФП дает гарантии защиты в многопоточной среде (ввиду отсутствия разделяемого состояния), считаете, что ООП или процедурная парадигма должны давать таковые же.

Ну вот не должны. Потому что ООП - парадигма, построенная на наличии разделяемого состояния, со своими ограничениями и областью применения. Вся фишка в том, что в 90% случаев именно ФПшные гарантии вообще не нужны, а ограничения накладывают.

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

Код на 90% синхронный, и да, в современном серверном применении - тоже. Многопоточные гарантии "искаропки" хорошо, когда вычислительно-сложные задачи выполняешь. Их, как правило, в процессинге пользовательских запросов нет.

Вот и получается, что ФП-парадигма - это, может быть, и хорошо, но в подавляющей массе решений не нужно.

И как вы собираетесь, не используя инкапсуляцию, защитить поле? synchronized? Ну так:

  1. Работает либо с методами, либо с блоками кода - т.е. поверх инкапсуляции.

  2. Собственно по сути является инкапсуляцией.

Как без инкапсуляции защищать собираетесь? Обертывать в synchronized каждое обращение к полю ручками? Ну, удачи, чо...

А мешает, простите, как?

Вот есть поле, поле вы никак защитить не можете. Плохо.

Вот есть приватное поле, а также геттер и сеттер. Пока не понятно, зачем. Но, на самом деле, стало лучше, сейчас объясню, чем.

Теперь мы в геттер и сеттер вкорячили мьютекс. Теперь видно, чем лучше стало. А не было бы геттера/сеттера - было бы все еще плохо.

Т.е., получается, помогает? Ну, исходя из элементарной логики!

Инкапсуляция должна включать гарантии потокобезопасности

Кому должна?)))

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

Ну вы же сами в пример привели класс BankAccount! И, внезапно, проектов по управлению балансами банковских счетов без базы с транзакциями - действительно не бывает.

Был бы другой пример, другой бы разговор был. А для банковских счетов есть 2 неотъемлемых требования:

  1. Транзакционность

  2. Последовательность операций.

Требование параллельно 2 операции по одному счету выполнять - ересь, высосанная из пальца. На банковские транзакции ФП ложится отвратительно, не надо его тащить. А ООП вы не понимаете, от слова "вообще"

1
23 ...

Information

Rating
3,693-rd
Location
Бийск, Алтайский край, Россия
Date of birth
Registered
Activity