Tôi có cần tiêm phụ thuộc vào NodeJS không, hay làm thế nào để đối phó với lỗi?


219

Tôi hiện đang tạo một số dự án thử nghiệm với nodejs. Tôi đã lập trình rất nhiều ứng dụng web Java EE với Spring và đánh giá cao sự dễ dàng của việc tiêm phụ thuộc vào đó.

Bây giờ tôi tò mò: Làm thế nào để tôi thực hiện tiêm phụ thuộc với nút? Hoặc: Tôi thậm chí có cần nó không? Có một khái niệm thay thế, bởi vì phong cách lập trình là khác nhau?

Tôi đang nói về những điều đơn giản, như chia sẻ một đối tượng kết nối cơ sở dữ liệu, cho đến nay, nhưng tôi chưa tìm thấy giải pháp nào thỏa mãn tôi.


1
Nếu bạn quyết định sử dụng DI, OpenTable gần đây đã mở một thư viện cho nó: github.com/opentable/spur-ioc tôi đã sử dụng nó (tôi làm việc ở đó) và có thể nói nó khá đơn giản và tuyệt vời để thử nghiệm.
tybro0103 18/03/2015

Câu trả lời:


107

Nói tóm lại, bạn không cần một thùng chứa phụ thuộc hoặc bộ định vị dịch vụ như bạn làm trong C # / Java. Vì Node.js, tận dụng module pattern, không cần thiết phải thực hiện hàm tạo hoặc hàm thuộc tính. Mặc dù bạn vẫn có thể.

Điều tuyệt vời về JS là bạn có thể sửa đổi bất cứ thứ gì để đạt được những gì bạn muốn. Điều này có ích khi thử nghiệm.

Kìa ví dụ rất khập khiễng của tôi.

MyClass.js:

var fs = require('fs');

MyClass.prototype.errorFileExists = function(dir) {
    var dirsOrFiles = fs.readdirSync(dir);
    for (var d in dirsOrFiles) {
        if (d === 'error.txt') return true;
    }
    return false;
};

MyClass.test.js:

describe('MyClass', function(){
    it('should return an error if error.txt is found in the directory', function(done){
        var mc = new MyClass();
        assert(mc.errorFileExists('/tmp/mydir')); //true
    });
});

Lưu ý làm thế nào MyClassphụ thuộc vào các fsmô-đun? Như @ShatyemShekhar đã đề cập, bạn thực sự có thể thực hiện xây dựng hoặc tiêm thuộc tính như trong các ngôn ngữ khác. Nhưng nó không cần thiết trong Javascript.

Trong trường hợp này, bạn có thể làm hai điều.

Bạn có thể ngắt fs.readdirSyncphương thức hoặc bạn có thể trả về một mô-đun hoàn toàn khác khi bạn gọi require.

Cách 1:

var oldmethod = fs.readdirSync;
fs.readdirSync = function(dir) { 
    return ['somefile.txt', 'error.txt', 'anotherfile.txt']; 
};

*** PERFORM TEST ***
*** RESTORE METHOD AFTER TEST ****
fs.readddirSync = oldmethod;

Cách 2:

var oldrequire = require
require = function(module) {
    if (module === 'fs') {
        return {
            readdirSync: function(dir) { 
                return ['somefile.txt', 'error.txt', 'anotherfile.txt']; 
            };
        };
    } else
        return oldrequire(module);

}

Chìa khóa là tận dụng sức mạnh của Node.js và Javascript. Lưu ý, tôi là một anh chàng CoffeeScript, vì vậy cú pháp JS của tôi có thể không chính xác ở đâu đó. Ngoài ra, tôi không nói rằng đây là cách tốt nhất, nhưng nó là một cách. Các chuyên gia về Javascript có thể hòa nhập với các giải pháp khác.

Cập nhật:

Điều này sẽ giải quyết câu hỏi cụ thể của bạn về kết nối cơ sở dữ liệu. Tôi sẽ tạo một mô-đun riêng để bạn đóng gói logic kết nối cơ sở dữ liệu của bạn. Một cái gì đó như thế này:

MyDbConnection.js: (hãy chắc chắn chọn một tên tốt hơn)

var db = require('whichever_db_vendor_i_use');

module.exports.fetchConnection() = function() {
    //logic to test connection

    //do I want to connection pool?

    //do I need only one connection throughout the lifecyle of my application?

    return db.createConnection(port, host, databasename); //<--- values typically from a config file    
}

Sau đó, bất kỳ mô-đun nào cần kết nối cơ sở dữ liệu sẽ chỉ bao gồm MyDbConnectionmô-đun của bạn .

SuperCoolWebApp.js:

var dbCon = require('./lib/mydbconnection'); //wherever the file is stored

//now do something with the connection
var connection = dbCon.fetchConnection(); //mydbconnection.js is responsible for pooling, reusing, whatever your app use case is

//come TEST time of SuperCoolWebApp, you can set the require or return whatever you want, or, like I said, use an actual connection to a TEST database. 

Đừng làm theo ví dụ này nguyên văn. Đó là một ví dụ khập khiễng khi cố gắng truyền đạt rằng bạn tận dụng modulemô hình để quản lý các phụ thuộc của mình. Hy vọng điều này sẽ giúp một chút nữa.


42
Điều này đúng với việc thử nghiệm, nhưng DI có những lợi ích khác; bằng cách sử dụng DI, bạn có thể lập trình đến một giao diện, không phải triển khai.
moteutsch

3
@moteutsch Không chắc tại sao bạn lại vì JS không có khái niệm giao diện như hầu hết các ngôn ngữ tĩnh. Tất cả những gì bạn thực sự có là các triển khai, ngay cả khi bạn muốn sử dụng một số "giao diện" được thỏa thuận trước.
JP Richardson

16
@JPRichardson Làm cách nào tôi có thể viết một thành phần sử dụng bộ ghi mà không phụ thuộc vào bất kỳ một thư viện nào? Nếu tôi require('my_logger_library'), những người sử dụng thành phần của tôi sẽ phải ghi đè yêu cầu sử dụng thư viện của riêng họ. Thay vào đó, tôi có thể cho phép mọi người chuyển một cuộc gọi lại kết thúc việc thực hiện logger vào phương thức "constructor" hoặc "init" của các thành phần. Đó là mục đích của DI.
vi trần

4
Kể từ giữa năm 2014 - npmjs.org/package/proxyquire khiến việc loại bỏ "yêu cầu" trở nên tầm thường.
arcseldon

4
Tôi không nhận được nó, thay thế yêu cầu trong một mô-đun không thay thế nó trong một mô-đun khác. Nếu tôi đặt yêu cầu cho một chức năng trong thử nghiệm của mình và sau đó yêu cầu mô-đun được kiểm tra thì các câu lệnh yêu cầu trong đối tượng cần kiểm tra không sử dụng chức năng được đặt trong mô-đun thử nghiệm. Làm thế nào để tiêm phụ thuộc này?
HMR

72

requirenhững cách quản lý phụ thuộc vào Node.js và chắc chắn nó là trực quan và hiệu quả, nhưng nó cũng có những hạn chế của nó.

Lời khuyên của tôi là hãy xem một số bộ chứa Dependency Injection hiện có để Node.js có ý tưởng về ưu / nhược điểm của chúng. Một số trong số họ là:

Chỉ để một vài tên.

Bây giờ câu hỏi thực sự là, bạn có thể đạt được gì với bộ chứa Node.js DI, so với đơn giản require?

Ưu điểm:

  • khả năng kiểm tra tốt hơn: các mô-đun chấp nhận phụ thuộc của chúng làm đầu vào
  • Inversion of Control: quyết định cách nối dây các mô-đun của bạn mà không cần chạm vào mã chính của ứng dụng.
  • một thuật toán tùy biến để giải quyết các mô-đun: các phụ thuộc có các định danh "ảo", thông thường chúng không bị ràng buộc với một đường dẫn trên hệ thống tệp.
  • Khả năng mở rộng tốt hơn: được kích hoạt bởi IoC và số nhận dạng "ảo".
  • Những thứ ưa thích khác có thể:
    • Khởi tạo không đồng bộ
    • Quản lý vòng đời mô-đun
    • Khả năng mở rộng của chính container DI
    • Có thể dễ dàng thực hiện trừu tượng cấp cao hơn (ví dụ AOP)

Nhược điểm:

  • Khác với "trải nghiệm" của Node.js: không sử dụng require chắc chắn cảm giác như bạn đang đi chệch khỏi lối suy nghĩ của Node.
  • Mối quan hệ giữa một phụ thuộc và việc thực hiện nó không phải lúc nào cũng rõ ràng. Một phụ thuộc có thể được giải quyết trong thời gian chạy và bị ảnh hưởng bởi các tham số khác nhau. Mã trở nên khó hiểu và gỡ lỗi hơn
  • Thời gian khởi động chậm hơn
  • Trưởng thành (tại thời điểm này): không có giải pháp hiện tại nào thực sự phổ biến tại thời điểm này, vì vậy không có nhiều hướng dẫn, không có hệ sinh thái, không được thử nghiệm chiến đấu.
  • Một số bộ chứa DI sẽ không chơi tốt với các gói mô-đun như Browserify và Webpack.

Như với bất cứ điều gì liên quan đến phát triển phần mềm, việc lựa chọn giữa DI hoặc requirephụ thuộc vào yêu cầu của bạn, độ phức tạp hệ thống và phong cách lập trình của bạn.


3
Bạn có nghĩ rằng tình hình đã thay đổi đáng kể kể từ '09?
Juho Vepsäläinen

13
Bạn có nghĩa là từ 10 ngày trước? :)
Mario

2
Không. Ngày 9 tháng 12 ... Nên đã biết.
Juho Vepsäläinen

4
Tôi "triển khai" DI bằng cách sử dụng loại mô-đun module.exports = function (deps) {}. Vâng, nó hoạt động, nhưng nó không hoàn toàn lý tưởng.
Juho Vepsäläinen

3
các mô-đun chấp nhận sự phụ thuộc của chúng vì đầu vàoPhụ thuộc không phải là âm thanh rõ ràng đối với tôi như một mâu thuẫn.
Anton Rudeshko

53

Tôi biết chủ đề này khá cũ ở thời điểm này, nhưng tôi đoán rằng tôi đã đồng ý với suy nghĩ của mình về điều này. TL; DR là do tính chất động, không được kiểm soát của JavaScript, bạn thực sự có thể làm được khá nhiều việc mà không cần dùng đến mẫu tiêm phụ thuộc (DI) hoặc sử dụng khung DI. Tuy nhiên, khi một ứng dụng phát triển lớn hơn và phức tạp hơn, DI chắc chắn có thể giúp duy trì mã của bạn.

DI trong C #

Để hiểu lý do tại sao DI không phải là một nhu cầu lớn trong JavaScript, thật hữu ích khi xem xét một ngôn ngữ được gõ mạnh như C #. (Xin lỗi những người không biết C #, nhưng nó cũng đủ dễ để theo dõi.) Giả sử chúng tôi có một ứng dụng mô tả một chiếc xe và còi của nó. Bạn sẽ định nghĩa hai lớp:

class Horn
{
    public void Honk()
    {
        Console.WriteLine("beep!");
    }
}

class Car
{
    private Horn horn;

    public Car()
    {
        this.horn = new Horn();
    }

    public void HonkHorn()
    {
        this.horn.Honk();
    }
}

class Program
{
    static void Main()
    {
        var car = new Car();
        car.HonkHorn();
    }
}

Có vài vấn đề với việc viết mã theo cách này.

  1. Các Carlớp học được kết hợp chặt chẽ với việc thực hiện cụ thể của sừng trong Hornlớp. Nếu chúng ta muốn thay đổi loại còi được sử dụng bởi ô tô, chúng ta phải sửa đổi Carlớp mặc dù cách sử dụng còi không thay đổi. Điều này cũng làm cho việc kiểm tra trở nên khó khăn vì chúng ta không thể kiểm tra Carlớp một cách tách biệt với sự phụ thuộc của nó, Hornlớp.
  2. Các Car lớp chịu trách nhiệm về vòng đời của Hornlớp. Trong một ví dụ đơn giản như thế này không phải là vấn đề lớn, nhưng trong các ứng dụng thực tế, phụ thuộc sẽ có phụ thuộc, sẽ có phụ thuộc, v.v.Car Lớp sẽ cần có trách nhiệm tạo toàn bộ cây phụ thuộc của nó. Điều này không chỉ phức tạp và lặp đi lặp lại mà còn vi phạm "trách nhiệm duy nhất" của lớp. Nó nên tập trung vào việc là một chiếc xe, không tạo ra các trường hợp.
  3. Không có cách nào để sử dụng lại các trường hợp phụ thuộc tương tự. Một lần nữa, điều này không quan trọng trong ứng dụng đồ chơi này, nhưng hãy xem xét kết nối cơ sở dữ liệu. Bạn thường có một trường hợp duy nhất được chia sẻ trên ứng dụng của bạn.

Bây giờ, hãy cấu trúc lại cái này để sử dụng mẫu tiêm phụ thuộc.

interface IHorn
{
    void Honk();
}

class Horn : IHorn
{
    public void Honk()
    {
        Console.WriteLine("beep!");
    }
}

class Car
{
    private IHorn horn;

    public Car(IHorn horn)
    {
        this.horn = horn;
    }

    public void HonkHorn()
    {
        this.horn.Honk();
    }
}

class Program
{
    static void Main()
    {
        var horn = new Horn();
        var car = new Car(horn);
        car.HonkHorn();
    }
}

Chúng tôi đã thực hiện hai điều quan trọng ở đây. Đầu tiên, chúng tôi đã giới thiệu một giao diện mà Hornlớp chúng tôi thực hiện. Điều này cho phép chúng tôi mã Carlớp cho giao diện thay vì thực hiện cụ thể. Bây giờ mã có thể mất bất cứ điều gì thực hiện IHorn. Thứ hai, chúng tôi đã lấy tiếng còi ra khỏiCar và thay vào đó. Điều này giải quyết các vấn đề ở trên và để nó cho chức năng chính của ứng dụng để quản lý các trường hợp cụ thể và vòng đời của chúng.

Điều này có nghĩa là có thể giới thiệu một loại còi mới cho xe sử dụng mà không cần chạm vào Carlớp:

class FrenchHorn : IHorn
{
    public void Honk()
    {
        Console.WriteLine("le beep!");
    }
}

Chính chỉ có thể tiêm một thể hiện của FrenchHornlớp thay thế. Điều này cũng đơn giản hóa đáng kể việc thử nghiệm. Bạn có thể tạo một MockHornlớp để tiêm vàoCar tạo để đảm bảo bạn đang kiểm tra chỉCar lớp đó.

Ví dụ trên cho thấy tiêm phụ thuộc thủ công. Thông thường DI được thực hiện với một khung (ví dụ: Unity hoặc Ninject trong thế giới C #). Các khung này sẽ thực hiện tất cả các hệ thống dây phụ thuộc cho bạn bằng cách đi qua biểu đồ phụ thuộc của bạn và tạo các thể hiện khi cần thiết.

Cách Node.js tiêu chuẩn

Bây giờ hãy xem xét ví dụ tương tự trong Node.js. Chúng tôi có thể sẽ chia mã của chúng tôi thành 3 mô-đun:

// horn.js
module.exports = {
    honk: function () {
        console.log("beep!");
    }
};

// car.js
var horn = require("./horn");
module.exports = {
    honkHorn: function () {
        horn.honk();
    }
};

// index.js
var car = require("./car");
car.honkHorn();

Bởi vì JavaScript được tháo gỡ, chúng tôi không có sự kết hợp chặt chẽ giống như trước đây. Không cần giao diện (cũng không tồn tại) vì carmô-đun sẽ chỉ cố gắng gọi honkphương thức trên bất cứ điều gìhorn mô-đun xuất.

Ngoài ra, vì requirelưu trữ tất cả mọi thứ của Node , các mô-đun về cơ bản là các singletons được lưu trữ trong một thùng chứa. Bất kỳ mô-đun nào khác thực hiện một requiretrênhorn mô-đun sẽ nhận được cùng một ví dụ chính xác. Điều này làm cho việc chia sẻ các đối tượng singleton như kết nối cơ sở dữ liệu rất dễ dàng.

Bây giờ vẫn còn vấn đề là carmô-đun chịu trách nhiệm tìm nạp phụ thuộc của chính nó horn. Nếu bạn muốn chiếc xe sử dụng một mô-đun khác cho còi của nó, bạn phải thay đổi requiretuyên bố trongcar mô-đun. Đây không phải là một điều rất phổ biến để làm, nhưng nó gây ra vấn đề với thử nghiệm.

Cách thông thường mọi người xử lý vấn đề kiểm tra là với proxyquire . Do tính chất động của JavaScript, proxyquire chặn các cuộc gọi để yêu cầu và trả lại bất kỳ sơ khai / giả nào bạn cung cấp thay thế.

var proxyquire = require('proxyquire');
var hornStub = {
    honk: function () {
        console.log("test beep!");
    }
};

var car = proxyquire('./car', { './horn': hornStub });

// Now make test assertions on car...

Điều này là quá đủ cho hầu hết các ứng dụng. Nếu nó hoạt động cho ứng dụng của bạn thì hãy đi với nó. Tuy nhiên, theo kinh nghiệm của tôi khi các ứng dụng phát triển lớn hơn và phức tạp hơn, việc duy trì mã như thế này trở nên khó khăn hơn.

DI trong JavaScript

Node.js rất linh hoạt. Nếu bạn không hài lòng với phương pháp trên, bạn có thể viết các mô-đun của mình bằng cách sử dụng mẫu tiêm phụ thuộc. Trong mẫu này, mỗi mô-đun xuất một hàm nhà máy (hoặc một hàm tạo lớp).

// horn.js
module.exports = function () {
    return {
        honk: function () {
            console.log("beep!");
        }
    };
};

// car.js
module.exports = function (horn) {
    return {
        honkHorn: function () {
            horn.honk();
        }
    };
};

// index.js
var horn = require("./horn")();
var car = require("./car")(horn);
car.honkHorn();

Điều này rất giống với phương pháp C # trước đó ở chỗ index.jsmô-đun chịu trách nhiệm cho vòng đời và hệ thống dây điện. Kiểm thử đơn vị khá đơn giản vì bạn chỉ có thể chuyển qua các mô hình / sơ khai cho các chức năng. Một lần nữa, nếu điều này là đủ tốt cho ứng dụng của bạn đi với nó.

Khung Bolus DI

Không giống như C #, không có khung DI tiêu chuẩn nào được thiết lập để giúp quản lý phụ thuộc của bạn. Có một số khung trong sổ đăng ký npm nhưng không có khung nào được áp dụng rộng rãi. Nhiều lựa chọn trong số này đã được trích dẫn trong các câu trả lời khác.

Tôi không đặc biệt hài lòng với bất kỳ tùy chọn nào có sẵn nên tôi đã viết bolus của riêng mình . Bolus được thiết kế để hoạt động với mã được viết theo kiểu DI ở trên và cố gắng rất DRY và rất đơn giản. Sử dụng chính xác car.jshorn.jscác mô-đun ở trên, bạn có thể viết lại index.jsmô-đun với bolus như:

// index.js
var Injector = require("bolus");
var injector = new Injector();
injector.registerPath("**/*.js");

var car = injector.resolve("car");
car.honkHorn();

Ý tưởng cơ bản là bạn tạo ra một kim phun. Bạn đăng ký tất cả các mô-đun của bạn trong kim phun. Sau đó, bạn chỉ cần giải quyết những gì bạn cần. Bolus sẽ đi theo biểu đồ phụ thuộc và tạo và tiêm phụ thuộc khi cần thiết. Bạn không tiết kiệm nhiều trong một ví dụ đồ chơi như thế này, nhưng trong các ứng dụng lớn với cây phụ thuộc phức tạp, số tiền tiết kiệm là rất lớn.

Bolus hỗ trợ một loạt các tính năng tiện lợi như phụ thuộc tùy chọn và toàn cầu thử nghiệm, nhưng có hai lợi ích chính mà tôi đã thấy liên quan đến cách tiếp cận Node.js tiêu chuẩn. Đầu tiên, nếu bạn có nhiều ứng dụng tương tự, bạn có thể tạo mô-đun npm riêng cho cơ sở của mình để tạo ra một trình tiêm và đăng ký các đối tượng hữu ích trên đó. Sau đó, các ứng dụng cụ thể của bạn có thể thêm, ghi đè và giải quyết khi cần giống như cách AngularJScông việc kim phun. Thứ hai, bạn có thể sử dụng bolus để quản lý các bối cảnh phụ thuộc khác nhau. Ví dụ: bạn có thể sử dụng phần mềm trung gian để tạo một trình tiêm con theo yêu cầu, đăng ký id người dùng, id phiên, logger, v.v. trên trình tiêm cùng với bất kỳ mô-đun nào tùy thuộc vào các mô-đun đó. Sau đó giải quyết những gì bạn cần để phục vụ yêu cầu. Điều này cung cấp cho bạn các phiên bản của các mô-đun của bạn theo yêu cầu và ngăn chặn việc phải chuyển bộ ghi, v.v. cho mỗi cuộc gọi chức năng mô-đun.


1
cũng đúng là có các giải pháp thay thế proxyquirenhư sinoncho phép bạn thực hiện các giả lập rất súc tích, ví dụ let readFileStub = sinon.stub(fs, 'readFile').yields(new Error('something went wrong'));và sau đó các cuộc gọi tiếp theo fs.readFilesẽ trả về lỗi cho đến khi bạn hoàn nguyên sơ khai readFileStub.restore(). Cá nhân tôi thấy DI sử dụng có vấn đề bởi vì tôi cảm thấy gần như yêu cầu sử dụng các lớp / đối tượng, đó là một giả định đáng ngờ được đưa ra dựa trên chức năng của javascript.
Kevin

Cảm ơn cho câu trả lời tốt + chi tiết này. Tôi gần như đã bỏ lỡ nó, khi lần đầu tiên tôi đọc tiêu đề DI trong C # .
Konstantin A. Magg

1
Câu trả lời chính xác. Tôi đang tự hỏi những gì suy nghĩ của bạn là vào năm 2019. Đối với các dự án lớn, như một vấn đề sở thích cá nhân, mà những bạn thích - DI / IoC trong Node, hoặc chỉ stubbing / chế giễu với jest, rewire, proxyquire, vv? Cảm ơn.
Jamie Corkhill

Câu trả lời cân bằng tuyệt vời! Cảm ơn bạn.
Johnny Oshika

36

Tôi cũng đã viết một mô-đun để thực hiện điều này, nó được gọi là tua lại . Chỉ cần sử dụng npm install rewirevà sau đó:

var rewire = require("rewire"),
    myModule = rewire("./path/to/myModule.js"); // exactly like require()

// Your module will now export a special setter and getter for private variables.
myModule.__set__("myPrivateVar", 123);
myModule.__get__("myPrivateVar"); // = 123


// This allows you to mock almost everything within the module e.g. the fs-module.
// Just pass the variable name as first parameter and your mock as second.
myModule.__set__("fs", {
    readFile: function (path, encoding, cb) {
        cb(null, "Success!");
    }
});
myModule.readSomethingFromFileSystem(function (err, data) {
    console.log(data); // = Success!
});

Tôi đã được truyền cảm hứng từ tiêm chích của Nathan MacInnes nhưng đã sử dụng một cách tiếp cận khác. Tôi không sử dụng vmđể đánh giá mô-đun thử nghiệm, trên thực tế tôi sử dụng yêu cầu riêng của nút. Bằng cách này, mô-đun của bạn hoạt động chính xác như sử dụng require()(ngoại trừ sửa đổi của bạn). Ngoài ra gỡ lỗi được hỗ trợ đầy đủ.


7
Kể từ giữa năm 2014 - npmjs.org/package/proxyquire khiến việc loại bỏ "yêu cầu" trở nên tầm thường.
arcseldon

proxyquire cũng rất tuyệt! Bây giờ họ đang sử dụng mô-đun "mô-đun" nội bộ, cách này tốt hơn so với sử dụng vm của nút. Nhưng sau tất cả, đó chỉ là vấn đề về phong cách. Tôi thích mô-đun của tôi để sử dụng yêu cầu ban đầu và để trao đổi các phụ thuộc sau này. Hơn nữa, tua lại cũng cho phép ghi đè lên toàn cầu.
Julian Ewald

Rất thú vị khi tìm kiếm một cái gì đó như thế này để sử dụng tại nơi làm việc, mô-đun này có ảnh hưởng đến các mô-đun hạ lưu không?
akst

đối với proxyquire Người ta đã nói trong phần mô tả rằng nó được sử dụng để thử nghiệm, 'Proxyjs yêu cầu để cho phép ghi đè các phụ thuộc trong quá trình thử nghiệm .' không phải cho DI, phải không?
Marwen Trabelsi

17

Tôi đã xây dựng Electrolyte cho mục đích này. Các giải pháp tiêm phụ thuộc khác ngoài kia quá xâm lấn vào thị hiếu của tôi, và gây rối với toàn cầu requirelà một sự bất bình đặc biệt của tôi.

Electrolyte bao gồm các mô-đun, cụ thể là các mô-đun xuất một chức năng "thiết lập" như bạn thấy trong phần mềm trung gian Connect / Express. Về cơ bản, các loại mô-đun này chỉ là nhà máy cho một số đối tượng mà chúng trả về.

Ví dụ: mô-đun tạo kết nối cơ sở dữ liệu:

var mysql = require('mysql');

exports = module.exports = function(settings) {
  var connection = mysql.createConnection({
    host: settings.dbHost,
    port: settings.dbPort
  });

  connection.connect(function(err) {
    if (err) { throw err; }
  });

  return connection;
}

exports['@singleton'] = true;
exports['@require'] = [ 'settings' ];

Những gì bạn nhìn thấy ở phía dưới là các chú thích , thêm một chút siêu dữ liệu mà Electrolyte sử dụng để khởi tạo và thêm các phụ thuộc, tự động nối các thành phần của ứng dụng với nhau.

Để tạo kết nối cơ sở dữ liệu:

var db = electrolyte.create('database');

Chất điện phân di chuyển ngang qua các phần @requirephụ thuộc và đưa các thể hiện làm đối số cho hàm xuất.

Điều quan trọng là điều này là xâm lấn tối thiểu. Mô-đun này là hoàn toàn có thể sử dụng, độc lập với chính Electrolyte. Điều đó có nghĩa là các bài kiểm tra đơn vị của bạn chỉ có thể kiểm tra mô-đun được kiểm tra , chuyển qua các đối tượng giả mà không cần phụ thuộc bổ sung để tua lại nội bộ.

Khi chạy ứng dụng đầy đủ, Electrolyte bước vào ở cấp độ giữa các mô-đun, kết nối mọi thứ với nhau mà không cần phải có toàn cầu, singletons hoặc hệ thống ống nước quá mức.


1
Bạn có làm rõ những gì xảy ra trong mã bạn đã đăng khi có lệnh gọi connect()không? Mặc dù tôi không quen với API MySql cho Node, tôi sẽ mong đợi cuộc gọi này không đồng bộ, vì vậy hình minh họa không rõ ràng lắm.
Andrey Agibalov

hiện đang sử dụng chất điện phân. Bạn cho rằng thật dễ dàng để KIẾM mô-đun thông qua xuất khẩu ['@ Yêu cầu']. Nhưng nếu tôi phải sơ khai một trong các mô-đun cần thiết thì làm thế nào có thể đạt được trong chất điện phân. Hiện tại nếu chúng tôi yêu cầu các mô-đun, điều này có thể dễ dàng đạt được. Nhưng đối với chất điện phân thì đây là một tiêu cực rất lớn .... Bạn có ví dụ về việc chúng ta có thể sử dụng phiên bản mô-đun sơ khai và chuyển nó một cách linh hoạt trong khi khởi tạo / ioc.use từ các trường hợp thử nghiệm. Vì vậy, về cơ bản trong thử nghiệm đơn vị, nếu chúng ta có thể thực hiện ioc.create ('tên mô-đun') và sau đó thực hiện tiêm các mô-đun phụ thuộc (nhưng vẫn còn sơ khai) sẽ là lý tưởng ...
user1102171

1
Bạn sẽ không gọi ioc.createtừ một bài kiểm tra đơn vị. Một thử nghiệm đơn vị nên kiểm tra chỉ module dưới kiểm tra, và không mang lại phụ thuộc khác, bao gồm cả điện giải. Làm theo lời khuyên này, bạn sẽ làmobjToTest = require('modulename')(mockObj1, mockObj2);
Jared Hanson

8

Tôi nhìn vào điều này bản thân mình. Tôi không thích giới thiệu các thư viện phụ thuộc ma thuật cung cấp các cơ chế để chiếm quyền điều khiển nhập khẩu mô-đun của tôi. Thay vào đó, tôi đã đưa ra một "hướng dẫn thiết kế" cho nhóm của mình để nói rõ hơn những gì phụ thuộc có thể bị chế giễu bằng cách giới thiệu xuất khẩu chức năng của nhà máy trong các mô-đun của tôi.

Tôi sử dụng rộng rãi các tính năng ES6 cho các tham số và phá hủy để tránh một số bản tóm tắt và cung cấp một cơ chế ghi đè phụ thuộc có tên.

Đây là một ví dụ:

import foo from './utils/foo';
import bob from './utils/bob';

// We export a factory which accepts our dependencies.
export const factory = (dependencies = {}) => {
  const {
    // The 'bob' dependency.  We default to the standard 'bob' imp if not provided.
    $bob = bob, 
    // Instead of exposing the whole 'foo' api, we only provide a mechanism
    // with which to override the specific part of foo we care about.
    $doSomething = foo.doSomething // defaults to standard imp if none provided.
  } = dependencies;  

  return function bar() {
    return $bob($doSomething());
  }
}

// The default implementation, which would end up using default deps.
export default factory();

Và đây là một ví dụ về việc sử dụng nó

import { factory } from './bar';

const underTest = factory({ $bob: () => 'BOB!' }); // only override bob!
const result = underTest();

Xin lỗi cú pháp ES6 cho những người không quen thuộc với nó.


Thực sự rất tài tình!
arnold

4

Gần đây tôi đã kiểm tra chủ đề này với nhiều lý do tương tự như OP - hầu hết các lib tôi gặp phải tạm thời viết lại câu lệnh yêu cầu. Tôi đã có nhiều mức độ thành công với phương pháp này, và vì vậy tôi đã kết thúc bằng cách sử dụng phương pháp sau.

Trong ngữ cảnh của một ứng dụng cấp tốc - tôi gói app.js trong tệp bootstrap.js:

var path = require('path');
var myapp = require('./app.js');

var loader = require('./server/services/loader.js');

// give the loader the root directory
// and an object mapping module names 
// to paths relative to that root
loader.init(path.normalize(__dirname), require('./server/config/loader.js')); 

myapp.start();

Bản đồ đối tượng được truyền cho trình tải trông như thế này:

// live loader config
module.exports = {
    'dataBaseService': '/lib/dataBaseService.js'
}

// test loader config
module.exports = {
    'dataBaseService': '/mocks/dataBaseService.js'
    'otherService' : {other: 'service'} // takes objects too...
};

Sau đó, thay vì gọi trực tiếp yêu cầu ...

var myDatabaseService = loader.load('dataBaseService');

Nếu không có bí danh nào nằm trong trình tải - thì nó sẽ chỉ mặc định theo yêu cầu thông thường. Điều này có hai lợi ích: Tôi có thể trao đổi trong bất kỳ phiên bản nào của lớp và nó loại bỏ nhu cầu sử dụng tên đường dẫn tương đối trong ứng dụng (vì vậy nếu tôi cần một lib tùy chỉnh bên dưới hoặc bên trên tệp hiện tại, tôi không cần phải duyệt qua và yêu cầu sẽ lưu trữ mô-đun theo cùng một khóa). Nó cũng cho phép tôi chỉ định các giả ở bất kỳ điểm nào trong ứng dụng, thay vì trong bộ thử nghiệm ngay lập tức.

Tôi vừa xuất bản một mô-đun npm nhỏ để thuận tiện:

https://npmjs.org/package/nodejs-simple-loader


3

Thực tế là bạn có thể kiểm tra node.js của mình mà không cần bộ chứa IoC vì JavaScript là ngôn ngữ lập trình thực sự năng động và bạn có thể sửa đổi hầu hết mọi thứ trong thời gian chạy.

Hãy xem xét những điều sau đây:

import UserRepository from "./dal/user_repository";

class UserController {
    constructor() {
        this._repository = new UserRepository();
    }
    getUsers() {
        this._repository.getAll();
    }
}

export default UserController;

Vì vậy, bạn có thể ghi đè khớp nối giữa các thành phần trong thời gian chạy. Tôi muốn nghĩ rằng chúng ta nên nhắm mục tiêu tách các mô-đun JavaScript của chúng tôi.

Cách duy nhất để đạt được sự tách rời thực sự là bằng cách xóa tham chiếu đến UserRepository:

class UserController {
    constructor(userRepository) {
        this._repository = userRepository;
    }
    getUsers() {
        this._repository.getAll();
    }
}

export default UserController;

Điều này có nghĩa là ở một nơi khác bạn sẽ cần phải thực hiện thành phần đối tượng:

import UserRepository from "./dal/user_repository";
import UserController from "./dal/user_controller";

export default new UserController(new UserRepository());

Tôi thích ý tưởng ủy thác thành phần đối tượng cho một container IoC. Bạn có thể tìm hiểu thêm về ý tưởng này trong bài viết Trạng thái đảo ngược phụ thuộc hiện tại trong JavaScript . Bài viết cố gắng gỡ lỗi một số "huyền thoại về bộ chứa JavaScript IoC":

Chuyện lầm tưởng 1: Không có chỗ cho các bộ chứa IoC trong JavaScript

Chuyện lầm tưởng 2: Chúng tôi không cần bộ chứa IoC, chúng tôi đã có bộ tải mô-đun!

Chuyện lầm tưởng 3: Nghịch đảo phụ thuộc === tiêm phụ thuộc

Nếu bạn cũng thích ý tưởng sử dụng bộ chứa IoC, bạn có thể xem InversifyJS. Bản phát hành mới nhất (2.0.0) hỗ trợ nhiều trường hợp sử dụng:

  • Mô-đun hạt nhân
  • Phần mềm trung gian hạt nhân
  • Sử dụng các lớp, chuỗi ký tự hoặc Biểu tượng làm định danh phụ thuộc
  • Tiêm các giá trị không đổi
  • Tiêm xây dựng lớp
  • Tiêm nhà máy
  • Nhà máy tự động
  • Tiêm nhà cung cấp (nhà máy async)
  • Trình xử lý kích hoạt (được sử dụng để tiêm proxy)
  • Đa tiêm
  • Tagged ràng buộc
  • Trang trí thẻ tùy chỉnh
  • Các ràng buộc được đặt tên
  • Bối cảnh
  • Ngoại lệ thân thiện (ví dụ: phụ thuộc tròn)

Bạn có thể tìm hiểu thêm về nó tại InversifyJS .


2

Đối với ES6, tôi đã phát triển vùng chứa này https://github.com/zazoomauro/node-dependency-injection

import {ContainerBuilder} from 'node-dependency-injection'

let container = new ContainerBuilder()
container.register('mailer', 'Mailer')

Sau đó, bạn có thể đặt, ví dụ: lựa chọn vận chuyển trong container:

import {ContainerBuilder} from 'node-dependency-injection'

let container = new ContainerBuilder()
container
  .register('mailer', 'Mailer')
  .addArgument('sendmail')

Lớp này bây giờ linh hoạt hơn nhiều khi bạn đã tách sự lựa chọn vận chuyển ra khỏi việc thực hiện và vào container.

Bây giờ dịch vụ gửi thư nằm trong container, bạn có thể thêm nó như một phần phụ thuộc của các lớp khác. Nếu bạn có một lớp NewsletterManager như thế này:

class NewsletterManager {
    construct (mailer, fs) {
        this._mailer = mailer
        this._fs = fs
    }
}

export default NewsletterManager

Khi xác định dịch vụ newsletter_manager, dịch vụ gửi thư chưa tồn tại. Sử dụng lớp Tham chiếu để báo cho container chứa dịch vụ gửi thư khi nó khởi chạy trình quản lý bản tin:

import {ContainerBuilder, Reference, PackageReference} from 'node-dependency-injection'
import Mailer from './Mailer'
import NewsletterManager from './NewsletterManager'

let container = new ContainerBuilder()

container
  .register('mailer', Mailer)
  .addArgument('sendmail')

container
  .register('newsletter_manager', NewsletterManager)
  .addArgument(new Reference('mailer'))
  .addArgument(new PackageReference('fs-extra'))

Bạn cũng có thể thiết lập vùng chứa với các tệp cấu hình như tệp Yaml, Json hoặc JS

Các container dịch vụ có thể được biên dịch vì nhiều lý do. Những lý do này bao gồm kiểm tra bất kỳ vấn đề tiềm ẩn nào như tham chiếu vòng tròn và làm cho container hiệu quả hơn.

container.compile()

1

Nó phụ thuộc vào thiết kế của ứng dụng của bạn. Bạn rõ ràng có thể thực hiện một java như tiêm trong đó bạn tạo một đối tượng của một lớp với sự phụ thuộc được truyền vào hàm tạo như thế này.

function Cache(store) {
   this._store = store;
}

var cache = new Cache(mysqlStore);

Nếu bạn không làm OOP trong javascript, bạn có thể tạo một hàm init thiết lập mọi thứ.

Tuy nhiên, có một cách tiếp cận khác mà bạn có thể thực hiện phổ biến hơn trong một hệ thống dựa trên sự kiện, chẳng hạn như node.js. Nếu bạn có thể mô hình hóa ứng dụng của mình chỉ (hầu hết thời gian) hành động theo các sự kiện thì tất cả những gì bạn cần làm là thiết lập mọi thứ (mà tôi thường làm bằng cách gọi một hàm init) và phát ra các sự kiện từ gốc. Điều này làm cho việc kiểm tra khá dễ dàng và dễ đọc hơn.


Cảm ơn câu trả lời của bạn, nhưng tôi không hiểu hết phần thứ hai trong câu trả lời của bạn.
Erik

1

Tôi luôn thích sự đơn giản của khái niệm IoC - "Bạn không cần phải biết gì về môi trường, bạn sẽ được ai đó gọi khi cần"

Nhưng tất cả các triển khai IoC mà tôi thấy đã làm ngược lại - họ làm lộn xộn mã với nhiều thứ hơn là không có nó. Vì vậy, tôi đã tạo ra IoC của riêng mình hoạt động như tôi muốn - nó vẫn ẩn và vô hình 90% thời gian .

Nó được sử dụng trong khung web MonoJS http://monojs.org

Tôi đang nói về những điều đơn giản, như chia sẻ một đối tượng kết nối cơ sở dữ liệu, cho đến nay, nhưng tôi chưa tìm thấy giải pháp nào thỏa mãn tôi.

Nó được thực hiện như thế này - đăng ký thành phần một lần trong cấu hình.

app.register 'db', -> 
  require('mongodb').connect config.dbPath

Và sử dụng nó ở bất cứ đâu

app.db.findSomething()

Bạn có thể xem mã định nghĩa thành phần đầy đủ (với Kết nối DB và các Thành phần khác) tại đây https://github.com/sinizinairina/mono/blob/master/mono.coffee

Đây là nơi duy nhất khi bạn phải nói cho IoC biết phải làm gì, sau đó tất cả các thành phần đó sẽ được tạo và nối dây tự động và bạn không phải xem mã cụ thể của IoC trong ứng dụng của mình nữa.

Bản thân IoC https://github.com/alexeypetrushin/miconjs


6
Mặc dù được quảng cáo là DI, nhưng điều này có vẻ giống như một công cụ định vị dịch vụ.
KyorCode

2
Trông thật tuyệt, thật xấu hổ khi chỉ có trong coffescript
Rafael P. Miranda

1

Tôi nghĩ rằng chúng ta vẫn cần Dependency Injection trong Nodejs vì nó làm mất đi sự phụ thuộc giữa các dịch vụ và làm cho ứng dụng rõ ràng hơn.

Lấy cảm hứng từ Spring Framework , tôi cũng triển khai mô-đun của riêng mình để hỗ trợ tiêm phụ thuộc vào Nodejs. Mô-đun của tôi cũng có thể phát hiện code changesauto reload các dịch vụ mà không cần khởi động lại ứng dụng của bạn.

Ghé thăm dự án của tôi tại: Buncha - IoC container

Cảm ơn bạn!



0

Tôi đã làm việc với .Net, PHP và Java trong một thời gian dài vì vậy tôi cũng muốn có một Dependency Injection thuận tiện trong NodeJS. Mọi người nói rằng DI tích hợp trong NodeJS là đủ khi chúng ta có thể lấy nó với Module. Nhưng nó không làm tôi hài lòng. Tôi muốn giữ một Module không quá Class. Ngoài ra, tôi muốn DI có hỗ trợ đầy đủ cho quản lý vòng đời Mô-đun (mô-đun đơn, mô-đun tạm thời, v.v.) nhưng với mô-đun Node, tôi phải viết mã thủ công rất thường xuyên. Cuối cùng, tôi muốn làm cho bài kiểm tra đơn vị dễ dàng hơn. Đó là lý do tại sao tôi tạo ra một Tiêm phụ thuộc cho chính mình.

Nếu bạn đang tìm kiếm một DI, hãy thử. Nó có thể được tìm thấy ở đây: https://github.com/robo-creative/nodejs-robo-container . Nó được ghi chép đầy đủ. Nó cũng giải quyết một số vấn đề phổ biến với DI và cách giải quyết chúng theo cách OOP. Hy vọng nó giúp.


Có bạn đúng một thư viện DI trong các dự án của bạn rất quan trọng đối với các kiến ​​trúc tốt, Nếu bạn muốn xem trường hợp sử dụng cho DI, hãy xem readme cho kho lưu trữ này cũng là thư viện DI cho nút Jems DI .
Francisco Mercedes

-1

Gần đây tôi đã tạo một thư viện có tên là Circuitbox cho phép bạn sử dụng phép nội xạ phụ thuộc với node.js. Nó thực hiện tiêm phụ thuộc thực sự so với nhiều thư viện dựa trên tra cứu phụ thuộc mà tôi đã thấy. Circuitbox cũng hỗ trợ các thói quen khởi tạo và khởi tạo không đồng bộ. Dưới đây là một ví dụ:

Giả sử đoạn mã sau nằm trong một tệp có tên consoleMessagePrinter.js

'use strict';

// Our console message printer
// deps is injected by circuitbox with the dependencies
function ConsoleMessagePrinter(deps) {
  return {
    print: function () {
      console.log(deps.messageSource.message());
    }
  };
}

module.exports = ConsoleMessagePrinter;

Giả sử sau đây là trong tệp main.js

'use strict';

// our simple message source
// deps is injected by circuitbox with the dependencies
var simpleMessageSource = function (deps) {
  return {
    message: function () {
      return deps.message;
    }
  };
};

// require circuitbox
var circuitbox = require('../lib');

// create a circuitbox
circuitbox.create({
  modules: [
    function (registry) {
      // the message to be used
      registry.for('message').use('This is the message');

      // define the message source
      registry.for('messageSource').use(simpleMessageSource)
        .dependsOn('message');

      // define the message printer - does a module.require internally
      registry.for('messagePrinter').requires('./consoleMessagePrinter')
        .dependsOn('messageSource');
    }
  ]
}).done(function (cbx) {

  // get the message printer and print a message
  cbx.get('messagePrinter').done(function (printer) {
    printer.print();
  }, function (err) {
    console.log('Could not recieve a printer');
    return;
  });

}, function (err) {
  console.log('Could not create circuitbox');
});

Circuitbox cho phép bạn xác định các thành phần của bạn và khai báo các phụ thuộc của chúng dưới dạng các mô-đun. Khi được khởi tạo, nó cho phép bạn truy xuất một thành phần. Circuitbox tự động tiêm tất cả các thành phần mà thành phần đích yêu cầu và đưa nó cho bạn sử dụng.

Dự án là phiên bản alpha. Nhận xét, ý tưởng và phản hồi của bạn đều được chào đón.

Hy vọng nó giúp!


-1

Tôi nghĩ rằng các bài viết khác đã làm rất tốt trong cuộc tranh luận về việc sử dụng DI. Đối với tôi những lý do là

  1. Tiêm phụ thuộc mà không biết đường dẫn của họ. Điều này có nghĩa là nếu bạn thay đổi vị trí mô-đun trên đĩa hoặc trao đổi nó với vị trí khác, bạn không cần phải chạm vào mọi tệp phụ thuộc vào nó.

  2. Nó làm cho nó dễ dàng hơn rất nhiều để giả định các phụ thuộc để thử nghiệm mà không phải chịu sự áp đảo của requirechức năng toàn cầu theo cách hoạt động mà không gặp vấn đề.

  3. Nó giúp bạn tổ chức và lý do về ứng dụng của bạn dưới dạng các mô đun được ghép lỏng lẻo.

Nhưng tôi đã rất khó khăn trong việc tìm kiếm một khung DI mà nhóm của tôi và tôi có thể dễ dàng áp dụng. Vì vậy, gần đây tôi đã xây dựng một khung gọi là deppie dựa trên các tính năng này

  • API tối thiểu có thể học trong vài phút
  • Không cần thêm mã / cấu hình / chú thích
  • Một với một ánh xạ trực tiếp vào requiremodule
  • Có thể được thông qua một phần để làm việc với mã hiện có

-1

Nó nên linh hoạt và đơn giản như thế này:

var MyClass1 = function () {}
var MyClass2 = function (myService1) {
    // myService1.should.be.instanceof(MyClass1); 
}


container.register('myService1', MyClass1);
container.register('myService2', MyClass2, ['myService1']);

Tôi đã viết bài viết về Dependency Injection trong node.js.

Tôi hy vọng nó có thể giúp bạn với điều này.


-1

Node.js yêu cầu DI nhiều như bất kỳ nền tảng nào khác. Nếu bạn đang xây dựng một cái gì đó lớn, DI sẽ giúp dễ dàng giả định các phụ thuộc của mã của bạn và kiểm tra mã của bạn một cách kỹ lưỡng.

Ví dụ, các mô-đun lớp cơ sở dữ liệu của bạn không nên được yêu cầu tại các mô-đun mã doanh nghiệp của bạn bởi vì, khi đơn vị kiểm tra các mô-đun mã kinh doanh này, dao sẽ tải và kết nối với cơ sở dữ liệu.

Một giải pháp sẽ là vượt qua các phụ thuộc dưới dạng tham số mô-đun:

module.exports = function (dep1, dep2) {
// private methods

   return {
    // public methods
       test: function(){...}
   }
}

Cách phụ thuộc này có thể được chế giễu một cách dễ dàng và tự nhiên và bạn có thể tập trung vào kiểm tra mã của mình mà không cần sử dụng bất kỳ thư viện bên thứ 3 phức tạp nào.

Có những giải pháp khác ngoài kia (broadway, architect, v.v.) có thể giúp bạn với điều này. mặc dù họ có thể làm nhiều hơn những gì bạn muốn hoặc sử dụng nhiều lộn xộn hơn.


Hầu như thông qua sự tiến hóa tự nhiên tôi đã kết thúc như vậy. Tôi vượt qua một phụ thuộc như là một tham số và nó hoạt động rất tốt để thử nghiệm.
munkee

-1

Tôi đã phát triển một thư viện xử lý việc tiêm phụ thuộc một cách đơn giản, làm giảm mã soạn sẵn. Mỗi mô-đun được xác định bởi một tên duy nhất và chức năng điều khiển. Các tham số của bộ điều khiển phản ánh sự phụ thuộc của mô-đun.

Đọc thêm về KlarkJS

Ví dụ ngắn gọn:

KlarkModule(module, 'myModuleName1', function($nodeModule1, myModuleName2) {
    return {
        log: function() { console.log('Hello from module myModuleName1') }
    };
});
  • myModuleName1 là tên của mô-đun.
  • $nodeModule1là một thư viện bên ngoài từ node_module. Tên giải quyết thành node-module1. Tiền tố $chỉ ra rằng nó là một mô-đun bên ngoài.
  • myModuleName2 là tên của một mô-đun nội bộ.
  • Giá trị trả về của bộ điều khiển được sử dụng từ các mô-đun bên trong khác, khi chúng xác định tham số myModuleName1.

-1

Tôi đã phát hiện ra câu hỏi này trong khi trả lời một vấn đề trên mô-đun DI của riêng tôi, hỏi tại sao người ta lại cần một hệ thống DI để lập trình NodeJS.

Câu trả lời rõ ràng có xu hướng cho những người được đưa ra trong chủ đề này: nó phụ thuộc. Có sự đánh đổi cho cả hai cách tiếp cận và đọc câu trả lời của câu hỏi này cho thấy hình dạng tốt của chúng.

Vì vậy, câu trả lời thực sự cho câu hỏi này, nên là trong một số tình huống, bạn sẽ sử dụng hệ thống DI, trong những trường hợp khác thì không.

Điều đó nói rằng, những gì bạn muốn là một nhà phát triển là không lặp lại chính mình và sử dụng lại các dịch vụ của bạn trên các ứng dụng khác nhau của bạn.

Điều này có nghĩa là chúng ta nên viết các dịch vụ đã sẵn sàng để được sử dụng trong hệ thống DI nhưng không bị ràng buộc với các thư viện DI. Đối với tôi, điều đó có nghĩa là chúng ta nên viết các dịch vụ như thế này:

module.exports = initDBService;

// Tells any DI lib what it expects to find in it context object
// The $inject prop is the de facto standard for DI imo 
initDBService.$inject = ['ENV'];

// Note the context object, imo, a DI tool should bring
// services in a single context object
function initDBService({ ENV }) {
/// actual service code
}

Bằng cách đó, dịch vụ của bạn hoạt động không thành vấn đề nếu bạn sử dụng nó có hoặc không có công cụ DI.


-1

TypeDI là ngọt ngào nhất trong tất cả các đề cập ở đây, hãy xem mã này trong TypeDI

import "reflect-metadata";
import {Service, Container} from "typedi";

@Service()
class SomeClass {

    someMethod() {
    }

}

let someClass = Container.get(SomeClass);
someClass.someMethod();

Nhìn mã này quá:

import {Container, Service, Inject} from "typedi";

// somewhere in your global app parameters
Container.set("authorization-token", "RVT9rVjSVN");

@Service()
class UserRepository {

    @Inject("authorization-token")
    authorizationToken: string;

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.