У вас очень логичный вопрос на счет сеттера: сеттер полностью уничтожает задумку с инкапсуляцией. Правильно это называется "семантическое нарушение инкапсуляции" — то есть как-бы мы инкапсулировали, но по факту назад открыли прямую запись в свойство напрямую. Все доводы "ну мы можем в сеттере валидировать" не корректны
Правильным способом будет не использовать сеттеры. Вот моя статья на эту тему:
https://habr.com/ru/post/469323/
Более подробно: каждый класс нужно проектирвовать так, чтобы данные внутри были максимально связаны. Например каждый метод работать должен в хорошем случае с максимальным числом полей класса, тогда у него высокий cohesion... В тоже время снаружи наужно работать с максимальн омалым числом метода, тогда будет низкая связанность (coupling). Это пара принципов из GRASP.
Когда вы делаете сеттеры и геттеры, то у вас данные внутри между собой почти никак не взаимодействут: с геттером работают снаружи, с сеттером работаю снаружи — весь класс нараспашку, а в нем в 100% случаев появляются данные, которые вместе не должны находиться и никак не связаны — анрушена и абстракция и инвариант и много чего еще...
Про инвариант отдельно: например есть платеж, у него есть значение, с которым платеж инициирвоан (initValue), есть значение холда (holdAmount) и есть значение чарджа на списание (chargeAmount)
Когда вы работаете с платежом, контролируя ивнариант в самом классе, то ваш каждый метод првоеряет др значения и позволяет перейти к др состоянию... методов будет 2-3, все инкапсулировано и безопасно.
Например:
class Payment {
pub func charge(amount int) void {
if (this.holdAmount < amount && this.initAmount < amount) {
throw new PaymentException('Unavailable charge amount')
}
if (this.status === PaymentStatus:finish) {
throw new PaymentException('Payment already fisnished')
}
this.chargeAmount = amount
this.holdAmount -= amount
this.status = PaymentStatus:finish
}
}
Тут в одном методе полные проверки и класс сам контролирует все состояние внутри, также соблюдается закон Деметры. У даннго кода высокий cohesion (из GRASP), тк внутри идет плотная работа с внутр данными (значит они корректно тут закроекны) и низкий coupling ( с теми данными для данного кейса только один метод работы, все внутри)
Когда вы раскроете сеттерами и геттерами, то ваше состояние становится непредсказуемо и полагается только на то, что снаружи точно подумали об инварианте (нет)