kouzdra (kouzdra) wrote,
kouzdra
kouzdra

Categories:

goбъектное

В go, как я уже грил, оригинальных концепций мало - в основном это довольно удачная комбинация хорошо известных идей (хотя многие вещи я бы сделал по другому). Что довольно понятно, если учесть, что команда разработчиков восходит к авторам Unix (Кен Томпсон там во плоти, Ричи и прочие присутствую духовно - чрез язык Limbo*, от которого в некотором роде унаследован Go).

Одна из двух оригинальных (вторая - defer), в смысле мне ранее не встречавшихся, - это их подход к объектности.

Сама по себе идея, что VMT или ее аналог должна ходить вместе с указателем, а не лежать внутри объекта довольно очевидна, мне по крайней мере она пришла в голову почти сразу, когда в начале 90-х я впервые задумался на тему привнесения в ООП какой-то внятной семантики.

Проблема тут в реализации. И вот пожалуй в Go она решена c моей точки зрения правильно (есть шероховатости, но концепция в целом не вызывает ощущения кривизны). В чем задача: придумать сравнительно низкоуровневую модель, в которой с одной стороны наследование и прямое и множественное будут выразимы, причем в различных вариантах, и будут выразимы без извращений всем знакомым по С++. Второе - должен быть выдержан принцип - "платишь только за то, чем пользуешься" - то есть вспомогательные структуры типа vmt и косвенности не должны отягощать программу там, где они не нужны.

Вышло на удивление просто: в два приема:

Есть понятие структуры. В самом обычном смысле. В структуре кроме обычных полей могут быть "анонимные" - описываемые просто указанием типа, кой должен быть либо структурой, либо примитивным типом, либо указателем на оные. Анонимные поля получают имена своих типов, а их компоненты и методы могут быть доступны напрямую. Есть естественное требование уникальности имен анонимных полей внутри структуры:
import "fmt"
import . "math"

type Point2D struct {
	x, y float64
}

func (p Point2D) abs () float64 { return Sqrt(p.x*p.х + p.y*p.y) }

type Point3D struct {
	Point2D
	z float64
}

func (p Point3D) abs () float64 { return Sqrt(Pow(p.Point2D.abs(),2) + p.z*p.z) }

func main() {
	p := Point3D {Point2D {1, 2}, 3}
	p.x += 10
	fmt.Println(p.abs ())
	fmt.Println(p.Point2D.abs ())
}

(если убрать описание метода abs (Point3D) программа будет работать как и можно ожидать. Особо обращаю внимание, что это само по себе не объектность:

Методы - это не то, чтобы методы, это просто такие особенные функции.
И Point3D не является подтипом Point2D (хотя ничего не мешает авторам языка это ограничение снять - но это не нужно). Пока это просто синтаксис.

А теперь собственно то, что требуется:
type Point interface {
	abs () float64
}

func printAbs (p Point) {
	fmt.Println(p.abs());
}

func main() {
	p2 := Point2D {1, 2}
	p3 := Point3D {p2, 3}
	printAbs(p2)
	printAbs(p3)
}

Работает так, как и хочется. У интерфейсов, в отличие от структур, есть отношение тип-подтип просто по включению сигнатур. Как на этом выражаются все объектные извращения вплоть до виртуального множественного наследования, я думаю объяснять не надо.

Особенно отмечу, что тут я с указателями не игрался, но они в этой конструкции являются довольно существенной деталью. Например "виртуальное наследование" в стиле С++ подразумевает как раз анонимное поле-указатель.

Поскольку прописывать VMT в структуру не надо, в качестве конструктора может выступать обычная функция (единственная реально важная функция конструктора - именно инициализация VMT), а логику инициализации можно задать ту, которая нужна в данном случае, а не ту, которую навязывает компилятор.

Простенько и со вкусом. И без всяких мистических сущностей и куч ad-hoc правил.

PS: Поскольку я подозреваю, что народ возбудится - инкапсуляция делается на уровне межмодульных ограничений видимости. Как в Turbo Pascal.
*) Я перечитал описание Limbo и понял, что мое давнее впечатление от него было верным - громоздко и малоинтересно. Отличия вроде бы мелкие, но результат совершенно не впечатляет.
Tags: go, goбьектное, Компутерщина, Языки программирования
Subscribe
  • Post a new comment

    Error

    default userpic

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 38 comments