Pull to refresh
10
0
Алексей Орлов @lelikPtz

User

Send message

Можно ли SOLID использовать не правильно? да. Это значит надо учиться правильно применять, вот и всё, отсюда и огромное количество статей, так как это не очень просто.

Не умение их применять очень удобно списывать на "солид переоценён", "солид не догма", "именно в моей задаче он не нужен" и тд

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

Если есть опыт написания тестов и хорошо владеть tdd, то задачи решаются быстрее, чем без них. Сильно экономит время на отладке кода и сразу подсвечивает если что-то сломали "по пути", а не на этапе ручного тестирования/проде.

простота map + RWMutex может быть предпочтительнее

Почему простота? надо правильно написать отдельную обёртку, использовать mutex, понимать разницу RWMutex/Mutex, а sync.Map только тип кастануть при получении.

Выглядит будто sync.Map удобней использовать по дефолту даже при небольшом количестве горутин и операций: проще написать, меньше шансов ошибиться, потокобезопасно по умолчанию.

Option pattern удобно и более правильно использовать для установки параметров структуры отличных от дефолтных по умолчанию, например так:

func NewScannedItem(opts ...ScanFunc) *ScannedItem {
	item := &ScannedItem{}
	for _, fn := range opts {
		fn(item)
	}

	return item
}

func WithGS1(value bool) ScanFunc {
	return func(item *ScannedItem) {
		item.GS1 = value
	}
}

func main() {
	NewScannedItem(WithGS1(true))
}

В вашем случае, если в методе Scan надо научиться запускать кастомные валидаторы, то можно в структуру добавить validators []ScanFunc, задавать их список в конструкторе с так же с помощью opts, а в методе Scan вызывать

В том и проблема что можно написать любую функцию, которая будет менять состояние ScannedItem, и будет очень сложно контролировать все возможные варианты, например так:

func (item *ScannedItem) ChangeGS1(value bool) ScanFunc {
	return func(item *ScannedItem) {
		item.GS1 = value
	}
}

и тогда в зависимости от порядка опций в вызове ScannedItem айтем с одними и теме же Datamatrix может быть и валидным и не валидным

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

Если каждый класс реализует интерфейс, а использующие его классы зависят от него, а не от реализации, то:

1) Легче писать тесты замокав эти интерфейсы.
2) По мере развития кода очень просто на основе контракта, описанного в интерфейсе, подменить реализацию на новую или использовать несколько разных реализаций.

Как и везде есть исключения, но как мне кажется доказывать надо именно их необходимость в конкретных случаях, а в общем случае лучше интерфейс

Не нарушает, в пункте речь про конфигурацию di, а не код, видимо не очень чётко сформулировал мысль

Очень часто в процессе развития проектов встречал ситуации, когда разработчики докидывают аргументы в существующие реализации, расширяя поведение, при этом не рассматривая альтернативы, так как этот вариант самый дешёвый и простой в конкретный момент времени. А вот с "фанатами правил из книжек" на практике не посчастливилось столкнуться:(

Предложенное ограничение заставляет задуматься об альтернативах. И само собой бывают исключения.

Не хотелось очень подробно расписывать обоснования каждого пункта, так как отдельно это обсуждалось много раз и решил попробовать изложить как можно короче, видимо не очень получилось, наброс принят)

Information

Rating
Does not participate
Location
Белград, Белград, Сербия
Date of birth
Registered
Activity

Specialization

Backend Developer
Senior
From 250,000 ₽
PHP
OOP
PostgreSQL
Nginx