নোড.জেএস-তে চক্রীয় নির্ভরতাগুলি কীভাবে মোকাবেলা করতে হয়


162

আমি ইদানীং নোডেজের সাথে কাজ করে যাচ্ছি এবং এখনও মডিউল সিস্টেমটি ধরে ফেলছি তাই ক্ষমা চাইছি যদি এটি একটি সুস্পষ্ট প্রশ্ন। আমি নীচের মত মোটামুটি কোড চাই:

a.js (নোড দিয়ে চালিত মূল ফাইল)

var ClassB = require("./b");

var ClassA = function() {
    this.thing = new ClassB();
    this.property = 5;
}

var a = new ClassA();

module.exports = a;

b.js

var a = require("./a");

var ClassB = function() {
}

ClassB.prototype.doSomethingLater() {
    util.log(a.property);
}

module.exports = ClassB;

আমার সমস্যাটি মনে হচ্ছে যে আমি ক্লাসবি এর উদাহরণ থেকে ক্লাসএ এর উদাহরণটি অ্যাক্সেস করতে পারছি না।

আমি যা চাই তা অর্জনের জন্য মডিউলগুলি গঠনের সঠিক / আরও ভাল উপায় আছে? মডিউলগুলির মধ্যে ভেরিয়েবলগুলি ভাগ করার আরও ভাল উপায় কি নেই?


আমি আপনাকে পরামর্শ দিচ্ছি যে ক্যোয়ারী বিভাজন, পর্যবেক্ষণযোগ্য প্যাটার্ন এবং তারপরে সিএস ছেলেরা ম্যানেজারকে কী বলে - যা পর্যবেক্ষণযোগ্য প্যাটার্নের জন্য মূলত একটি মোড়ক command
দেওয়ালওয়াল্ড

উত্তর:


86

যদিও নোড.জেএস বিজ্ঞপ্তি requireনির্ভরতাগুলি মঞ্জুরি দেয় , যেমনটি আপনি পেয়েছেন এটি বেশ অগোছালো হতে পারে এবং আপনার কোডটির প্রয়োজন না হওয়ার জন্য পুনর্গঠন করা আপনি আরও ভাল। হতে পারে একটি তৃতীয় শ্রেণীর তৈরি করুন যা আপনার প্রয়োজন অনুসারে অন্যান্য দুটি ব্যবহার করে।


6
+1 এটি সঠিক উত্তর। বিজ্ঞপ্তি নির্ভরতা কোড গন্ধ। যদি এ এবং বি সর্বদা একসাথে ব্যবহার করা হয় তবে তারা কার্যকরভাবে একটি একক মডিউল, তাই তাদের মার্জ করুন। বা নির্ভরতা ভাঙার একটি উপায় সন্ধান করুন; সম্ভবত এটি একটি সম্মিলিত প্যাটার্ন
জেমস

94
সর্বদা না। ডাটাবেস মডেলগুলিতে, উদাহরণস্বরূপ, আমার কাছে A এবং B মডেল থাকলে মডেল এআই মডেল বি (যেমন অপারেশনগুলিতে যোগদানের জন্য) উল্লেখ করতে পারে, এবং তদ্বিপরীত। অতএব, "আবশ্যক" ফাংশনটি ব্যবহারের আগে বেশ কয়েকটি এ এবং বি বৈশিষ্ট্য (যা অন্যান্য মডিউলগুলির উপর নির্ভর করে না) রফতানি করা আরও ভাল উত্তর হতে পারে।
জোও ব্রুনো আবু হাতেম ডি লিজ

11
আমি কোড গন্ধ হিসাবে বিজ্ঞপ্তি নির্ভরতা দেখতে পাচ্ছি না। আমি এমন একটি সিস্টেম বিকাশ করছি যেখানে কয়েকটি ক্ষেত্রে এটির প্রয়োজন হয়। উদাহরণস্বরূপ, মডেলিং দল এবং ব্যবহারকারীরা যেখানে ব্যবহারকারীরা অনেক দলের অন্তর্ভুক্ত থাকতে পারেন। সুতরাং, এটি এমন নয় যে আমার মডেলিংয়ের সাথে কিছু ভুল হয়েছে। স্পষ্টতই, আমি দুটি সত্তার মধ্যে বিজ্ঞপ্তি নির্ভরতা এড়াতে আমার কোডটি রিফ্যাক্টর করতে পারি, তবে এটি ডোমেন মডেলের সর্বাধিক বিশুদ্ধ রূপ নয়, তাই আমি এটি করব না।
আলেকজান্ডার মার্টিনি

1
তারপরে যখন আমার প্রয়োজন হয় তখন নির্ভরতা ইনজেকশন করা উচিত, আপনি কি এটি বোঝাতে চেয়েছিলেন? চক্রীয় সমস্যার সাথে দুটি নির্ভরতার মধ্যে মিথস্ক্রিয়া নিয়ন্ত্রণ করতে তৃতীয়টি ব্যবহার করছেন?
giovannipds

2
এটি অগোছালো নয় .. কেউ আইআইএল ফাইলের কোডের বই এড়ানোর জন্য কোনও ফাইল ব্রেক করতে পারে। নোডের পরামর্শ exports = {}অনুসারে আপনার কোডের শীর্ষে আপনার কোডের শেষে একটি যুক্ত করা উচিত exports = yourData। এই অনুশীলনের সাহায্যে আপনি বিজ্ঞপ্তি নির্ভরতা থেকে প্রায় সমস্ত ত্রুটি এড়াতে পারবেন।
পুরোহিত

178

বৈশিষ্ট্যগুলি module.exportsসম্পূর্ণরূপে প্রতিস্থাপনের পরিবর্তে সেট করার চেষ্টা করুন । যেমন, module.exports.instance = new ClassA()a.js, module.exports.ClassB = ClassBb.js। আপনি যখন বিজ্ঞপ্তি মডিউল নির্ভরতা তৈরি করেন, প্রয়োজনীয় মডিউলটি প্রয়োজনীয় মডিউল module.exportsথেকে অসম্পূর্ণ একটি রেফারেন্স পাবেন , যা আপনি অন্যান্য বৈশিষ্ট্যগুলি পরে যুক্ত করতে পারেন, তবে আপনি যখন সম্পূর্ণ সেট করেন module.exports, আপনি আসলে একটি নতুন অবজেক্ট তৈরি করেন যা প্রয়োজনীয় মডিউলটির কোনও নেই no অ্যাক্সেস উপায়।


6
এটি সমস্ত সত্য হতে পারে, তবে আমি বলব যে এখনও বিজ্ঞপ্তি নির্ভরতা এড়ান। অসম্পূর্ণভাবে লোড হওয়া মডিউলগুলির সাথে মোকাবিলা করার জন্য বিশেষ ব্যবস্থা করা এটি আপনার ভবিষ্যতের কোনও সমস্যা তৈরি করতে চাইবে না। এই উত্তরটি অসম্পূর্ণভাবে লোড হওয়া মডিউলগুলি কীভাবে মোকাবেলা করতে পারে তার একটি সমাধানের প্রস্তাব দেয় ... আমি মনে করি এটি ভাল ধারণা নয়।
আলেকজান্ডার মিলস

1
ক্লাসের কনস্ট্রাক্টরকে module.exportsপুরোপুরি প্রতিস্থাপন না করে কীভাবে আপনি অন্য ক্লাসকে ক্লাসের উদাহরণ 'নির্মাণ' করার অনুমতি দেবেন?
টিম ভিজি

1
আমি মনে করি না আপনি পারবেন। আপনার মডিউলটি ইতিমধ্যে আমদানি করেছে এমন মডিউলগুলি সে পরিবর্তনটি দেখতে পাবে না
ল্যানজ

52

[সম্পাদনা] এটি ২০১৫ নয় এবং বেশিরভাগ লাইব্রেরি (অর্থাত্ এক্সপ্রেস) আরও ভাল নিদর্শন সহ আপডেট করেছে যাতে বিজ্ঞপ্তি নির্ভরতা আর প্রয়োজন হয় না। আমি কেবল তাদের ব্যবহার না করার পরামর্শ দিচ্ছি


আমি জানি আমি এখানে একটি পুরানো উত্তর খনন করছি ... এখানে সমস্যাটি হল মডিউল.ইপোর্টগুলি আপনার ক্লাসবি লাগানোর পরে সংজ্ঞায়িত করা হয় । (যা জনিএইচকে-র লিঙ্কটি দেখায়) বিজ্ঞপ্তি নির্ভরতা নোডে দুর্দান্ত কাজ করে, তারা কেবলমাত্র সিঙ্ক্রোনালি সংজ্ঞাযুক্ত। সঠিকভাবে ব্যবহার করা হলে, তারা আসলে প্রচুর নোডের সমস্যাগুলি সমাধান করে (যেমন appঅন্যান্য ফাইলগুলি থেকে এক্সপ্রেস.জেজেস অ্যাক্সেস করা)

বিজ্ঞপ্তি নির্ভরতা সহ কোনও ফাইলের প্রয়োজন হওয়ার আগে আপনার প্রয়োজনীয় রফতানিগুলি সংজ্ঞায়িত করা হয়েছে তা নিশ্চিত করুন ।

এটি ভঙ্গ হবে:

var ClassA = function(){};
var ClassB = require('classB'); //will require ClassA, which has no exports yet

module.exports = ClassA;

এটি কাজ করবে:

var ClassA = module.exports = function(){};
var ClassB = require('classB');

appঅন্যান্য ফাইলে এক্সপ্রেস.জেএস অ্যাক্সেস করার জন্য আমি এই প্যাটার্নটি সর্বদা ব্যবহার করি :

var express = require('express');
var app = module.exports = express();
// load in other dependencies, which can now require this file and use app

2
প্যাটার্নটি ভাগ করে নেওয়ার জন্য ধন্যবাদ এবং তারপরে রফতানির সময় আপনি কীভাবে এই প্যাটার্নটি সাধারণত ব্যবহার করেন তা ভাগ করে নেওয়ার জন্যapp = express()
user566245

34

কখনও কখনও এটি তৃতীয় শ্রেণীর (যেমন জনিএইচকে পরামর্শ দেয়) প্রবর্তন করা সত্যিই কৃত্রিম হয়, সুতরাং ইয়ানজ ছাড়াও: আপনি যদি মডিউল.এক্সপোর্টগুলি প্রতিস্থাপন করতে চান তবে উদাহরণস্বরূপ যদি আপনি একটি বর্গ তৈরি করে থাকেন (যেমন বি.জেএস ফাইলের মতো ফাইল) উপরের উদাহরণ), এটিও সম্ভব, কেবল নিশ্চিত করুন যে বিজ্ঞপ্তিটি শুরু হওয়া ফাইলটিতে 'মডিউল.এক্সপোর্টস = ...' বিবৃতি প্রয়োজনীয় বিবৃতি দেওয়ার আগে ঘটে।

a.js (নোড দিয়ে চালিত মূল ফাইল)

var ClassB = require("./b");

var ClassA = function() {
    this.thing = new ClassB();
    this.property = 5;
}

var a = new ClassA();

module.exports = a;

b.js

var ClassB = function() {
}

ClassB.prototype.doSomethingLater() {
    util.log(a.property);
}

module.exports = ClassB;

var a = require("./a"); // <------ this is the only necessary change

ধন্যবাদ কোয়েন, আমি কখনই বুঝতে পারি নি যে মডিউল.এক্সপোর্টগুলি বৃত্তাকার নির্ভরতার উপর প্রভাব ফেলে।
লরেন্ট পেরিন

এটি মঙ্গুজ (মঙ্গোডিবি) মডেলগুলির সাথে বিশেষভাবে কার্যকর; ব্লগপোস্ট মডেলটিতে মন্তব্যগুলির উল্লেখ সহ একটি অ্যারে থাকাকালীন আমাকে সমস্যা সমাধানে সহায়তা করে এবং প্রতিটি মন্তব্য মডেলটিতে ব্লগপোস্টের রেফারেন্স থাকে।
ওলেগ জেরেভেনেই

14

সমাধানটি অন্য কোনও নিয়ামকের প্রয়োজনের আগে আপনার রফতানি অবজেক্টকে 'ফরওয়ার্ড ডিক্লেয়ার' করা। সুতরাং আপনি যদি আপনার সমস্ত মডিউলগুলি এর মতো করে গঠন করেন এবং আপনি এর মতো কোনও সমস্যার মুখোমুখি হন না:

// Module exports forward declaration:
module.exports = {

};

// Controllers:
var other_module = require('./other_module');

// Functions:
var foo = function () {

};

// Module exports injects:
module.exports.foo = foo;

3
আসলে, এটি আমাকে exports.foo = function() {...}পরিবর্তে কেবল ব্যবহার করতে পরিচালিত করেছিল । কৌতুক অবশ্যই করেছে। ধন্যবাদ!
জানানো

আপনি এখানে কী প্রস্তাব দিচ্ছেন তা আমি নিশ্চিত নই। module.exportsডিফল্ট হিসাবে ইতিমধ্যে একটি সরল অবজেক্ট, সুতরাং আপনার "ফরোয়ার্ড ঘোষণা" লাইন অপ্রয়োজনীয়।
ZachB

7

এমন একটি সমাধান যা অপ্রত্যাশিত পরিবর্তনের module.exportsপরিবর্তে সর্বনিম্ন পরিবর্তন প্রয়োজন require

a.js - অ্যাপ এন্ট্রি পয়েন্ট এবং মডিউল যা বি.জেএস * থেকে পদ্ধতি ব্যবহার করে

_ = require('underscore'); //underscore provides extend() for shallow extend
b = require('./b'); //module `a` uses module `b`
_.extend(module.exports, {
    do: function () {
        console.log('doing a');
    }
});
b.do();//call `b.do()` which in turn will circularly call `a.do()`

b.js - মডিউল যা পদ্ধতি ব্যবহার করে a.js থেকে করে

_ = require('underscore');
a = require('./a');

_.extend(module.exports, {
    do: function(){
        console.log('doing b');
        a.do();//Call `b.do()` from `a.do()` when `a` just initalized 
    }
})

এটি কাজ করবে এবং উত্পাদন করবে:

doing b
doing a

যদিও এই কোডটি কাজ করবে না:

a.js

b = require('./b');
module.exports = {
    do: function () {
        console.log('doing a');
    }
};
b.do();

b.js

a = require('./a');
module.exports = {
    do: function () {
        console.log('doing b');
    }
};
a.do();

আউটপুট:

node a.js
b.js:7
a.do();
    ^    
TypeError: a.do is not a function

4
আপনার যদি না থাকে underscoreতবে ES6 এর উত্তরে Object.assign()একই কাজটি করতে _.extend()পারে।
জোয়েটউইডল

5

অলস প্রয়োজন যখন কেবল যখন আপনার প্রয়োজন হয়? সুতরাং আপনার বি.জেএস নীচের মত দেখাচ্ছে

var ClassB = function() {
}
ClassB.prototype.doSomethingLater() {
    var a = require("./a");    //a.js has finished by now
    util.log(a.property);
}
module.exports = ClassB;

অবশ্যই সমস্ত প্রয়োজনীয় বিবৃতি ফাইলের উপরে রাখাই ভাল অনুশীলন। তবে এমন কিছু অনুষ্ঠান রয়েছে, যেখানে আমি অন্য কোনও সম্পর্কযুক্ত মডিউল থেকে কিছু বাছাই করার জন্য নিজেকে ক্ষমা করি। এটিকে হ্যাক বলুন, তবে কখনও কখনও এটি আরও নির্ভরতা প্রবর্তন করা বা একটি অতিরিক্ত মডিউল যুক্ত করা বা নতুন কাঠামো যুক্ত করার (ইভেন্টেমিটার ইত্যাদির) চেয়ে ভাল is


এবং কখনও কখনও এটি কোনও পিতামাতার সাথে রেফারেন্স বজায় রাখা শিশুদের সাথে গাছের ডেটা কাঠামোর সাথে কাজ করার সময় সমালোচনা করে। বখশিশের জন্য ধন্যবাদ.
রবার্ট অসলারের

5

লোকেদের করা একটি অন্য পদ্ধতিটি প্রথম লাইনে রফতানি করা এবং এটিকে স্থানীয় ভেরিয়েবল হিসাবে সংরক্ষণ করা হয়:

let self = module.exports = {};

const a = require('./a');

// Exporting the necessary functions
self.func = function() { ... }

আমি এই পদ্ধতিটি ব্যবহার করার প্রবণতা রাখি, আপনি কি এর কোনও ডাউনসাইড সম্পর্কে জানেন?


আপনি বরং করতে পারেন module.exports.func1 = ,module.exports.func2 =
অশ্বানী আগরওয়াল

4

আপনি এটি সহজে সমাধান করতে পারেন: মডিউল যেখানে আপনি মডিউল ব্যবহার করেন সেগুলির জন্য অন্য কিছু প্রয়োজন হওয়ার আগে কেবল আপনার ডেটা রফতানি করুন:

classA.js

class ClassA {

    constructor(){
        ClassB.someMethod();
        ClassB.anotherMethod();
    };

    static someMethod () {
        console.log( 'Class A Doing someMethod' );
    };

    static anotherMethod () {
        console.log( 'Class A Doing anotherMethod' );
    };

};

module.exports = ClassA;
var ClassB = require( "./classB.js" );

let classX = new ClassA();

classB.js

class ClassB {

    constructor(){
        ClassA.someMethod();
        ClassA.anotherMethod();
    };

    static someMethod () {
        console.log( 'Class B Doing someMethod' );
    };

    static anotherMethod () {
        console.log( 'Class A Doing anotherMethod' );
    };

};

module.exports = ClassB;
var ClassA = require( "./classA.js" );

let classX = new ClassB();

3

ল্যাঞ্জ এবং সেক্টেটের উত্তরের মতো, আমি নিম্নলিখিত প্যাটার্নটি ব্যবহার করছি:

module.exports = Object.assign(module.exports, {
    firstMember: ___,
    secondMember: ___,
});

Object.assign()কপি মধ্যে সদস্যদের exportsবস্তু ইতিমধ্যে অন্যান্য মডিউল দেওয়া হয়েছে।

=নিয়োগ কথাটি অপ্রয়োজনীয়, কারণ এটা শুধু সেটিং করা হয় module.exportsনিজেই, কিন্তু আমি এটা ব্যবহার করছি কারণ এটি সাহায্য করে আমার আইডিই (WebStorm) চিনতে যে firstMemberএই মডিউল একটি সম্পত্তি, তাই "to go -> ঘোষণাপত্র" (উঠলে Cmd-বি) এবং অন্যান্য সরঞ্জামগুলি অন্যান্য ফাইল থেকে কাজ করবে।

এই নিদর্শনটি খুব সুন্দর নয়, সুতরাং যখন আমি একটি চক্রীয় নির্ভরতা ইস্যুটি সমাধান করা দরকার তখনই আমি এটি ব্যবহার করি।


2

এখানে একটি দ্রুত কাজের সন্ধান পেয়েছি যা আমি পুরো ব্যবহারটি পেয়েছি।

'A.js' ফাইলটিতে

let B;
class A{
  constructor(){
    process.nextTick(()=>{
      B = require('./b')
    })
  } 
}
module.exports = new A();

'B.js' ফাইলটিতে নিম্নলিখিত লিখুন

let A;
class B{
  constructor(){
    process.nextTick(()=>{
      A = require('./a')
    })
  } 
}
module.exports = new B();

ইভেন্টের লুপ ক্লাসগুলির পরবর্তী পুনরাবৃত্তিতে এই পদ্ধতিটি সঠিকভাবে সংজ্ঞায়িত করা হবে এবং যাদের বিবৃতি প্রয়োজন তা প্রত্যাশা অনুযায়ী কাজ করবে।


1

আসলে আমার সাথে আমার নির্ভরতা প্রয়োজন

 var a = null;
 process.nextTick(()=>a=require("./a")); //Circular reference!

সুন্দর নয়, তবে এটি কাজ করে। এটি b.js পরিবর্তনের চেয়ে আরও বোধগম্য এবং সৎ (উদাহরণস্বরূপ কেবলমাত্র মডিউলগুলি বাড়িয়ে দেওয়া হয় ex এক্সপোর্ট), যা অন্যথায় যেমন সঠিক।


এই পৃষ্ঠার সমস্ত সমাধানগুলির মধ্যে, এটিই কেবলমাত্র আমার সমস্যা সমাধান করেছে। আমি প্রতিটি ঘুরে ফিরে চেষ্টা করেছিলাম।
জো ল্যাপ

0

এটি এড়ানোর এক উপায় হ'ল অন্য কোনও ফাইলের প্রয়োজন না হয় কেবল এটি অন্য কোনও ফাইলে আপনার যা প্রয়োজন তা কোনও ফাংশনের আর্গুমেন্ট হিসাবে পাস করুন। এভাবে বিজ্ঞপ্তি নির্ভরতা কখনই উত্থিত হবে না।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.