Шаг 87.
Язык программирования Go.
Типы с проверкой
На этом шаге рассмотрим типы с проверкой в Go.
Для многих простых пользовательских типов проверка просто не нужна. Например,
в программе может иметься тип Point { X, Y int } ,
для которого любые значения полей X и Y будут допустимыми.
Кроме того, поскольку компилятор Go гарантирует инициализацию всех переменных (включая поля структур) соответствующими нулевыми значениями,
это существенно снижает потребность в явных конструкторах.
В ситуациях, когда нулевые значения не подходят для инициализации, можно создать функцию-конструктор. Go не поддерживает неявного вызова конструкторов,
поэтому функция-конструктор должна вызываться явно.
Для этого следует описать тип как не допускающий инициализацию нулевым значением и реализовать одну или более функций-конструкторов для создания допустимых значений.
Аналогичный подход можно использовать для организации проверки значений полей.
Такие поля можно сделать неэкспортируемыми (частными) и реализовать методы доступа к ним, в которых выполнять необходимые проверки1.
Задание 1. Вывести широту и долготу Кургана и Москвы (рис.1):
Рис.1. Результат работы приложения
Раскрыть/скрыть решение и комментарии.
type Place struct
{
широта, долгота, высота_над_уровнем_моря float64
Название string
}
func New(широта, долгота, высота_над_уровнем_моря float64, название string) *Место {
return &Место{Проверка(0, широта), Проверка(0, долгота),
Проверка(0, высота_над_уровнем_моря), название}
}
func (место *Место) Широта() float64 { return место.широта }
func (место *Место) Установить_широту(широта float64) {
место.широта = Проверка(место.широта, широта)
}
func (место *Место) Долгота() float64 { return место.долгота }
func (место *Место) Установить_долготу(долгота float64) {
место.долгота = Проверка(место.долгота, долгота)
}
func (место *Место) Высота_над_уровнем_моря() float64 {
return место.высота_над_уровнем_моря
}
func (место *Место) Установить_высоту_над_уровнем_моря
(высота_над_уровнем_моря float64) {
место.высота_над_уровнем_моря =
Проверка(место.высота_над_уровнем_моря, высота_над_уровнем_моря)
}
func (место *Место) String() string {
return fmt.Sprintf("(%.3f°, %.3f°) %q\nВысота над уровнем моря: %.3f м",
место.широта, место.долгота, место.Название, место.высота_над_уровнем_моря)
}
func (оригинал *Место) Copy() *Место {
return &Место{оригинал.широта, оригинал.долгота,
оригинал.высота_над_уровнем_моря, оригинал.Название}
}
func Проверка(а, б float64) float64 {
if б >= а {
return б
} else {
return а
}
}
func main() {
курган := New(55.4500, 65.3333, 76, "Курган") //Курган–значение типа *Место
fmt.Println(курган)
москва := курган.Copy() // Москва – значение типа *Место
москва.Установить_широту(курган.Широта() + 0.30222)
москва.Установить_долготу(курган.Долгота() - 27.71774)
москва.Установить_высоту_над_уровнем_моря(курган.Высота_над_уровнем_моря()+68)
москва.Название = "Москва"
fmt.Println(москва)
}
Архив с примерами можно взять здесь.
Тип Место является экспортируемым (за пределы пакета, где он определен), но его поля широта, долгота и высота_над_уровнем_моря – нет, потому что они требуют проверки.
Здесь имеется функция-конструктор New(), гарантирующая создание допустимого значения типа *место.
В языке Go принято давать функциям-конструкторам имя New() или имена, начинающиеся с New, если таких функций несколько.
А наличие методов чтения и записи неэкспортируемых полей гарантирует, что им могут быть присвоены только допустимые значения.
Наличие метода String() означает, что значения типа *Место полностью отвечают требованиям интерфейса fmt.Stringer,
и обеспечивает вывод значений *Место в требуемом формате.
Здесь также реализован метод Copy(), но в нем не выполняются никакие проверки, потому что известно, что копируемое значение уже является допустимым.
1 В языке Go неэкспортируемыми (то есть видимыми только внутри пакета, где они объявлены) являются идентификаторы,
начинающиеся с буквы в нижнем регистре, а экспортируемыми (видимыми в любом пакете, импортирующем данный пакет)
являются идентификаторы, начинающиеся с буквы в верхнем регистре.
На следующем шаге рассмотрим интерфейсы в Go.
Предыдущий шаг
Содержание
Следующий шаг