paint-brush
गो में निर्भरता व्युत्क्रम सिद्धांत: यह क्या है और इसका उपयोग कैसे करेंद्वारा@kirooha
27,512 रीडिंग
27,512 रीडिंग

गो में निर्भरता व्युत्क्रम सिद्धांत: यह क्या है और इसका उपयोग कैसे करें

द्वारा Kirill Parasotchenko10m2024/05/12
Read on Terminal Reader

बहुत लंबा; पढ़ने के लिए

इस लेख में, हम निर्भरता व्युत्क्रम सिद्धांत पर चर्चा करेंगे। संक्षेप में, हम इस बारे में बात करेंगे कि यह क्या है और एक सरल गो एप्लिकेशन का उदाहरण लेकर इस सिद्धांत की जांच करेंगे।
featured image - गो में निर्भरता व्युत्क्रम सिद्धांत: यह क्या है और इसका उपयोग कैसे करें
Kirill Parasotchenko HackerNoon profile picture

पहचान

इस लेख में, हम निर्भरता व्युत्क्रम सिद्धांत पर चर्चा करेंगे। संक्षेप में, हम इस बारे में बात करेंगे कि यह क्या है और एक सरल गो एप्लिकेशन का उदाहरण लेकर इस सिद्धांत की जांच करेंगे।

निर्भरता व्युत्क्रम सिद्धांत क्या है?

निर्भरता व्युत्क्रम सिद्धांत (डीआईपी) ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग (ओओपी) के पांच ठोस सिद्धांतों में से एक है, जिसे सबसे पहले रॉबर्ट सी. मार्टिन ने पेश किया था। इसमें कहा गया है:


  1. उच्च-स्तरीय मॉड्यूल को निम्न-स्तरीय मॉड्यूल से कुछ भी आयात नहीं करना चाहिए। दोनों को अमूर्तता (जैसे, इंटरफेस) पर निर्भर होना चाहिए।


  2. अमूर्तन को विवरणों पर निर्भर नहीं होना चाहिए। विवरण (ठोस कार्यान्वयन) अमूर्तन पर निर्भर होना चाहिए।


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

उदाहरण

आइए विचार करें कि DI सिद्धांत का कार्यान्वयन Go में कैसा दिख सकता है। हम एक एकल एंडपॉइंट/बुक वाले HTTP एप्लिकेशन के सरल उदाहरण से शुरू करेंगे, जो किसी पुस्तक के बारे में उसकी ID के आधार पर जानकारी लौटाता है। पुस्तक के बारे में जानकारी प्राप्त करने के लिए, एप्लिकेशन बाहरी HTTP सेवा के साथ इंटरैक्ट करेगा।

परियोजना संरचना

cmd - गो कमांड वाला फ़ोल्डर। मुख्य फ़ंक्शन यहीं रहेगा।


आंतरिक - आंतरिक एप्लिकेशन कोड वाला फ़ोल्डर। हमारा सारा कोड यहीं रहेगा।

DI के बिना स्पेगेटी कोड का उदाहरण

main.go केवल HTTP सर्वर प्रारंभ करता है।

 package main import ( "log" "net/http" "example.com/books/internal/app/httpbookapi" ) func main() { http.Handle("/book", &httpbookapi.Handler{}) log.Print("server listening at 9090") log.Fatal(http.ListenAndServe(":9090", nil)) }


हमारे HTTP समापन बिंदु को संभालने के लिए कोड यहां दिया गया है:

 package httpbookapi import ( "encoding/json" "fmt" "net/http" "example.com/books/internal/model" ) type Handler struct { } func (h *Handler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { var ( ctx = request.Context() id = request.URL.Query().Get("id") book model.Book ) url := fmt.Sprintf("http://localhost:8080/book?id=%s", id) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { http.Error(writer, err.Error(), http.StatusInternalServerError) return } resp, err := http.DefaultClient.Do(req) if err != nil { http.Error(writer, err.Error(), http.StatusInternalServerError) return } defer resp.Body.Close() if err := json.NewDecoder(resp.Body).Decode(&book); err != nil { http.Error(writer, err.Error(), http.StatusInternalServerError) return } book.Price = 10.12 if book.Title == "Pride and Prejudice" { book.Price += 2 } writer.Header().Add("Content-Type", "application/json") if err := json.NewEncoder(writer).Encode(book); err != nil { http.Error(writer, err.Error(), http.StatusInternalServerError) return } }

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

सुधार का पहला कदम

पहले चरण के रूप में, आइए इस कोड को एक अलग स्थान पर ले जाएँ। ऐसा करने के लिए, हम internal/pkg/getbook/usecase.go नामक एक फ़ाइल बनाएंगे, जहाँ हमारी पुस्तक को पुनः प्राप्त करने और संसाधित करने के लिए तर्क मौजूद होगा, और internal/pkg/getbook/types.go , जहाँ हम आवश्यक getbook प्रकारों को संग्रहीत करेंगे।


usecase.go कोड

 package getbook import ( "context" "encoding/json" "fmt" "net/http" ) type UseCase struct { bookServiceClient *http.Client } func NewUseCase() *UseCase { return &UseCase{} } func (u *UseCase) GetBook(ctx context.Context, id string) (*Book, error) { var ( book Book url = fmt.Sprintf("http://localhost:8080/book?id=%s", id) ) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return nil, err } resp, err := u.bookServiceClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if err := json.NewDecoder(resp.Body).Decode(&book); err != nil { return nil, err } book.Price = 10.12 if book.Title == "Pride and Prejudice" { book.Price += 2 } return &book, nil }


types.go कोड

 package getbook type Book struct { ID string `json:"id"` Title string `json:"title"` Author string `json:"author"` Price float64 `json:"price"` }


नया हैंडलर कोड:

 package httpbookapi import ( "encoding/json" "net/http" "example.com/books/internal/pkg/getbook" ) type Handler struct { getBookUseCase *getbook.UseCase } func NewHandler(getBookUseCase *getbook.UseCase) *Handler { return &Handler{ getBookUseCase: getBookUseCase, } } func (h *Handler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { var ( ctx = request.Context() id = request.URL.Query().Get("id") ) book, err := h.getBookUseCase.GetBook(ctx, id) if err != nil { http.Error(writer, err.Error(), http.StatusInternalServerError) return } writer.Header().Add("Content-Type", "application/json") if err := json.NewEncoder(writer).Encode(book); err != nil { http.Error(writer, err.Error(), http.StatusInternalServerError) return } }


जैसा कि आप देख सकते हैं, हैंडलर कोड बहुत साफ हो गया है, लेकिन अब, हमारे लिए getbook/usecase.go पर नज़र डालना बहुत अधिक दिलचस्प है

 type UseCase struct { bookServiceClient *http.Client }


UseCase में *http.Client के रूप में निर्भरता है, जिसे हम वर्तमान में किसी भी तरह से आरंभ नहीं कर रहे हैं। हम *http.Client को NewUseCase() कंस्ट्रक्टर में पास कर सकते हैं या सीधे कंस्ट्रक्टर के भीतर *http.Client बना सकते हैं। हालाँकि, आइए एक बार फिर याद करें कि DI सिद्धांत हमें क्या बताता है।


उच्च-स्तरीय मॉड्यूल को निम्न-स्तरीय मॉड्यूल से कुछ भी आयात नहीं करना चाहिए। दोनों को अमूर्तता (जैसे, इंटरफेस) पर निर्भर होना चाहिए


हालाँकि, इस दृष्टिकोण के साथ, हमने बिलकुल विपरीत किया है। हमारा उच्च-स्तरीय मॉड्यूल, getbook, निम्न-स्तरीय मॉड्यूल, HTTP को आयात करता है।

निर्भरता व्युत्क्रमण का परिचय

आइए इस बारे में सोचें कि हम इसे कैसे ठीक कर सकते हैं। शुरू करने के लिए, आइए internal/pkg/bookserviceclient/client.go नामक एक फ़ाइल बनाएँ। इस फ़ाइल में बाहरी सेवा और संबंधित इंटरफ़ेस के लिए HTTP अनुरोधों का कार्यान्वयन शामिल होगा।

 package bookserviceclient import ( "context" "fmt" "io" "net/http" ) type Client interface { GetBook(ctx context.Context, id string) ([]byte, error) } type client struct { httpClient *http.Client } func NewClient() Client { return &client{ httpClient: http.DefaultClient, } } func (c *client) GetBook(ctx context.Context, id string) ([]byte, error) { var ( url = fmt.Sprintf("http://localhost:8080/book?id=%s", id) ) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return nil, err } resp, err := c.httpClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() b, err := io.ReadAll(resp.Body) if err != nil { return nil, err } return b, nil }


इसके बाद, हमें अपने UseCase को अपडेट करना होगा ताकि वह bookserviceclient पैकेज से इंटरफ़ेस का उपयोग करना शुरू कर दे।

 package getbook import ( "context" "encoding/json" "example.com/books/internal/pkg/bookserviceclient" ) type UseCase struct { bookClient bookserviceclient.Client } func NewUseCase(bookClient bookserviceclient.Client) *Usecase { return &UseCase{ bookClient: bookClient, } } func (u *UseCase) GetBook(ctx context.Context, id string) (*Book, error) { var ( book Book ) b, err := u.bookClient.GetBook(ctx, id) if err != nil { return nil, err } if err := json.Unmarshal(b, &book); err != nil { return nil, err } book.Price = 10.12 if book.Title == "Pride and Prejudice" { book.Price += 2 } return &book, nil }


ऐसा लगता है कि चीजें काफी हद तक सुधर गई हैं, और हमने निम्न-स्तरीय मॉड्यूल पर useсase की निर्भरता समस्या को संबोधित किया है। हालाँकि, यह अभी तक पूरी तरह से नहीं हुआ है। आइए इसे एक कदम आगे ले चलते हैं। अभी, निर्भरता घोषित करने के लिए, useсase निम्न-स्तरीय मॉड्यूल से एक इंटरफ़ेस का उपयोग कर रहा है। क्या हम इसे सुधार सकते हैं? क्या होगा यदि हम pkg/getbook/types.go में अपनी ज़रूरत के इंटरफ़ेस घोषित करते हैं?


इस तरह, हम निम्न-स्तरीय मॉड्यूल पर स्पष्ट निर्भरता को हटा देंगे। यानी, हमारा उच्च-स्तरीय मॉड्यूल अपने संचालन के लिए आवश्यक सभी इंटरफेस की घोषणा करेगा, इस प्रकार, निम्न-स्तरीय मॉड्यूल पर सभी निर्भरता को हटा देगा। एप्लिकेशन के शीर्ष स्तर ( main.go ) पर, हम तब useсcase के काम करने के लिए आवश्यक सभी इंटरफेस को लागू करेंगे।


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

अंतिम कोड

usecase.go

 package getbook import ( "context" "encoding/json" ) type UseCase struct { bookClient bookClient } func NewUseCase(bookClient bookClient) *UseCase { return &UseCase{ bookClient: bookClient, } } func (u *UseCase) GetBook(ctx context.Context, id string) (*Book, error) { var ( book Book ) b, err := u.bookClient.GetBook(ctx, id) if err != nil { return nil, err } if err := json.Unmarshal(b, &book); err != nil { return nil, err } book.Price = 10.12 if book.Title == "Pride and Prejudice" { book.Price += 2 } return &book, nil }


प्रकार.go

 package getbook import "context" type bookClient interface { GetBook(ctx context.Context, id string) ([]byte, error) } type Book struct { ID string `json:"id"` Title string `json:"title"` Author string `json:"author"` Price float64 `json:"price"` }


client.go

 package bookserviceclient import ( "context" "fmt" "io" "net/http" ) type Client struct { httpClient *http.Client } func NewClient() *Client { return &Client{ httpClient: http.DefaultClient, } } func (c *Client) GetBook(ctx context.Context, id string) ([]byte, error) { var ( url = fmt.Sprintf("http://localhost:8080/book?id=%s", id) ) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return nil, err } resp, err := c.httpClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() b, err := io.ReadAll(resp.Body) if err != nil { return nil, err } return b, nil }


मुख्य.जाओ

 package main import ( "log" "net/http" "example.com/books/internal/app/httpbookapi" "example.com/books/internal/pkg/bookserviceclient" "example.com/books/internal/pkg/getbook" ) func main() { bookServiceClient := bookserviceclient.NewClient() useCase := getbook.NewUsecase(bookServiceClient) handler := httpbookapi.NewHandler(useCase) http.Handle("/book", handler) log.Print("server listening at 9090") log.Fatal(http.ListenAndServe(":9090", nil)) }

सारांश

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