इसे पूरी तरह से लागू करें। डि-इन-js


सभी को नमस्कार! आज मैं शुद्ध जावास्क्रिप्ट में निर्भरता इंजेक्शन के साथ प्रयोग करने की कोशिश करूंगा जो लोग नहीं जानते कि यह किस तरह का खेल है और इसे कैसे पकाना है, मैं आपको खुद को परिचित करने के लिए आमंत्रित करता हूं। खैर, जो लोग जानते हैं, उनके पास एक महत्वपूर्ण और उपयोगी टिप्पणी लिखने का अवसर होगा। तो, हम चलाई ...


निर्भरता अन्तःक्षेपण


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


बिना DI:


   class Engine {...};
   class ElectroEngine {...};
   class Transmission {...};
   class Chassis {...};
   class TestChassis {...};

   class Car {
        constructor() {
            this.engine = new Engine();
            this.transmission = new Transmission();
            this.chassis = new Chassis();
        }
    }

    class ElectroCar {
        constructor() {
            this.engine = new ElectroEngine();
            this.transmission = new Transmission();
            this.chassis = new Chassis();
        }
    }

   class TestCar {
        constructor() {
            this.engine = new Engine();
            this.transmission = new Transmission();
            this.chassis = new TestChassis ();
        }
    }

    const car = new Car();
    const electroCar = new ElectroCar();
    const testCar = new TestCar();

DI के साथ:


    class Engine{...};
    class ElectroEngine {...};
    class TestEngine {...};

    class Transmission {...};
    class TestTransmission {...};

    class Chassis {...};
    class SportChassis {...};
    class TestChassis {...};

     class Car {
        constructor(engine, transmission, chassis) {
            this.engine = engine;
            this.transmission = transmission;
            this.chassis = chassis;
        }
    }

    const petrolCar = new Car(new Engine(), new Transmission(), new Chassis());
    const sportCar = new Car(new Engine(), new Transmission(), new SportChassis());
    const electroCar = new Car(new ElectroEngine(), new Transmission(), new Chassis());
    const testCar = new Car(new TestEngine(), new TestTransmission(), new TestChassis());

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


DI, Car. . ! — . , "" — .


DI . "" — , , . , ? , . :


class Engine{
   constructor(candles, pistons, oil) {….}
};

class Chassis{
    constructor(doors, hood, trunk) {….}
};

const petrolCar = new Car(
    new Engine(new Candles(), new Pistons(), new Oil() ), 
    new Transmission(…..), 
    new Chassis(new Doors, new Hood(), new Trunk())
);

. , . , , .


Inversion of Control


"" DI- — Inversion of Control (IoC). , — , . DI, IoC , . - , . :


class Engine{...};
class Transmission{...};
class Chassis{…}

class Car {
        constructor(engine: Engine, transmission: Transmission, chassis: Chassis) {}
} 

const car = new Car();

car.engine instanceof Engine; //*true*

— new Car(). — , a .


DI-in-JS


JS. , DI . "".


, , , , . . .


constructor(engine = Engine, transmission = Transmission, chassis = Chassis)

:


constructor(engine: Engine, transmission: Transmission, chassis: Chassis)

, . IoC «» , . , ?
, Reflection. , — .


, JS:


function reflectionMetaInfo(a) { console.log(a); }

reflectionMetaInfo.name ;       // reflectionMetaInfo;
reflectionMetaInfo.length   ;   //1
reflectionMetaInfo.toString();  //function reflectionMeta(a) { console.log(a);}
arguments;                      //Arguments [%value%/]

, toString(). . , , . () , . , .


const constructorSignature =  classFunc
                                 .toString()
                                 .replace(/\s|['"]/g, '')
                                 .replace(/.*(constructor\((?:\w+=\w+?,?)+\)).*/g, '$1')
                                 .match(/\((.*)\)/)[1]
                                 .split(',')
                                 .map(item => item.split('='));

constructorSignature // [ [dep1Name, dep1Value], [dep2Name, dep2Value] …. ]

, , . , , . . IoC — .


:


function Injectable(classFunc, options) {
    const 
        depsRegistry = Injectable.depsRegistry || (Injectable.depsRegistry = {}),
        className = classFunc.name,
        factories = options && options.factories;

    if (factories) {
        Object.keys(factories).forEach(factoryName => {
           depsRegistry[factoryName] = factories[factoryName];
        })
    }

    const depDescriptions = classFunc.toString()
                                .replace(/\s/g, '')
                                .match(/constructor\((.*)[^(]\){/)[1]
                                .replace(/"|'/g, '')
                                .split(',')
                                .map(item => item.split('='));

    const injectableClassFunc = function(...args) {

            const instance = new classFunc(...args);

            depDescriptions.forEach(depDescription => {
                const 
                    depFieldName = depDescription[0],
                    depDesc = depDescription[1];

                if (instance[depFieldName]) return;

                try {
                    instance[depFieldName] = new depsRegistry[depDesc]();
                } catch (err) {
                    instance[depFieldName] = depDesc;
                } 
            });

            return instance;
        }

    return depsRegistry[classFunc.name] = injectableClassFunc;
}

class CustomComponent {
    constructor(name = "Custom Component") {
        this.name = name;
    }
    sayName() {
        alert(this.name);
    }
}

const Button = Injectable(
    class Button extends CustomComponent {
        constructor(name = 'Button') {
            super(name);
        }
    }
)

const Popup = Injectable(
    class Popup extends CustomComponent {
        constructor(
            confirmButton = 'confirmButtonFactory',
            closeButton = Button,
            name = 'NoticePopup'
        ) {
            super(name);
        }
    },
    {
        factories: {
            confirmButtonFactory: function() { return new Button('Confirm Button') }
        }
    }
);

const Panel = Injectable(
    class Panel extends CustomComponent {
        constructor(
            closeButton = 'closeButtonFactory',
            popup = Popup,
            name = 'Head Panel'
        ) {
            super(name);
        }
    },
    {
        factories: {
            closeButtonFactory: function() { return new Button('Close Button') }
        }
    }
);

const customPanel = new Panel();

, , . , . — Injectable, IoC. :


  1. ;
  2. ;
  3. ;
  4. ;
  5. ;

, try-catch .


:


  1. . . , , , - .
  2. . , , . - option.factories, .

.


:


function inject(context, ...deps) {
    const 
        depsRegistry = inject.depsRegistry || (inject.depsRegistry = {}),
        className = context.constructor.name;

    let depsNames = depsRegistry[className]; 

    if (!depsNames) {
        depsNames 
            = depsRegistry[className] 
            = context.constructor
                .toString()
                .replace(/\s|['"]/g, '')
                .replace(/.*(inject\((?:\w+,?)+\)).*/g, '$1')
                .replace(/inject\((.*)\)/, '$1')
                .split(',');

       depsNames.shift();
    } 

    deps.forEach((dep, index) => {
        const depName = depsNames[index];
        try {
            context[depName] = new dep();
        } catch (err) {
            context[depName] = dep;
        }
    });

    return context;
}

class Component {

    constructor(name = 'Component') {

        inject(this, name);
    }

    showName() {
        alert(this.name);
    }
}

class Button extends Component {

    constructor(name = 'Component') {
        super();
        inject(this, name);
    }

    disable() {
        alert(`button ${this.name} is disabled`);
    }

    enable() {
        alert(`button ${this.name} is enabled`);
    }
}

class PopupComponent extends Component {

    show() {
        alert(`show ${this.name} popup`);
    }

    hide() {
         alert(`hide ${this.name} popup`);
    }
}

class TopPopup extends PopupComponent {
    constructor(
        popupButton = Button,
        name = 'Top Popup'
    ) {
        super();
        inject(this, popupButton, name);
        this.popupButton.name = 'TopPopup Button';
    }
}

class BottomPopup extends PopupComponent {
    constructor(
        popupButton = function() { return new Button('BottomPopup Button') },
        name = 'Bottom Popup'
    ) {
        super();
        inject(this, popupButton, name);
    }
}

class Panel extends Component {
    constructor(
        name = 'Panel',
        popup1 = TopPopup,
        popup2 = BottomPopup,
        buttonClose = function() { return new Button('Close Button') }
    ) {
        super();
        inject(this, name, popup1, popup2, buttonClose);
    }
}

const panel = new Panel('Panel 1');

. . . , inject, .


inject :


  1. (this)
  2. context.constructor.
  3. .
  4. , — inject.depsRegistry
  5. context

— . — Inject , . , .


:


class Injectable {
    constructor(...dependensies) {

       const 
            depsRegistry = Injectable.depsRegistry || (Injectable.depsRegistry = {}),
            className = this.constructor.name;

       let depNames = depsRegistry[className];

       if (!depNames) {
           depNames = this.constructor
                            .toString()
                            .replace(/\s|['"]/g, '')
                            .replace(/.*(super\((?:\w+,?)+\)).*/g, '$1')
                            .replace(/super\((.*)\)/, '$1')
                            .split(',');
       }

       dependensies.forEach((dependense, index) => {
          const depName = depNames[index];
          try {
            this[depName] = new dependense();
          } catch (err) {
            this[depName] = dependense;
          }
       })                     
    }
}

class Component extends Injectable {
    showName() {
        alert(this.name);
    }
}

class Button extends Component {
    constructor(name = 'button') {
        super(name);
    }

    disable() {
        alert(`button ${this.name} is disabled`);
    }

    enable() {
        alert(`button ${this.name} is enabled`);
    }
}

class PopupComponent extends Component {
    show() {
        alert(`show ${this.name} popup`);
    }

    hide() {
         alert(`hide ${this.name} popup`);
    }
}

class TopPopup extends PopupComponent {
    constructor(
        popupButton = Button,
        name = 'Top Popup'
    ) {
        super(popupButton, name); 
        this.popupButton.name = 'TopPopup Button';
    }
}

class BottomPopup extends PopupComponent {
    constructor(
        popupButton = function() { return new Button('BottomPopup Button') },
        name = 'Bottom Popup'
    ) {
        super(popupButton, name);
    }
}

class Panel extends Component {
    constructor(
        name = 'Panel',
        popup1 = TopPopup,
        popup2 = BottomPopup,
        buttonClose = function() { return new Button('Close Button') }
    ) {
        super(name, popup1, popup2, buttonClose);
    }
}

const panel = new Panel('Panel 1');

Injectable. super , .


:



मेरी राय में, अंतिम - तीसरा विकल्प DI & IoC परिभाषाओं के लिए सबसे उपयुक्त है, ताकि कार्यान्वयन तंत्र क्लाइंट कोड से सबसे छिपा हो।
अच्छा यही सब है। मुझे उम्मीद है कि यह दिलचस्प और जानकारीपूर्ण था। अलविदा सबको!


All Articles