वंशानुक्रम और निर्भरता इंजेक्शन


97

मेरे पास कोणीय 2 घटकों का एक सेट है जिसे सभी को कुछ सेवा प्राप्त करनी चाहिए। मेरा पहला विचार यह था कि सुपर क्लास बनाना और वहाँ सेवा को इंजेक्ट करना सबसे अच्छा होगा। मेरा कोई भी घटक उस सुपरक्लास का विस्तार करेगा लेकिन यह दृष्टिकोण काम नहीं करता है।

सरलीकृत उदाहरण:

export class AbstractComponent {
  constructor(private myservice: MyService) {
    // Inject the service I need for all components
  }
}

export MyComponent extends AbstractComponent {
  constructor(private anotherService: AnotherService) {
    super(); // This gives an error as super constructor needs an argument
  }
}

मैं MyServiceप्रत्येक घटक के भीतर इंजेक्शन लगाकर इसे हल कर सकता था और super()कॉल के लिए उस तर्क का उपयोग कर सकता था लेकिन यह निश्चित रूप से किसी तरह का बेतुका है।

मेरे घटकों को सही तरीके से कैसे व्यवस्थित करें ताकि वे सुपर क्लास से एक सेवा प्राप्त करें?


यह कोई डुप्लिकेट नहीं है। संदर्भित किया जा रहा सवाल यह है कि कैसे एक DERIVED वर्ग का निर्माण किया जाए जो पहले से परिभाषित सुपर क्लास द्वारा इंजेक्ट की गई सेवा को प्राप्त कर सके। मेरा प्रश्न यह है कि एक सुपर क्लास का निर्माण कैसे किया जाए जो व्युत्पन्न वर्गों के लिए एक सेवा विरासत में मिले। यह बस के आसपास दूसरा रास्ता है।
मैक्सहब

आपका उत्तर (आपके प्रश्न में इनलाइन) का कोई मतलब नहीं है। इस तरह से आप एक इंजेक्टर बनाते हैं जो आपके आवेदन के लिए इंजेक्टर कोणीय उपयोग से स्वतंत्र है। new MyService()इंजेक्शन लगाने के बजाय उपयोग करने से आपको बिल्कुल वही परिणाम मिलता है (अधिक कुशल को छोड़कर)। यदि आप एक ही सेवा उदाहरण को विभिन्न सेवाओं और / या घटकों में साझा करना चाहते हैं, तो यह काम नहीं करेगा। हर वर्ग को एक और MyServiceउदाहरण मिलेगा ।
गुंटर ज़ोचबॉयर

आप पूरी तरह से सही हैं, मेरा कोड बहुत सारे उदाहरण उत्पन्न करेगा myService। एक समाधान है कि इस से बचा जाता है लेकिन व्युत्पन्न वर्ग के लिए और अधिक कोड कहते हैं ... मिले
maxhb

इंजेक्टर को इंजेक्ट करना केवल एक सुधार है जब कई अलग-अलग सेवाएं होती हैं जिन्हें कई स्थानों पर इंजेक्ट करने की आवश्यकता होती है। आप एक ऐसी सेवा को भी इंजेक्ट कर सकते हैं, जिसमें अन्य सेवाओं के लिए निर्भरता है और उन्हें एक गेटर (या विधि) का उपयोग करके प्रदान किया जाता है। इस तरह आपको केवल एक सेवा को इंजेक्ट करने की आवश्यकता है लेकिन सेवाओं के एक सेट का उपयोग कर सकते हैं। आपके समाधान और मेरे प्रस्तावित विकल्प में दोनों नुकसान हैं कि वे यह देखना कठिन बनाते हैं कि कौन सी सेवा किस वर्ग पर निर्भर करती है। मैं इसके बजाय टूल बनाना चाहता हूं (जैसे WebStorm में लाइव टेम्प्लेट) जो बॉयलरप्लेट कोड बनाना आसान बनाता है और निर्भरता के बारे में स्पष्ट होना चाहिए
गुंटर ज़ोचबॉयर

जवाबों:


72

मैं प्रत्येक घटक के भीतर MyService इंजेक्ट करके इसे हल कर सकता था और सुपर () कॉल के लिए उस तर्क का उपयोग कर सकता था, लेकिन यह निश्चित रूप से किसी तरह का बेतुका है।

यह बेतुका नहीं है। यह है कि कंस्ट्रक्टर और कंस्ट्रक्टर इंजेक्शन कैसे काम करता है।

प्रत्येक इंजेक्टेबल क्लास को निर्भरता को कंस्ट्रक्टर मापदंडों के रूप में घोषित करना पड़ता है और यदि सुपरक्लास में भी निर्भरताएं होती हैं, तो इन सबक्लास के कंस्ट्रक्टर में सूचीबद्ध होने की आवश्यकता होती है और super(dep1, dep2)कॉल के साथ सुपरक्लास को पास किया जाता है ।

एक इंजेक्टर के आसपास से गुजरना और निर्भरता प्राप्त करना अनिवार्य रूप से गंभीर नुकसान है।

यह निर्भरताएँ छिपाता है जो कोड को पढ़ना कठिन बनाता है।
यह एक परिचित की अपेक्षाओं का उल्लंघन करता है कि Angular2 DI कैसे काम करता है।
यह ऑफ़लाइन संकलन को तोड़ता है जो प्रदर्शन को बेहतर बनाने और कोड के आकार को कम करने के लिए घोषणात्मक और अनिवार्य डीआई को बदलने के लिए स्थिर कोड उत्पन्न करता है।


4
बस इसे स्पष्ट करने के लिए: मुझे इसकी आवश्यकता है। उस निर्भरता को मेरे सुपर क्लास में ले जाने की कोशिश कर रहा है ताकि EACH व्युत्पन्न वर्ग प्रत्येक व्युत्पन्न वर्ग को व्यक्तिगत रूप से इसे इंजेक्ट करने की आवश्यकता के बिना सेवा का उपयोग कर सके।
मैक्सहब

9
अपने ही सवाल का जवाब बदसूरत हैक है। प्रश्न पहले से ही दर्शाता है कि यह कैसे किया जाना चाहिए। मैंने थोड़ा और विस्तार किया।
गुंटर ज़ोचबॉयर

7
यह उत्तर सही है। ओपी ने अपने स्वयं के प्रश्न का उत्तर दिया, लेकिन ऐसा करने में बहुत सारे सम्मेलनों को तोड़ दिया। आपने वास्तविक नुकसान को सूचीबद्ध किया है जो सहायक है और मैं इसके लिए प्रतिज्ञा करूंगा - मैं भी यही सोच रहा था।
दुवेद २५'१17 को

6
मैं वास्तव में ओपी के "हैक" पर इस उत्तर का उपयोग (और जारी रखना) करना चाहता हूं। लेकिन मेरा कहना है कि यह DRY से बहुत दूर है और बहुत दर्दनाक है जब मैं आधार वर्ग में एक निर्भरता जोड़ना चाहता हूं। मुझे बस superलगभग 20+ कक्षाओं में ctor इंजेक्शन (और संबंधित कॉल) जोड़ना था और यह संख्या भविष्य में बढ़ने वाली है। तो दो बातें: 1) मैं एक "बड़े कोडबेस" को देखने से नफरत करता हूँ; और 2) भगवान का शुक्र qऔर विस्कोस के लिए धन्यवादctrl+.

5
सिर्फ इसलिए कि यह असुविधाजनक है इसका मतलब यह नहीं है कि यह बुरा अभ्यास है। कंस्ट्रक्टर्स असुविधाजनक हैं क्योंकि वस्तु आरंभीकरण को मज़बूती से किया जाना बहुत मुश्किल है। मेरा मानना ​​है कि इससे भी बदतर प्रथा एक ऐसी सेवा का निर्माण कर रही है, जिसे "15 सेवाओं के लिए एक बेस क्लास की जरूरत है और 6 से विरासत में मिली है"।
गुंटर Zöchbauer

64

अद्यतन समाधान, वैश्विक इंजेक्टर का उपयोग करके उत्पन्न होने वाले myService के कई उदाहरणों को रोकता है।

import {Injector} from '@angular/core';
import {MyServiceA} from './myServiceA';
import {MyServiceB} from './myServiceB';
import {MyServiceC} from './myServiceC';

export class AbstractComponent {
  protected myServiceA:MyServiceA;
  protected myServiceB:MyServiceB;
  protected myServiceC:MyServiceC;

  constructor(injector: Injector) {
    this.settingsServiceA = injector.get(MyServiceA);
    this.settingsServiceB = injector.get(MyServiceB);
    this.settingsServiceB = injector.get(MyServiceC);
  }
}

export MyComponent extends AbstractComponent {
  constructor(
    private anotherService: AnotherService,
    injector: Injector
  ) {
    super(injector);

    this.myServiceA.JustCallSomeMethod();
    this.myServiceB.JustCallAnotherMethod();
    this.myServiceC.JustOneMoreMethod();
  }
}

यह सुनिश्चित करेगा कि MyService का उपयोग किसी भी वर्ग के भीतर किया जा सकता है जो हर व्युत्पन्न वर्ग में MyService को इंजेक्ट करने की आवश्यकता के बिना AbstractComponent का विस्तार करता है।

इस समाधान के लिए कुछ विपक्ष हैं (मेरे मूल प्रश्न के नीचे @ Günter Zöchbauer से Ccomment देखें):

  • वैश्विक इंजेक्टर को इंजेक्ट करना केवल एक सुधार है जब कई अलग-अलग सेवाएं हैं जिन्हें कई स्थानों पर इंजेक्ट करने की आवश्यकता होती है। यदि आपके पास बस एक साझा सेवा है, तो यह बेहतर है कि व्युत्पन्न वर्ग के भीतर उस सेवा को इंजेक्ट करना आसान / आसान है
  • मेरे समाधान और उनके प्रस्तावित विकल्प के दोनों नुकसान हैं कि वे यह देखना कठिन बनाते हैं कि कौन सी सेवा किस वर्ग पर निर्भर करती है।

Angular2 में निर्भरता इंजेक्शन की एक बहुत अच्छी तरह से लिखित स्पष्टीकरण के लिए इस ब्लॉग पोस्ट को देखें जिसने मुझे समस्या को हल करने में बहुत मदद की: http://blog.thoughtram.io/angular/2015/05/18/d dependency-injection-in-angular- 2.html


7
इससे यह समझना बहुत कठिन हो जाता है कि वास्तव में सेवाओं को क्या इंजेक्ट किया जाता है।
साइमन डुफौर

यह नहीं होना चाहिए this.myServiceA = injector.get(MyServiceA);आदि?
जेंसन-बटन-ईवेंट

9
@Gunter Zochbauer का उत्तर सही है। यह ऐसा करने का सही तरीका नहीं है और बहुत सारे कोणीय सम्मेलनों को तोड़ता है। यह सरल हो सकता है कि उन सभी इंजेक्शन कॉल को कोड करना एक "दर्द" है, लेकिन यदि आप एक बड़े कोडबेस को बनाए रखने में सक्षम होने के लिए कंस्ट्रक्टर कोड लिखने के लिए बलिदान करना चाहते हैं, तो आप अपने आप को पैर में गोली मार रहे हैं। यह समाधान स्केलेबल, IMO नहीं है, और सड़क के नीचे बहुत सारे भ्रामक कीड़े पैदा करेगा।
दुवेद २५'१17 को

3
एक ही सेवा के कई उदाहरणों का जोखिम नहीं है। आपको बस अपने आवेदन के मूल में एक सेवा प्रदान करनी होगी जो कि कई उदाहरणों को रोकने के लिए हो सकती है जो कि आवेदन की विभिन्न शाखाओं पर हो सकती है। वंशानुक्रम परिवर्तन के नीचे सेवाओं को पारित करने से कक्षाओं के नए उदाहरण नहीं बनते हैं। @Gunter Zochbauer का उत्तर सही है।
ktamlyn

@ maxhb क्या आपने कभी Injectorभी किसी भी पैरामीटर की श्रृंखला से बचने के लिए विश्व स्तर पर विस्तार किया है AbstractComponent? fwiw, मुझे लगता है कि गंदे कंस्ट्रक्टर चिनिंग से बचने के लिए व्यापक रूप से इस्तेमाल किए जाने वाले बेस क्लास में संपत्ति पर निर्भरता को सामान्य नियम के लिए पूरी तरह से वैध अपवाद है।
क्वेंटिन-स्टारिन

4

सभी सेवाओं को मैन्युअल रूप से इंजेक्ट करने के बजाय मैंने सेवाओं को प्रदान करने वाला एक वर्ग बनाया, उदाहरण के लिए, इसे सेवाओं को इंजेक्ट किया जाता है। इस वर्ग को तब व्युत्पन्न वर्गों में अंतःक्षिप्त किया जाता है और बेस क्लास में पास किया जाता है।

व्युत्पन्न वर्ग:

@Component({
    ...
    providers: [ProviderService]
})
export class DerivedComponent extends BaseComponent {
    constructor(protected providerService: ProviderService) {
        super(providerService);
    }
}

आधार वर्ग:

export class BaseComponent {
    constructor(protected providerService: ProviderService) {
        // do something with providerService
    }
}

सेवा प्रदान करने वाला वर्ग:

@Injectable()
export class ProviderService {
    constructor(private _apiService: ApiService, private _authService: AuthService) {
    }
}

3
यहां समस्या यह है कि आप एक "जंक दराज" सेवा बनाने का जोखिम उठाते हैं जो अनिवार्य रूप से इंजेक्टर सेवा के लिए एक प्रॉक्सी है।
kpup

1

एक ऐसी सेवा को इंजेक्ट करने के बजाय जिसमें निर्भरता के रूप में अन्य सभी सेवाएं हैं, जैसे:

class ProviderService {
    constructor(private service1: Service1, private service2: Service2) {}
}

class BaseComponent {
    constructor(protected providerService: ProviderService) {}

    ngOnInit() {
        // Access to all application services with providerService
        this.providerService.service1
    }
}

class DerivedComponent extends BaseComponent {
    ngOnInit() {
        // Access to all application services with providerService
        this.providerService.service1
    }
}

मैं इस अतिरिक्त कदम को छोड़ दूंगा और बस बेसकंपोनेंट में सभी सेवाओं को जोड़ दूंगा, जैसे:

class BaseComponent {
    constructor(protected service1: Service1, protected service2: Service2) {}
}

class DerivedComponent extends BaseComponent {
    ngOnInit() {
        this.service1;
        this.service2;
    }
}

यह तकनीक 2 चीजों को मानती है:

  1. आपकी चिंता पूरी तरह से घटक विरासत से संबंधित है। सबसे अधिक संभावना है, जिस कारण से आप इस प्रश्न पर उतरे हैं वह गैर-सूखी (WET?) कोड की भारी मात्रा के कारण है जिसे आपको प्रत्येक व्युत्पन्न वर्ग में दोहराना होगा। यदि आप अपने सभी घटकों और सेवाओं के लिए एकल प्रविष्टि बिंदु का लाभ चाहते हैं , तो आपको अतिरिक्त कदम उठाने की आवश्यकता होगी।

  2. हर घटक का विस्तार होता है BaseComponent

एक नुकसान यह भी है यदि आप एक व्युत्पन्न वर्ग के निर्माता का उपयोग करने का निर्णय लेते हैं, क्योंकि आपको super()सभी निर्भरताओं में कॉल करने और पास करने की आवश्यकता होगी । हालाँकि मुझे वास्तव में ऐसा उपयोग मामला दिखाई नहीं देता है जिसके लिए constructorइसके उपयोग की आवश्यकता होती है ngOnInit, यह पूरी तरह से संभव है कि इस तरह का उपयोग मामला मौजूद हो।


2
तब आधार वर्ग के पास सभी सेवाओं पर निर्भरता होती है जो उसके किसी भी बच्चे की जरूरत होती है। चाइल्डकम्पोनेंटए को सेवाए की आवश्यकता है? खैर अब ChildComponentB को ServiceA भी मिल गया।
knallfrosch

0

यदि माता-पिता वर्ग को तृतीय पक्ष प्लग-इन से मिला है (और आप स्रोत को बदल नहीं सकते हैं) तो आप यह कर सकते हैं:

import { Injector } from '@angular/core';

export MyComponent extends AbstractComponent {
  constructor(
    protected injector: Injector,
    private anotherService: AnotherService
  ) {
    super(injector.get(MyService));
  }
}

या सबसे बेहतर तरीका (निर्माता में केवल एक पैरामीटर रहें):

import { Injector } from '@angular/core';

export MyComponent extends AbstractComponent {
  private anotherService: AnotherService;

  constructor(
    protected injector: Injector
  ) {
    super(injector.get(MyService));
    this.anotherService = injector.get(AnotherService);
  }
}

0

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


0

केवल हैक

कुछ समय पहले मेरे कुछ ग्राहक दो बीआईजी कोणीय परियोजनाओं को कल (कोणीय v4 को कोणीय v8) में शामिल करना चाहते हैं। प्रोजेक्ट v4 प्रत्येक घटक के लिए बेस व्यू वर्ग का उपयोग करता है और इसमें tr(key)अनुवाद के लिए विधि है (v8 में मैं एनजी-ट्रांसलेशन का उपयोग करता हूं)। इसलिए अनुवाद प्रणाली को बदलने से बचने के लिए और समानांतर में सैकड़ों टेम्पलेट्स (v4 में) या सेटअप 2 अनुवाद प्रणाली को संपादित करें। समानांतर में मैं बदसूरत हैक का उपयोग करता हूं (मुझे इस पर गर्व नहीं है) - AppModuleकक्षा में मैं निम्नलिखित रचनाकार जोड़ता हूं:

export class AppModule { 
    constructor(private injector: Injector) {
        window['UglyHackInjector'] = this.injector;
    }
}

और अब AbstractComponentआप उपयोग कर सकते हैं

export class AbstractComponent {
  private myservice: MyService = null;

  constructor() {
    this.myservice = window['UglyHackInjector'].get(MyService);
  }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.