Информация об изменениях

Сообщение Re[2]: ООП не получается от 27.06.2023 15:17

Изменено 27.06.2023 15:18 Doom100500

Re[2]: ООП не получается
Здравствуйте, Буравчик, Вы писали:

Б>Ты хочешь иметь дефолтную реализацию, и переопределить ее в наследниках?


Да, но есть нюанс . Надо вызвть наследника из базы. Я перепишу стартовый промер, чтобы намерения стали понятнее:

package main

type Behavor interface {
    CheckCondition() bool //Это могло быть и приватным, но пусть клиентский код тоже сможет проверять условие
    DoAction()
}

type defaultBefavior struct{}

func (*defaultBefavior) CheckCondition() bool {
    panic("Not implemented!!!")
}

func (b *defaultBefavior) DoAction() {
    if !b.CheckCondition() { //Дёрнуть наследника
        return
    }
    // Do actual action
    // Общий код для всех наследников
}

type stupidBehavior struct {
    *defaultBefavior
}

func (b *stupidBehavior) CheckCondition() bool {
    //Проверяем внутреннее состояние - разное для всех наследников
    // и, в соответсвии с этим, возвращаем результат
    return true
}

func NewStupidBehavior() Behavor {
    return &stupidBehavior{
        defaultBefavior: &defaultBefavior{},
    }
}

func main() {
    st := NewStupidBehavior()
    st.DoAction() // panics
}


Это не работает, и даже понятно почему: нет никакого наследования и виртуальных таблиц, есть композиция.
И это нормально работает в объектно — ориентированных языках с виртуальными методами и настоящим наследованием.
То, что я хочу должно выглядеть (для go) примерно так:
func (b Befavior) DoAction() { // здесь сам интерфейс является ресивером
    if !b.CheckCondition() { //Дёрнуть наследника
        return
    }
    // Do actual action
    // Общий код для всех наследников
}

Но так в go нельзя. Но что-то похожее обещали в go2 (но это, как всегда, неточно):
https://go.googlesource.com/proposal/+/master/design/go2draft-contracts.md

Зато можно вызывать базу из наследников. Сейчас у меня работет такой код:
package main

type Behavor interface {
    CheckCondition() bool //Это могло быть и приватным, но пусть клиентский код тоже сможет проверять условие
    DoAction()
}

type defaultBefavior struct{}

func (b *defaultBefavior) doActualAction() {
    // Общий код для всех наследников
}

type stupidBehavior struct {
    *defaultBefavior
}

func (b *stupidBehavior) CheckCondition() bool {
    //Проверяем внутреннее состояние - разное для всех наследников
    // и, в соответцвии с этим, возвращаем результат
    return true
}

func (b *stupidBehavior) DoAction() {
    if b.CheckCondition() {
        b.doActualAction()
    }
}

func NewStupidBehavior() Behavor {
    return &stupidBehavior{
        defaultBefavior: &defaultBefavior{},
    }
}

func main() {
    st := NewStupidBehavior()
    st.DoAction()
}


Но это значит, что метод DoAction() должен копироваться без изменений во все наследники.
Сначала я смирился и запилил 3-х наследников. Но потом, когда месяца через два спал пилить четвёртого, мой внутренний перфекционист взбунтовался — мало ли что я забуду скопировать.

Возможно я что-то делаю не так, но я так и не смог придумать как избавиться от копирования. Всё ночь снились дженерики и даже генерация кода.
Сдаётся мне, что должен быть какой-то идиоматический путь избежать такой копипасты, но я его не вижу...
Re[2]: ООП не получается
Здравствуйте, Буравчик, Вы писали:

Б>Ты хочешь иметь дефолтную реализацию, и переопределить ее в наследниках?


Да, но есть нюанс . Надо вызвать наследника из базы. Я перепишу стартовый пример, чтобы намерения стали понятнее:

package main

type Behavor interface {
    CheckCondition() bool //Это могло быть и приватным, но пусть клиентский код тоже сможет проверять условие
    DoAction()
}

type defaultBefavior struct{}

func (*defaultBefavior) CheckCondition() bool {
    panic("Not implemented!!!")
}

func (b *defaultBefavior) DoAction() {
    if !b.CheckCondition() { //Дёрнуть наследника
        return
    }
    // Do actual action
    // Общий код для всех наследников
}

type stupidBehavior struct {
    *defaultBefavior
}

func (b *stupidBehavior) CheckCondition() bool {
    //Проверяем внутреннее состояние - разное для всех наследников
    // и, в соответсвии с этим, возвращаем результат
    return true
}

func NewStupidBehavior() Behavor {
    return &stupidBehavior{
        defaultBefavior: &defaultBefavior{},
    }
}

func main() {
    st := NewStupidBehavior()
    st.DoAction() // panics
}


Это не работает, и даже понятно почему: нет никакого наследования и виртуальных таблиц, есть композиция.
И это нормально работает в объектно — ориентированных языках с виртуальными методами и настоящим наследованием.
То, что я хочу должно выглядеть (для go) примерно так:
func (b Befavior) DoAction() { // здесь сам интерфейс является ресивером
    if !b.CheckCondition() { //Дёрнуть наследника
        return
    }
    // Do actual action
    // Общий код для всех наследников
}

Но так в go нельзя. Но что-то похожее обещали в go2 (но это, как всегда, неточно):
https://go.googlesource.com/proposal/+/master/design/go2draft-contracts.md

Зато можно вызывать базу из наследников. Сейчас у меня работет такой код:
package main

type Behavor interface {
    CheckCondition() bool //Это могло быть и приватным, но пусть клиентский код тоже сможет проверять условие
    DoAction()
}

type defaultBefavior struct{}

func (b *defaultBefavior) doActualAction() {
    // Общий код для всех наследников
}

type stupidBehavior struct {
    *defaultBefavior
}

func (b *stupidBehavior) CheckCondition() bool {
    //Проверяем внутреннее состояние - разное для всех наследников
    // и, в соответцвии с этим, возвращаем результат
    return true
}

func (b *stupidBehavior) DoAction() {
    if b.CheckCondition() {
        b.doActualAction()
    }
}

func NewStupidBehavior() Behavor {
    return &stupidBehavior{
        defaultBefavior: &defaultBefavior{},
    }
}

func main() {
    st := NewStupidBehavior()
    st.DoAction()
}


Но это значит, что метод DoAction() должен копироваться без изменений во все наследники.
Сначала я смирился и запилил 3-х наследников. Но потом, когда месяца через два спал пилить четвёртого, мой внутренний перфекционист взбунтовался — мало ли что я забуду скопировать.

Возможно я что-то делаю не так, но я так и не смог придумать как избавиться от копирования. Всё ночь снились дженерики и даже генерация кода.
Сдаётся мне, что должен быть какой-то идиоматический путь избежать такой копипасты, но я его не вижу...