Rob Pike “Git kompozisyonla ilgilidir” dediğinde tam olarak ne demek istiyor? [kapalı]


Yanıtlar:


13

O, şu sırayla bir şey kullanacağınız anlamına gelir:

class A : public B {};

Java veya C ++ gibi bir şeyde, Go'da (buna eşdeğer bir şey) kullanırsınız:

class A {
    B b;
};

Evet, bu kalıtım benzeri özellikler sağlar. Yukarıdaki örneği biraz genişletelim:

struct B {
    int foo() {}
};

struct A { 
    B b;
};

A a;

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

Ancak bunu yapmak için, C ++ veya Java'da izin verilmeyen bir sözdizimi kullanırsınız - katıştırılmış nesneyi kendi adı olmadan bırakırsınız, bu yüzden daha çok şuna benzer:

struct A {
   B;
};

1
Merak ediyorum, bunu C ++ ile yapıyorum (kompozisyonu tercih et). Git bana Java / C ++ zaman miras oluşturmak zorunda yardımcı özellikler sağlar?
Doug T.

2
@DougT .: Evet, izin verdiği şeyin (bir kısmının) genel fikrini gösteren bir örnekte düzenledim.
Jerry Coffin

2
Bence bu önemli değil: fark sadece sınıflandırma oluşturmak için gömme kullandığınız anlamına gelen sözdizimsel bir değil. Gerçek şu ki, OOP yönteminin geçersiz kılınmaması, klasik taksonominizi oluşturmanızı engeller ve bunun yerine kompozisyon kullanmanız gerekir.
Denys Séguret

1
@dystroy: Java ile karşılaştırıldığında muhtemelen bir noktanız var. C ++ ile karşılaştırıldığında, çok fazla değil - çünkü (en azından ipucu olanların arasında) bu dev taksonomiler en son 20 yıl önce görüldü.
Jerry Coffin

1
@dystroy: Hala anlamıyorsun. Modern C ++ 'da üç seviyeli bir hiyerarşi duyulmamış. C ++ 'da iostreams kütüphanesinde ve istisna hiyerarşisinde olanları görürsünüz - ancak başka hiçbir yere yakın değildir. Eğer iostreams kütüphanesi bugün tasarlanıyorsa, bence de bu şekilde olmayacağını söylemek güvenlidir. Alt satır: argümanlarınız C ++ hakkında, onunla ne kadar temas kurduğunuzdan daha az gösterir. On yıllardır kullanmadığınız göz önüne alındığında, bu mantıklı. Mantıklı olmayan şey, bu tarihli deneyime dayanarak C ++ 'ın nasıl kullanıldığını söylemeye çalışmaktır.
Jerry Coffin

8

Bu soru / sorun benzer türüdür bu bir .

Go'da gerçekten OOP'niz yok.

Bir nesneyi "uzmanlaştırmak" istiyorsanız, bunu bir kompozisyon olan gömme ile yaparsınız, ancak bazı güzellikler onu kalıtımla kısmen benzer hale getirir . Bunu şöyle yapın:

type ConnexionMysql struct {
    *sql.DB
}

Bu örnekte, ConnexionMysql * sql.DB'nin bir uzmanlık alanıdır ve ConnexionMysql üzerinde * sql.DB'de tanımlanan işlevleri çağırabilirsiniz:

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)

Yani ilk bakışta bu kompozisyonun normal taksonominizi yapmanın bir aracı olduğunu düşünebilirsiniz.

Fakat

* sql.DB'de tanımlanan bir işlev * sql.DB'de tanımlanan diğer işlevleri çağırırsa, ConnexionMysql'de yeniden tanımlanmış işlevleri olsalar bile çağırmaz.

Klasik miras ile genellikle böyle bir şey yaparsınız:

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

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

Yani, doComplexThingsüper sınıfta uzmanlık çağrıları yapan bir organizasyon olarak tanımlayabilirsiniz .

Ancak Go'da bu özel işlev değil, "üst sınıf" işlevi olarak adlandırılır.

Bu nedenle, * sql.DB'de tanımlanmış ancak ConnexionMySQL'de (veya diğer uzmanlıklarda) yeniden tanımlanmış bazı işlevleri çağırmak isteyen bir algoritmaya sahip olmak istiyorsanız, bu algoritmayı * sql.DB'nin bir işlevi olarak tanımlayamazsınız, ancak başka bir yerde tanımlamanız gerekir ve bu işlev yalnızca sağlanan uzmanlığa yapılan çağrıları oluşturur.

Arayüzleri kullanarak bunu şöyle yapabilirsiniz:

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
}

Bu, sınıf hiyerarşilerinin klasik geçersiz kılmasından oldukça farklıdır.

Özellikle, doğrudan bir fonksiyonun ikincisinden miras kalan üçüncü bir seviyeye sahip olamayacağınız açıktır.

Uygulamada, çoğunlukla (dikey) arayüzleri kullanmayı sona erdirirsiniz ve uygulamanın, uygulamanın bu üst sınıflarını "üst sınıf" ına sahip olmak yerine, verilen bir uygulamadaki çağrıları oluşturmasına izin verirsiniz.

Deneyimlerime göre, bu, bir seviyeden daha derin hiyerarşilerin pratik yokluğuna yol açar.

Çoğu zaman, diğer dillerde, A kavramının B kavramının bir uzmanlığı olduğunu gördüğünüzde, B'nin bir alt sınıfı olarak bir B sınıfı ve A sınıfı oluşturarak bu gerçeği yeniden tanımlamak için refleksiniz vardır. verilerinizin etrafında programlayın, kodunuzdaki nesnelerin sınıflandırmasını yeniden üretmek için zaman harcarsınız, bu gerçekliktir.

Go'da genel bir algoritma tanımlayamaz ve onu özelleştiremezsiniz. Genel bir algoritma tanımlamalı ve genel olduğundan ve sağlanan arayüz uygulamalarıyla çalıştığından emin olmalısınız.

Kodlayıcıların, mantığı nihayet tüm seviyeleri ima eden bir algoritmayı barındırmak için karmaşık hackler yaptığı bazı hiyerarşi ağaçlarının artan karmaşıklığından dehşete düştükten sonra, daha basit Go mantığından memnun olduğumu söyleyebilirim, sadece uygulama modelinizin kavramlarını yeniden tanımlamak yerine düşünmeniz gerekir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.