Go: ООП

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go: ООП

Непрочитанное сообщение Olej » 23 фев 2024, 07:10

Язык Go объектно-ориентирован (хотя авторы и избегают термина называть его именно так) не в меньшей степени, чем более привычные C++ или Java, только объектность здесь совсем другой природы.
Я изобрел понятие «объектно-ориентированный»,
и могу заявить, что не имел в виду C++.
© Alan Kay

Let's Go: объектно-ориентированное программирование на Голанге
Gigi Sayfan
Aug 19, 2016 • 6 min read
Go - это странная смесь старых и новых идей. У него очень освежающий подход, при котором он не боится отбросить устоявшиеся представления о том, «как делать вещи». Многие люди даже не уверены, является ли Go объектно-ориентированным языком. Позвольте мне сказать это прямо сейчас. Является!
Объектно-ориентированное программирование в Golang
Андрей Шагин
2022
Давайте поучимся работать с объектно-ориентированной архитектурой в Golang. Здесь нет классов, зато есть структуры, работа с которыми является единственным способом поддержки объектно-ориентированной модели.
Документация GoLang:
Объектно-ориентированное программирование, по крайней мере в известных языках, включает в себя слишком много разговоров на тему взаимоотношений между типами, взаимоотношений, которые часто могут быть выведены автоматически. Go использует другой подход.
Вместо того, чтобы требовать от программиста объявлять заранее, что два типа связаны, в Go тип автоматически удовлетворяет любому интерфейсу, который специфицирует подмножество его методов. Помимо простого рутинного учёта, такой подход имеет реальные преимущества. Типы могут удовлетворить многим интерфейсам одновременно, без сложностей традиционного множественного наследования. Интерфейсы могут быть очень легковесными — интерфейсом с одним единственным методом, или даже вообще без методов, можно выразить весьма полезные концепции. Интерфейсы могут быть добавлены уже после того, когда новая идея приходит после, или во время тестирования, без существенного изменения оригинальных типов. Потому что нет отчётливой связи между типами и интерфейсами, нет выраженной иерархии типов, которой нужно управлять или которую обсуждать.
Это подобно использованию этих идей для создания чего-то аналогичного типабезопасных UNIX pipes. Например, посмотрите как fmt.Fprintf() позволяет форматировать печать на любой вывод, а не просто в файл, или как пакет bufio может быть полностью отделен от файла в вводе-выводе, или как пакеты обработки изображений позволяют генерировать сжатые файлы изображений. Все эти возможности проистекают из одного интерфейса (io.Writer), представляющего единственный метод (Write()). И это только самые поверхностные примеры. Интерфейсы Go имеют самое глубокое влияние на то, как структурированы программы.
Это требует некоторого привыкания, но этот неявный стиль зависимостей типов является одним из наиболее продуктивных сторон Go.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go: ООП

Непрочитанное сообщение Olej » 23 фев 2024, 07:20

Olej писал(а):
23 фев 2024, 07:10
Язык Go объектно-ориентирован
И никому не верьте, если вам кто-то говорит обратное... :lol:
Вот приложение ... сравнение языков программирования ... которое я писал в феврале 2014 года:
Olej писал(а):
14 фев 2014, 22:07
Сообщение Olej » 14 фев 2014, 22:07
...
А теперь, дети, я расскажу вам волшебную сказку ...
... и которое потом кочевало с места на место ... вплоть до вот этой книги: Книга: "Linux: многопроцессорная эффективность. Выбираем Go"
Изображение
Куда уж более ООП :?: :!: :lol:
P.S. Это, получается, на сегодня 10 лет исполнилось :?: :-o

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go: ООП

Непрочитанное сообщение Olej » 23 фев 2024, 07:33

Olej писал(а):
23 фев 2024, 07:20
Куда уж более ООП

Код: Выделить всё

package main

import (
	"errors"
	"fmt"
	"io"
	"math"
	"math/cmplx"
	"os"
	"strconv"
	"strings"
)

// -------------- класс точки вершины ---------------------
type point struct {
	xy complex128
}

func (p *point) String() string { // формат вывода
	return fmt.Sprintf("[%.2f,%.2f] ", real(p.xy), imag(p.xy))
}
func (p *point) inpoint() (ok bool, err error) { // ввод координат
	buf := make([]byte, 1024)
	ok = false
	n, err := os.Stdin.Read(buf)
	if err == io.EOF || n == 0 || buf[n-1] != '\n' { // конец ввода
		err = io.EOF
		return
	}
	as := strings.Split(string(buf[:n-1]), string(" "))
	if len(as) != 2 {
		err = errors.New("число параметров")
		return
	}
	x, err := strconv.ParseFloat(as[0], 64)
	if err != nil {
		return
	} // ошибка преобразования
	y, err := strconv.ParseFloat(as[1], 64)
	if err != nil {
		return
	} // ошибка преобразования
	p.xy = complex(x, y)
	ok, err = true, nil
	return
}

// ------------- класс многоугольника ---------------------
type shape []point

func (p *shape) append(data point) {
	slice := *p
	l := len(slice)
	if l+1 > cap(slice) { // недостаточно места
		newSlice := make([]point, (l+1)*2) // выделение вдвое большего буфера
		if l > 0 {                         // скопировать данные
			for i, c := range slice {
				newSlice[i] = c
			}
		}
		slice = newSlice
	}
	slice = slice[0 : l+1]
	slice[l] = data
	*p = slice
}
func (p *shape) String() string { // формат вывода
	slice := *p
	var s string = ""
	for _, c := range slice {
		s += c.String()
	}
	return s
}
func (p *shape) perimeter() float64 {
	summa := 0.0
	slice := *p
	for i, c := range slice {
		if i == 0 {
			summa += cmplx.Abs(c.xy - slice[len(slice)-1].xy)
		} else {
			summa += cmplx.Abs(c.xy - slice[i-1].xy)
		}
	}
	return summa
}
func (p *shape) square() float64 {
	summa := 0.0
	slice := *p
	for i := 0; i < len(slice)-2; i++ {
		r1, θ1 := cmplx.Polar(slice[i+1].xy - slice[0].xy)
		r2, θ2 := cmplx.Polar(slice[i+2].xy - slice[0].xy)
		summa += r1 * r2 * math.Abs(math.Sin(θ2-θ1)) / 2.
	}
	return summa
}

func main() {
	for {
		fmt.Println("координаты вершин в формате: X Y (^D конец ввода)")
		многоугольник := new(shape)
		i := 0
		точка := new(point)
		for {
			fmt.Printf("вершина № %v: ", i+1)
			ok, err := точка.inpoint() // ввод координат вершины
			if !ok {
				if err == io.EOF {
					fmt.Printf("\r")
					break
				} // конец ввода вершин
				fmt.Printf("ошибка ввода: %s!\n", err)
				continue
			}
			многоугольник.append(*точка)
			i++
		}
		fmt.Printf("вершин %d : %v\n", len(*многоугольник), многоугольник)
		fmt.Printf("периметр = %.2f\n", многоугольник.perimeter())
		fmt.Printf("площадь = %.2f\n", многоугольник.square())
		fmt.Println("---------------------------------")
	}
}
Сборка:

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/oop/triangle$ go build triangle.go 

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/oop/triangle$ ls -l triangle 
-rwxrwxr-x 1 olej olej 1975900 фев 23 06:31 triangle
Вложения
triangle.go
(3.13 КБ) 5 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go: ООП

Непрочитанное сообщение Olej » 23 фев 2024, 07:42

Olej писал(а):
23 фев 2024, 07:33
Сборка:
Выполнение:

Код: Выделить всё

[Olej@modules triangle]$ ./triangle
координаты вершин в формате: X Y
вершина № 1: 1. 1.
вершина № 2: 1. 2.
вершина № 3: 2. 1.
вершин 3 : [1.00,1.00] [1.00,2.00] [2.00,1.00]
периметр = 3.41
площадь = 0.50
---------------------------------
координаты вершин в формате: X Y
вершина № 1: 1. 1.
вершина № 2: 1. 2.
вершина № 3: 2. 2.
вершина № 4: 2. 1.
вершин 4 : [1.00,1.00] [1.00,2.00] [2.00,2.00] [2.00,1.00]
периметр = 4.00
площадь = 1.00
---------------------------------
координаты вершин в формате: X Y
вершина № 1: 3.3
ошибка ввода: число параметров!
вершина № 1: 1. 2. 3. 4.
ошибка ввода: число параметров!
вершина № 1: 2.2 4.r
ошибка ввода: strconv.ParseFloat: parsing "4.r": invalid syntax!
вершина № 1: k 5
ошибка ввода: strconv.ParseFloat: parsing "k": invalid syntax!
вершина № 1: ^C
Это вот так мы считаем площадь + периметр - любого выпуклого многоугольника :!:

Код: Выделить всё

olej@R420:~/2024/own.BOOKs/BHV.Go.3/examples.work/oop/triangle$ ./triangle
координаты вершин в формате: X Y (^D конец ввода)
вершина № 1: 2 0
вершина № 2: 1 -2
вершина № 3: 0 -3
вершина № 4: -1 -2
вершина № 5: -2 0
вершина № 6: -1 2
вершина № 7: 0 3
вершина № 8: 1 2
вершин 8 : [2.00,0.00] [1.00,-2.00] [0.00,-3.00] [-1.00,-2.00] [-2.00,0.00] [-1.00,2.00] [0.00,3.00] [1.00,2.00]
периметр = 14.60
площадь = 14.00
---------------------------------
координаты вершин в формате: X Y (^D конец ввода)

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go: ООП

Непрочитанное сообщение Olej » 23 фев 2024, 07:59

Olej писал(а):
23 фев 2024, 07:20
Это, получается, на сегодня 10 лет исполнилось
Но то дела давние ... А сейчас о другом:

Одна из самых интересных тем из области объектно-ориентированного программирования — активные объекты. Особенно активно эти темы пошли в обсуждение с языка программирования Oberon Никлауса Вирта. Активные объекты — это такие объекты, которые после своего создания содержат в себе постоянно действующую активность (поток POSIX pthread_t, горутину Go … всё что угодно), активность объекта всё время его жизни не прекращается, выполняет какую-то полезную работу. А взаимодействие с внешним окружением (программным кодом) активный объект осуществляет через какие-то механизмы взаимодействия и синхронизации параллельных ветвей.

В качестве примера, чтобы не усложнять, рассмотрим реализацию в технике активного объекта логер некоего проекта:
• Код проекта, в различных его точках, может генерировать сообщения логирования разных классов грубости…
• Физическое логирование (после формирования самого сообщения) может быть достаточно продолжительным … это может быть, например, отправка сообщения через Интернет с помощью электронной почты … а то ещё и с ожиданием подтверждения доставки, если это критическая область применения.
• Основной код приложения после отправки сообщения лога не должен беспокоиться о его дальнейшей судьбе, и не должен нисколько задерживаться после отправки сообщения.
:lol:
Olej писал(а):
23 фев 2024, 07:20
вплоть до вот этой книги:
Это из книги той же ... только из 3-го издания ... ещё только готовящегося :lol:

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go: ООП

Непрочитанное сообщение Olej » 23 фев 2024, 08:02

Olej писал(а):
23 фев 2024, 07:59
рассмотрим реализацию в технике активного объекта логер некоего проекта

Код: Выделить всё

package main

import (
	"fmt"
	"sync"
	"time"
)

type LogSeverityType string

const (
	Info    LogSeverityType = "Info"
	Warning LogSeverityType = "Warning"
	Error   LogSeverityType = "Error"
	None    LogSeverityType = "None"
)

type LogMessage struct { // структура сообщений логера
	Severity LogSeverityType
	Message  string
}

type ActiveLogger struct { // данные Active Object
	logQueue  chan *LogMessage // канал логирования
	stopEvent chan struct{}    // канал завершения
	tmStart   time.Time        // время старта
	wg        sync.WaitGroup
}

func createActiveLogger() *ActiveLogger {
	return &ActiveLogger{
		logQueue:  make(chan *LogMessage, 10),
		stopEvent: make(chan struct{}),
		tmStart:   time.Now(),
	}
}

func (l *ActiveLogger) Log(message *LogMessage) {
	l.logQueue <- message
}

func (l *ActiveLogger) StartLogProcessor() {
	l.wg.Add(1)
	go func() { // loop Active Object
		defer l.wg.Done()
		for { // цикл обслуживания
			select {
			case message := <-l.logQueue:
				l.processMessage(message)
			case <-l.stopEvent:
				return
			}
		}
	}()
}

// фактическое логирование: тут может быть любая длинная работа...
func (l *ActiveLogger) processMessage(m *LogMessage) {
	out := func(m string) {
		dur := func() time.Duration {
			return time.Now().Sub(l.tmStart)
		}
		fmt.Printf("% 14v : %s", dur(), m)
	}
	l.wg.Add(1)
	go func() {
		defer l.wg.Done()
		out(fmt.Sprintf("логируется... [%s]:\t%s\n", m.Severity, m.Message))
		time.Sleep(500 * time.Millisecond)
		out(fmt.Sprintf("залогировано! [%s]:\t%s\n", m.Severity, m.Message))
	}()
}

func (l *ActiveLogger) StopLogProcessor() {
	message := &LogMessage{
		Message:  fmt.Sprintf("завершение логера"),
		Severity: None,
	}
	l.Log(message)     // сообщение завершения логера
	close(l.stopEvent) // закрыть канал завершения
	l.wg.Wait()        // ... и ожидать завершения рабочего цикла
}

func main() {
	logger := createActiveLogger() // создание Active Object
	logger.StartLogProcessor()     // запуск активности ...
	const N = 5
	for i := range N {
		var Severity LogSeverityType
		if i < N-1 {
			Severity = Info
		} else {
			Severity = Error
		}
		message := &LogMessage{
			Message:  fmt.Sprintf("сообщение номер %d", i),
			Severity: Severity,
		}
		logger.Log(message) // сообщение логеру ...
	}
	time.Sleep(2 * time.Second)
	logger.StopLogProcessor() // завершение логера
}
Базовая идея вот отсюда: Easy Concurrency: Active Object Pattern in Go Explained
Iede Snoek, , Nov 25, 2023
Но всё переделано дотла...
Вложения
loger.go
(2.62 КБ) 5 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go: ООП

Непрочитанное сообщение Olej » 23 фев 2024, 08:10

Olej писал(а):
23 фев 2024, 08:02
Но всё переделано дотла...
Вот как это работает:

Код: Выделить всё

olej@R420:~/2024/Go/ActObj/loger$ go run loger.go
     199.844µs : логируется... [Error]: сообщение номер 4
     226.752µs : логируется... [Info]:  сообщение номер 3
     236.534µs : логируется... [Info]:  сообщение номер 1
     170.442µs : логируется... [Info]:  сообщение номер 2
     331.345µs : логируется... [Info]:  сообщение номер 0
  500.956811ms : залогировано! [Info]:  сообщение номер 0
  501.002193ms : залогировано! [Info]:  сообщение номер 1
  501.027609ms : залогировано! [Error]: сообщение номер 4
  501.035735ms : залогировано! [Info]:  сообщение номер 2
  501.046721ms : залогировано! [Info]:  сообщение номер 3
  2.001640435s : логируется... [None]:  завершение логера
  2.502352703s : залогировано! [None]:  завершение логера

Код: Выделить всё

olej@R420:~/2024/Go/ActObj/loger$ go run loger.go
     100.011µs : логируется... [Error]: сообщение номер 4
      123.31µs : логируется... [Info]:  сообщение номер 3
     133.566µs : логируется... [Info]:  сообщение номер 0
     155.837µs : логируется... [Info]:  сообщение номер 2
     174.337µs : логируется... [Info]:  сообщение номер 1
  500.855252ms : залогировано! [Info]:  сообщение номер 1
  500.873195ms : залогировано! [Info]:  сообщение номер 2
   500.90007ms : залогировано! [Info]:  сообщение номер 0
  500.903716ms : залогировано! [Info]:  сообщение номер 3
  500.944625ms : залогировано! [Error]: сообщение номер 4
  2.001708059s : логируется... [None]:  завершение логера
  2.502475414s : залогировано! [None]:  завершение логера
Специально показаны два последовательных запуска, чтобы было видно, что последовательность выполнения параллельных ветвей случайная — это, обычно, не достаточный, но необходимый (грубый) признак того, что в параллельном коде всё действительно играет параллельно. (Если у вас соблюдается строгий детерминизм между запусками, значит с параллелизмом у вас что-то не так!)

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go: ООП

Непрочитанное сообщение Olej » 23 фев 2024, 08:11

Olej писал(а):
23 фев 2024, 07:59
А сейчас о другом:
Olej писал(а):
23 фев 2024, 08:02
всё переделано дотла...
Очень коротко о том как это работает:
• После создания объекта createActiveLogger() для него тут же выполняется вызов метода createActiveLogger(), по которому в объекте начинает выполняться горутина с бесконечным циклом ожидания взаимодействия (метод StartLogProcessor());
• Горутина ожидает взаимодействия на двух каналах:
• … по каналу stopEvent приходит сообщение на завершение работы — один и последний раз;
• … по каналу logQueue приходят регулярно сообщения для логирования;
• Само логирование достаточно продолжительное (метод processMessage()), представлено оно пассивной задержкой (в примере обработка лога требует 0.5 секунды);
• При логировании к сообщению добавляется префикс временной метки — временная задержка отсчитываемая от момента запуска активного объекта;
• Дополнительная синхронизация sync.WaitGroup требуется для того, чтобы запретить завершение задачи (и активности объекта) до того момента (возможно, в некотором будущем, до 0.5 секунд), когда полностью завершится фактическая отправка всех переданных до этого сообщений логов.
Все остальные детали хорошо видны из кода.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go: ООП

Непрочитанное сообщение Olej » 23 фев 2024, 08:36

Olej писал(а):
23 фев 2024, 08:11
Все остальные детали хорошо видны из кода.
Вот так вот ... хлопцы :lol:
Нужны хорошие идеи по ООП :!: :roll:
Давайте - у кого есть хорошие наглядные идеи :?:
Пишите... :arrow:
Ваши идеи - мои тут же в ответ реализации :idea:

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Go: ООП

Непрочитанное сообщение Olej » 23 фев 2024, 19:20

Olej писал(а):
23 фев 2024, 08:36
Нужны хорошие идеи по ООП
Встраивание и агрегирование ... Наследование типов.
В Go отсутствует наследование типов, а для похожей, но не идентичной, конструкции используется анонимное вложение типов, встраивание).
Вот как-то так :-D :

Код: Выделить всё

...
type ColoredPoint struct { 
   color.Color // Анонимное (безымянное) поле (встраивание) 
   x, y int      // Именованные поля (агрегирование) 
} 
Вот с агрегированием понятнее - это то что везде: Pascal, C, C++, Java...
С встраимванием посложнее будет - у этих полей у типа нет имени :!:
Все операции, допустимые для типа встраиваемого поля, автоматически допустимы непосредственно и для значений типа структуры, в которую встроено поле.
Вот так в Go производятся новые типы.

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 14 гостей