Войти в систему

Home
    - Создать дневник
    - Написать в дневник
       - Подробный режим

LJ.Rossia.org
    - Новости сайта
    - Общие настройки
    - Sitemap
    - Оплата
    - ljr-fif

Редактировать...
    - Настройки
    - Список друзей
    - Дневник
    - Картинки
    - Пароль
    - Вид дневника

Сообщества

Настроить S2

Помощь
    - Забыли пароль?
    - FAQ
    - Тех. поддержка



Пишет kouzdra ([info]kouzdra)
@ 2013-07-06 10:07:00

Previous Entry  Add to memories!  Tell a Friend!  Next Entry
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.y + 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 и понял, что мое давнее впечатление от него было верным - громоздко и малоинтересно. Отличия вроде бы мелкие, но результат совершенно не впечатляет.


(Читать комментарии)

Добавить комментарий:

Как:
Identity URL: 
имя пользователя:    
Вы должны предварительно войти в LiveJournal.com
 
E-mail для ответов: 
Вы сможете оставлять комментарии, даже если не введете e-mail.
Но вы не сможете получать уведомления об ответах на ваши комментарии!
Внимание: на указанный адрес будет выслано подтверждение.
Имя пользователя:
Пароль:
Тема:
HTML нельзя использовать в теме сообщения
Сообщение:



Обратите внимание! Этот пользователь включил опцию сохранения IP-адресов пишущих комментарии к его дневнику.