当罗伯·派克(Rob Pike)说“ Go与构图有关”时,他到底是什么意思?[关闭]


Answers:


13

他的意思是您将按照以下顺序使用某些东西:

class A : public B {};

在Java或C ++之类的语言中,您会在Go中使用(等同于):

class A {
    B b;
};

是的,这提供了类似继承的功能。让我们将示例扩展到上面:

struct B {
    int foo() {}
};

struct A { 
    B b;
};

A a;

a.foo();  // not allowed in C++ or Java, but allowed in Go.

但是,要执行此操作,请使用C ++或Java不允许的语法-保留嵌入对象的名称,这样就更像:

struct A {
   B;
};

1
我很好奇,我在C ++中做到这一点(首选组合)。go在我必须继承的Java / C ++中,是否提供了可帮助我撰写的功能?
Doug T.

2
@DougT .:是的,我在一个示例中进行了编辑,以显示其允许的(部分)总体概念。
杰里·科芬

2
我认为这错了点:差异不只是一种语法,暗示您使用嵌入来建立分类法。事实是,缺少OOP方法重写会阻止您建立经典分类法,而必须使用组合。
DenysSéguret2012年

1
@dystroy:与Java相比,您可能有一点。与C ++相比,没有那么多-因为(至少在有线索的人中)那些巨大的分类法最后一次出现在20年前。
杰里·科芬

1
@dystroy:您仍然不了解。现代C ++中的三级层次结构几乎是闻所未闻的。在C ++中,您将在iostreams库和异常层次结构中看到它们-但是几乎没有其他地方。如果iostreams库是在今天设计的,我认为可以肯定地说不是那样。底线:您的论点显示的与C ++无关,而与C ++无关。鉴于您几十年来都没有使用它,这是有道理的。毫无根据的尝试是根据过时的经验来说明如何使用C ++。
杰里·科芬

8

这个问题/问题有点类似于这个问题。

在Go中,您实际上没有OOP。

如果要“专门化”对象,则可以通过嵌入来实现,它是一种组合,但具有一些优点,使其与继承部分相似。你这样做:

type ConnexionMysql struct {
    *sql.DB
}

在此示例中,ConnexionMysql是* sql.DB的一种特殊化,您可以在ConnexionMysql上调用在* sql.DB上定义的函数:

type BaseMysql struct {
    user     string
    password string
    database string
}

func (store *BaseMysql) DB() (ConnexionMysql, error) {
    db, err := sql.Open("mymysql", store.database+"/"+store.user+"/"+store.password)
    return ConnexionMysql{db}, err
}

func (con ConnexionMysql) EtatBraldun(idBraldun uint) (*EtatBraldun, error) {
    row := con.QueryRow("select pv, pvmax, pa, tour, dla, faim from compte where id=?", idBraldun)
    // stuff
    return nil, err
}

// somewhere else:
con, err := ms.bd.DB()
defer con.Close()
// ...
somethings, err = con.EtatBraldun(id)

因此,乍一看,您可能会认为此组合是进行常规分类的工具。

如果在* sql.DB上定义的函数调用在* sql.DB上定义的其他函数,则即使存在,也不会调用在ConnexionMysql上重新定义的函数。

使用经典继承,您通常会执行以下操作:

func (db *sql.DB) doComplexThing() {
   db.doSimpleThing()
   db.doAnotherSimpleThing()
}

func (db *sql.DB) doSimpleThing() {
   // standard implementation, that we expect to override
}

也就是说,您doComplexThing在超类上将其定义为调用专业化的组织。

但是在Go语言中,这不会调用专门的函数,而是“超类”函数。

因此,如果您希望某个算法需要调用在* sql.DB上定义但在ConnexionMySQL(或其他专业化)上重新定义的某些函数,则不能将此算法定义为* sql.DB的函数,而必须在其他位置定义它并且此函数将仅构成对所提供专业化的调用。

您可以使用interfaces这样做:

type interface SimpleThingDoer {
   doSimpleThing()
   doAnotherSimpleThing()
}

func doComplexThing(db SimpleThingDoer) {
   db.doSimpleThing()
   db.doAnotherSimpleThing()
}

func (db *sql.DB) doSimpleThing() {
   // standard implementation, that we expect to override
}

func (db ConnexionMySQL) doSimpleThing() {
   // other implemenation
}

这与经典的类层次结构的重写有很大不同。

特别是,您显然不能直接让第三级继承第二级的函数实现。

在实践中,您将最终使用大多数(正交)接口,并让函数在提供的实现上编写调用,而不是让实现的“超类”来组织这些调用。

以我的经验,这实际上导致了层次结构的深层缺失。

通常,在其他语言中,当您看到概念A是概念B的专门化时,就会产生反省,可以通过创建类B和类A作为B的子类来证实这一事实。在围绕数据的程序上,您花时间重现代码中对象的分类法,这是事实。

在Go中,您无法定义通用算法并将其专用化。您必须定义一个通用算法并确保它是通用算法,并且可以与提供的接口实现一起使用。

由于某些层次结构树的复杂性不断增长而感到震惊,编码人员在这些复杂树上进行复杂的破解以尝试适应一种逻辑最终暗示所有层次的算法,我想说我对更简单的Go逻辑感到满意,即使它迫使您要思考,而不仅仅是重新定义应用程序模型的概念。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.