गो इंटरफ़ेस फील्ड्स


105

मैं इस तथ्य से परिचित हूं कि, गो, इंटरफेस डेटा के बजाय कार्यक्षमता को परिभाषित करते हैं। आप एक इंटरफ़ेस में विधियों का एक सेट रखते हैं, लेकिन आप उस इंटरफ़ेस को लागू करने वाले किसी भी क्षेत्र को निर्दिष्ट करने में असमर्थ हैं।

उदाहरण के लिए:

// Interface
type Giver interface {
    Give() int64
}

// One implementation
type FiveGiver struct {}

func (fg *FiveGiver) Give() int64 {
    return 5
}

// Another implementation
type VarGiver struct {
    number int64
}

func (vg *VarGiver) Give() int64 {
    return vg.number
}

अब हम इंटरफ़ेस और इसके कार्यान्वयन का उपयोग कर सकते हैं:

// A function that uses the interface
func GetSomething(aGiver Giver) {
    fmt.Println("The Giver gives: ", aGiver.Give())
}

// Bring it all together
func main() {
    fg := &FiveGiver{}
    vg := &VarGiver{3}
    GetSomething(fg)
    GetSomething(vg)
}

/*
Resulting output:
5
3
*/

अब, आप जो नहीं कर सकते , वह कुछ इस तरह है:

type Person interface {
    Name string
    Age int64
}

type Bob struct implements Person { // Not Go syntax!
    ...
}

func PrintName(aPerson Person) {
    fmt.Println("Person's name is: ", aPerson.Name)
}

func main() {
    b := &Bob{"Bob", 23}
    PrintName(b)
}

हालांकि, इंटरफेस और एम्बेडेड संरचनाओं के साथ खेलने के बाद, मैंने एक फैशन के बाद, ऐसा करने का एक तरीका खोज लिया है:

type PersonProvider interface {
    GetPerson() *Person
}

type Person struct {
    Name string
    Age  int64
}

func (p *Person) GetPerson() *Person {
    return p
}

type Bob struct {
    FavoriteNumber int64
    Person
}

एम्बेडेड संरचना के कारण, बॉब के पास सब कुछ है जो व्यक्ति के पास है। यह व्यक्तिप्रोफ़ाइडर इंटरफ़ेस को भी लागू करता है, इसलिए हम बॉब को उन फ़ंक्शंस में पारित कर सकते हैं जो उस इंटरफ़ेस का उपयोग करने के लिए डिज़ाइन किए गए हैं।

func DoBirthday(pp PersonProvider) {
    pers := pp.GetPerson()
    pers.Age += 1
}

func SayHi(pp PersonProvider) {
    fmt.Printf("Hello, %v!\r", pp.GetPerson().Name)
}

func main() {
    b := &Bob{
        5,
        Person{"Bob", 23},
    }
    DoBirthday(b)
    SayHi(b)
    fmt.Printf("You're %v years old now!", b.Age)
}

यहाँ एक Go Playground है जो उपरोक्त कोड को प्रदर्शित करता है।

इस पद्धति का उपयोग करके, मैं एक इंटरफ़ेस बना सकता हूं जो व्यवहार के बजाय डेटा को परिभाषित करता है, और जिसे किसी भी संरचना द्वारा केवल उस डेटा को एम्बेड करके कार्यान्वित किया जा सकता है। आप उन कार्यों को परिभाषित कर सकते हैं जो स्पष्ट रूप से उस एम्बेडेड डेटा के साथ बातचीत करते हैं और बाहरी संरचना की प्रकृति से अनजान हैं। और सब कुछ संकलन समय पर जांचा जाता है! (एक ही तरीका है कि आप गड़बड़ कर सकते हैं, जिसे मैं देख सकता हूं, एक कंक्रीट के बजाय इंटरफ़ेस PersonProviderको एम्बेड करना होगा। यह रनटाइम पर संकलन और विफल हो जाएगा।)BobPerson

अब, यहाँ मेरा प्रश्न है: यह एक साफ चाल है, या मुझे इसे अलग तरह से करना चाहिए?


3
"मैं एक इंटरफ़ेस बना सकता हूं जो व्यवहार के बजाय डेटा को परिभाषित करता है"। मेरा तर्क है कि आपके पास एक व्यवहार है जो डेटा लौटाता है।
जलमनी

मैं एक उत्तर लिखने वाला हूँ; मुझे लगता है कि यह ठीक है अगर आपको इसकी आवश्यकता है और परिणाम पता है, लेकिन परिणाम हैं और मैं हर समय ऐसा नहीं करूंगा।
twotwotwo

@ जगमनी मुझे लगता है कि आप सही हैं, अगर आप इसे स्पष्ट रूप से देखना चाहते हैं। लेकिन कुल मिलाकर, मैंने जो अलग-अलग टुकड़े दिखाए हैं, वे शब्दार्थ बन गए हैं "यह फ़ंक्शन किसी भी संरचना को स्वीकार करता है जिसकी संरचना में ___ है"। कम से कम, यही मेरा इरादा है।
मैट मैक

1
यह "उत्तर" सामग्री नहीं है। मैंने आपके प्रश्न को "इंटरफ़ेस प्रॉपर्टी प्रॉपर्टी गोलांग" के रूप में जाना है। मुझे एक संरचना सेट करके एक समान दृष्टिकोण मिला जो एक इंटरफ़ेस को किसी अन्य संरचना की संपत्ति के रूप में लागू करता है। यहाँ खेल का मैदान है, play.golang.org/p/KLzREXk9xo मुझे कुछ विचार देने के लिए धन्यवाद।
डेल 4

1
पूर्वव्यापी में, और गो का उपयोग करने के 5 साल बाद, यह मेरे लिए स्पष्ट है कि ऊपर मुहावरेदार गो नहीं है। यह जेनेरिक के प्रति एक तनाव है। यदि आप इस तरह की बात करने के लिए ललचाते हैं, तो मैं आपको सलाह देता हूं कि आप अपने सिस्टम की वास्तुकला पर पुनर्विचार करें। इंटरफेस स्वीकार करें और वापस लौटें, संचार द्वारा साझा करें, और आनन्दित हों।
मैट मैक

जवाबों:


55

यह निश्चित रूप से एक स्वच्छ चाल है। हालाँकि, एक्सपोज़र पॉइंटर्स अभी भी डेटा तक सीधी पहुँच उपलब्ध कराते हैं, इसलिए यह आपको भविष्य में होने वाले बदलावों के लिए अतिरिक्त लचीलापन प्रदान करता है। इसके अलावा, गो सम्मेलनों में आपको हमेशा अपने डेटा विशेषताओं के सामने एक अमूर्तता रखने की आवश्यकता नहीं होती है

उन चीजों को एक साथ लेते हुए, मैं किसी दिए गए उपयोग के मामले में एक चरम या दूसरे की ओर जाता हूं: या तो) बस एक सार्वजनिक विशेषता बनाते हैं (यदि लागू हो तो एम्बेडिंग का उपयोग करें) और कंक्रीट के प्रकारों को पास या बी) यदि यह डेटा को उजागर करने की तरह लगता है बाद में परेशानी का कारण, अधिक मजबूत अमूर्तता के लिए एक गेटटर / सेटर को उजागर करना।

आप इसे प्रति-विशेषता के आधार पर तौलने जा रहे हैं। उदाहरण के लिए, यदि कुछ डेटा कार्यान्वयन-विशिष्ट हैं या आप किसी अन्य कारण से प्रतिनिधित्व बदलने की अपेक्षा करते हैं, तो आप संभवतः विशेषता को सीधे उजागर नहीं करना चाहते हैं, जबकि अन्य डेटा विशेषताएँ पर्याप्त रूप से स्थिर हो सकती हैं, जिससे उन्हें सार्वजनिक रूप से शुद्ध जीत मिलती है।


गेटर्स और सेटर्स के पीछे गुणों को छिपाने से आपको बाद में पीछे की ओर संगत बदलाव करने के लिए कुछ अतिरिक्त लचीलापन मिलता है। आप कहेंगे कि किसी दिन Personकेवल "नाम" फ़ील्ड को स्टोर करने के लिए बदलना चाहते हैं, लेकिन पहले / मध्य / अंतिम / उपसर्ग; यदि आपके पास तरीके हैं Name() stringऔर SetName(string), आप Personनए महीन-दाने वाले तरीकों को जोड़ते हुए इंटरफ़ेस के मौजूदा उपयोगकर्ताओं को खुश रख सकते हैं । या जब आप सहेजे गए परिवर्तनों को डेटाबेस-समर्थित ऑब्जेक्ट "गंदे" के रूप में चिह्नित करना चाहते हैं; आप ऐसा कर सकते हैं जब डेटा अपडेट सभी SetFoo()तरीकों से गुजरते हैं ।

इसलिए: गेटर्स / सेटर्स के साथ, आप संगत API को बनाए रखते हुए संरचनात्मक फ़ील्ड बदल सकते हैं, और प्रॉपर्टी पाने / सेट के आसपास तर्क जोड़ सकते हैं क्योंकि कोई भी p.Name = "bob"आपके कोड के बिना नहीं जा सकता है ।

जब प्रकार जटिल होता है (और कोडबेस बड़ा होता है) तो यह लचीलापन अधिक प्रासंगिक होता है। यदि आपके पास एक है PersonCollection, तो यह आंतरिक रूप से sql.Rows, एक []*Person, []uintडेटाबेस आईडी या जो कुछ भी हो, द्वारा समर्थित हो सकता है । सही इंटरफ़ेस का उपयोग करके, आप कॉलर्स को देखभाल करने से बचा सकते हैं जो यह है, जिस तरह io.Readerसे नेटवर्क कनेक्शन और फाइलें एक जैसे दिखते हैं।

एक खास बात: interfaceगो इन में अजीबोगरीब संपत्ति है जिसे आप इसे परिभाषित करने वाले पैकेज को आयात किए बिना भी लागू कर सकते हैं; जो आपको चक्रीय आयात से बचने में मदद कर सकता है । यदि आपका इंटरफ़ेस *Personसिर्फ स्ट्रिंग्स या जो भी है, के बजाय रिटर्न देता है , PersonProvidersतो सभी को उस पैकेज को आयात करना होगा जहां Personपरिभाषित किया गया है। यह ठीक या अपरिहार्य भी हो सकता है; इसके बारे में जानना सिर्फ एक परिणाम है।


लेकिन फिर से, गो समुदाय के पास आपके प्रकार के सार्वजनिक एपीआई में डेटा सदस्यों को उजागर करने के खिलाफ एक मजबूत सम्मेलन नहीं है । यह आपके निर्णय पर छोड़ दिया गया है कि क्या किसी दिए गए मामले में आपके API के हिस्से के रूप में किसी विशेषता के लिए सार्वजनिक पहुँच का उपयोग करना उचित है, बजाय इसके कि किसी भी जोखिम को हतोत्साहित किया जाए क्योंकि यह संभवतः बाद में एक कार्यान्वयन परिवर्तन को जटिल या रोक सकता है।

इसलिए, उदाहरण के लिए, stdlib ऐसी चीजें करता है जो आपको http.Serverअपने कॉन्फ़िगरेशन के साथ आरंभ करने देती हैं और वादा करती हैं कि एक शून्य bytes.Bufferप्रयोग करने योग्य है। यह ठीक है कि अपने सामान की तरह, और, वास्तव में, मुझे नहीं लगता कि आपको सारगर्भित चीजों को तुरंत दूर करना चाहिए यदि अधिक ठोस, डेटा-एक्सपोजिंग संस्करण काम करने की संभावना है। यह सिर्फ ट्रेडऑफ के बारे में पता होना है।


एक अतिरिक्त बात: एम्बेडिंग दृष्टिकोण विरासत की तरह थोड़ा अधिक है, है ना? आपको कोई भी फ़ील्ड और विधियाँ प्राप्त होती हैं जो एम्बेडेड संरचना में होती हैं, और आप इसके इंटरफ़ेस का उपयोग कर सकते हैं, ताकि कोई भी अधिरचना, इंटरफेस के पुन: लागू करने के बिना योग्य हो जाए।
मैट मैक

हाँ - अन्य लैंग्स में आभासी विरासत जैसा बहुत कुछ। आप इंटरफ़ेस को लागू करने के लिए एम्बेडिंग का उपयोग कर सकते हैं चाहे वह गेटर्स और सेटर या डेटा के लिए एक पॉइंटर के रूप में परिभाषित किया गया हो (या, छोटे संरचनाओं के लिए आसानी से तीसरा विकल्प, संरचना की एक प्रति)।
twotwotwo

मुझे यह कहना है, यह मुझे 1999 में फ्लैशबैक दे रहा है और जावा में बॉयलरप्लेट गेटर्स और सेटर के रीम्स लिखना सीख रहा है।
टॉम

यह बहुत बुरा है गो का अपना मानक पुस्तकालय हमेशा ऐसा नहीं करता है। मैं यूनिट परीक्षण के लिए os.Process को कुछ कॉल को मॉक करने की कोशिश कर रहा हूं। मैं सिर्फ एक इंटरफ़ेस में प्रक्रिया ऑब्जेक्ट को नहीं लपेट सकता क्योंकि Pid मेंबर वेरिएबल को सीधे एक्सेस किया जाता है और गो इंटरफेस में सदस्य वेरिएबल को सपोर्ट नहीं करता है।
एलेक्स जानसन

1
@ यह सच है। मुझे लगता है कि गेटर्स / सेटर एक पॉइंटर को उजागर करने की तुलना में अधिक लचीलापन जोड़ते हैं, लेकिन मुझे यह भी नहीं लगता कि हर किसी को गेटवे / सेटर-इफी सब कुछ चाहिए (या जो कि विशिष्ट गो शैली से मेल खाता होगा)। मैंने पहले उस पर कुछ शब्द इशारा किया था, लेकिन शुरुआत और अंत को संशोधित करके इसे और अधिक करने पर जोर दिया।
twotwotwo

2

अगर मैं सही ढंग से समझूं तो आप एक स्ट्रक्चर फील्ड को दूसरे में बदलना चाहते हैं। मेरी राय का विस्तार करने के लिए इंटरफेस का उपयोग नहीं करना है। आप इसे अगले दृष्टिकोण से आसानी से कर सकते हैं।

package main

import (
    "fmt"
)

type Person struct {
    Name        string
    Age         int
    Citizenship string
}

type Bob struct {
    SSN string
    Person
}

func main() {
    bob := &Bob{}

    bob.Name = "Bob"
    bob.Age = 15
    bob.Citizenship = "US"

    bob.SSN = "BobSecret"

    fmt.Printf("%+v", bob)
}

https://play.golang.org/p/aBJ5fq3uXtt

घोषणा Personमें ध्यान दें Bob। यह सम्मिलित संरचना क्षेत्र Bobको कुछ सिन्थेटिक चीनी के साथ सीधे संरचना में उपलब्ध कराया जाएगा ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.