React Native - Việc sử dụng singleton có phải là sự thay thế tốt nhất cho DI không?


8

Tôi đã đọc rất nhiều về mẫu đơn và mức độ "xấu" của nó bởi vì nó làm cho các lớp sử dụng nó khó kiểm tra nên không nên tránh. Tôi đã đọc một số bài viết giải thích làm thế nào singleton có thể được thay thế bằng tiêm phụ thuộc, nhưng nó có vẻ phức tạp không cần thiết đối với tôi.

Đây là vấn đề của tôi chi tiết hơn một chút. Tôi đang xây dựng một ứng dụng di động bằng React Native và tôi muốn tạo một ứng dụng khách REST sẽ giao tiếp với máy chủ, nhận dữ liệu, đăng dữ liệu và xử lý đăng nhập (lưu mã thông báo đăng nhập và gửi nó với mỗi yêu cầu sau khi đăng nhập).

Kế hoạch ban đầu của tôi là tạo một đối tượng đơn lẻ (RESTClient) mà ứng dụng của tôi sẽ sử dụng ban đầu để đăng nhập và sau đó thực hiện yêu cầu gửi thông tin đăng nhập khi cần. Cách tiếp cận DI có vẻ rất phức tạp đối với tôi (có thể vì tôi chưa bao giờ sử dụng DI trước đây) nhưng tôi đang sử dụng dự án này để tìm hiểu càng nhiều càng tốt nên tôi muốn làm những gì tốt nhất ở đây. Bất kỳ đề xuất và ý kiến ​​được nhiều đánh giá cao.

Chỉnh sửa: Bây giờ tôi đã nhận ra rằng tôi đã trả lời câu hỏi của tôi rất kém. Tôi muốn có một số hướng dẫn về cách tránh mẫu singleton trong RN và tôi thậm chí có nên làm điều đó không. May mắn thay Samuel đã cho tôi loại câu trả lời tôi muốn. Vấn đề của tôi là tôi muốn tránh mô hình singleton và sử dụng DI, nhưng có vẻ rất phức tạp khi thực hiện nó trong React Native. Tôi đã thực hiện một số nghiên cứu sâu hơn và thực hiện nó bằng hệ thống bối cảnh Reacts.

Đối với bất cứ ai quan tâm đây là cách tôi đã làm nó. Như tôi đã nói, tôi đã sử dụng bối cảnh trong RN giống như đạo cụ nhưng nó được truyền xuống mọi thành phần.

Trong thành phần gốc tôi cung cấp các phụ thuộc cần thiết như thế này:

export default class Root extends Component {
  getChildContext() {
    restClient: new MyRestClient();
  }

  render() {...}
}
Root.childContextTypes = {restClient: PropTypes.object};

Bây giờ restClient có sẵn trong tất cả các thành phần bên dưới Root. Tôi có thể truy cập nó như thế này.

export default class Child extends Component {
  useRestClient() {
    this.context.restClient.getData(...);
  }

  render() {...}
}
Child.contextTypes = {restClient: PropTypes.object}

Điều này có hiệu quả di chuyển việc tạo đối tượng ra khỏi logic và tách riêng việc thực hiện ứng dụng khách REST khỏi các thành phần của tôi.


3
Xem xét thay đổi tiêu đề của câu hỏi. Ngay bây giờ REST và client là các phần tử không liên quan. Câu hỏi duy nhất ở đây là: Liệu singleton có phải là sự thay thế tốt nhất cho DI trong tình huống cụ thể của tôi không? .
Laiv

Tôi không biết nhiều về phản ứng, nhưng nói chung DI là một khái niệm đơn giản. Nếu bạn thấy nó phức tạp, tôi nghi ngờ bạn chưa đọc / nghe một lời giải thích tốt.
Ben Aaronson

Câu trả lời:


15

Việc tiêm phụ thuộc hoàn toàn không cần phức tạp và nó hoàn toàn đáng để học hỏi và sử dụng. Thông thường, nó phức tạp khi sử dụng các khung tiêm phụ thuộc, nhưng chúng không cần thiết.

Ở dạng đơn giản nhất, tiêm phụ thuộc là truyền phụ thuộc thay vì nhập hoặc xây dựng chúng. Điều này có thể được thực hiện bằng cách đơn giản là sử dụng một tham số cho những gì sẽ được nhập khẩu. Hãy nói rằng bạn có một thành phần có tên MyListcần sử dụng RESTClientđể tìm nạp một số dữ liệu và hiển thị nó cho người dùng. Cách tiếp cận "đơn lẻ" sẽ trông giống như thế này:

import restClient from '...' // import singleton

class MyList extends React.Component {
  // use restClient and render stuff
}

Đây chặt chẽ các cặp vợ chồng MyListđến restClient, và không có cách nào bạn có thể kiểm tra đơn vị MyListmà không cần xét nghiệm restClient. Cách tiếp cận DI sẽ trông giống như thế này:

function MyListFactory(restClient) {
  class MyList extends React.Component {
    // use restClient and render stuff
  }

  return MyList
}

Đó là tất cả những gì nó cần để sử dụng DI. Nó thêm tối đa hai dòng mã và bạn có thể loại bỏ việc nhập. Lý do tại sao tôi giới thiệu một "nhà máy" chức năng mới là vì AFAIK bạn không thể truyền các tham số hàm tạo bổ sung trong React và tôi không muốn chuyển những thứ này qua các thuộc tính React vì nó không thể di động và tất cả các thành phần cha mẹ phải biết để vượt qua tất cả đạo cụ cho trẻ em.

Vì vậy, bây giờ bạn có một chức năng để xây dựng MyListcác thành phần, nhưng làm thế nào để bạn sử dụng nó? Các mẫu DI bong bóng lên chuỗi phụ thuộc. Nói rằng bạn có một thành phần MyAppsử dụng MyList. Cách tiếp cận "đơn lẻ" sẽ là:

import MyList from '...'
class MyApp extends React.Component {
  render() {
    return <MyList />
  }
}

Cách tiếp cận DI là:

function MyAppFactory(ListComponent) {
  class MyApp extends React.Component {
    render() {
      return <ListComponent />
    }
  }

  return MyApp
}

Bây giờ chúng tôi có thể kiểm tra MyAppmà không cần kiểm tra MyListtrực tiếp. Chúng tôi thậm chí có thể sử dụng lại MyAppvới một loại danh sách hoàn toàn khác. Mô hình này bong bóng lên đến gốc thành phần . Đây là nơi bạn gọi các nhà máy của bạn và dây tất cả các thành phần.

import RESTClient from '...'
import MyListFactory from '...'
import MyAppFactory from '...'

const restClient = new RESTClient(...)
const MyList = MyListFactory(restClient)
const MyApp = MyAppFactory(MyList)

ReactDOM.render(<MyApp />, document.getElementById('app'))

Bây giờ hệ thống của chúng tôi sử dụng một ví dụ duy nhất RESTClient, nhưng chúng tôi đã thiết kế nó theo cách các thành phần được ghép lỏng lẻo và dễ kiểm tra.


Nếu bạn không phiền tôi muốn tham khảo câu trả lời của bạn từ tôi, bởi vì tôi nghĩ nó minh họa rất rõ những điểm tôi chỉ đề cập một cách hời hợt.
Laiv

2
@Laiv, tôi nghĩ thuật ngữ "người nghèo DI" đang bị lỗi mốt, thiên về "DI thuần túy". Mặc dù không có ý định, nhưng trước đây âm thanh quá tiêu cực vì nó ngụ ý các container IoC "phong phú" hơn.
David Arno

Điều này. Tôi nghĩ rằng Dependency Injection cực kỳ phức tạp khi tôi lần đầu tiên nhìn thấy nó, nhưng sau một thời gian, nó cực kỳ dễ nắm bắt. Và @Samuel giải thích điều đó tốt!
Rhys Johns

1
@Samuel Vậy để áp dụng đúng DI trong React tôi có cần tạo nhà máy cho mọi thành phần không? Cũng trong ví dụ của bạn, không nên MyAppFactorytrả lại MyApplớp?
Mateo Hrastnik

@MattHammond Các thành phần đơn giản không có bất kỳ sự phụ thuộc nào không cần nhà máy. Và đây chỉ là cách tôi đã thực hiện, có thể có những cách tiếp cận khác với DI trong React tốt hơn, nhưng tôi đã nhắm đến sự đơn giản ở đây. Cảm ơn vì sự đúng đắn của bạn; Tôi đã sửa nó rồi.
Samuel

5

Theo cơ sở của bạn (học tập), câu trả lời đơn giản nhất là không, singletons không phải là sự thay thế tốt nhất cho Dependency Injection .

Nếu học tập là mục tiêu, bạn sẽ tìm thấy DI là một nguồn tài nguyên có giá trị hơn trong bạn hộp công cụ hơn Singleton. Nó có vẻ phức tạp, nhưng đường cong học tập gần như trên tất cả mọi thứ bạn phải học từ đầu. Đừng nằm trên vùng thoải mái của bạn hoặc sẽ không học được gì cả.

Về mặt kỹ thuật, có rất ít sự khác biệt giữa singletontrường hợp duy nhất (những gì tôi nghĩ rằng bạn đang cố gắng để làm). Học DI bạn sẽ nhận ra rằng bạn có thể tiêm các trường hợp đơn lẻ trên toàn bộ mã. Bạn sẽ thấy mã của mình dễ kiểm tra hơn và được ghép lỏng lẻo. ( Để biết thêm chi tiết, hãy xem câu trả lời của Samuel )

Nhưng đừng dừng lại ở đây. Thực hiện cùng mã với Singleton. Sau đó so sánh cả hai cách tiếp cận.

Hiểu và làm quen với cả hai triển khai sẽ cho phép bạn biết khi nào chúng được chiếm dụng và có lẽ bạn sẽ ở trong một vị trí để tự trả lời câu hỏi.

Bây giờ, trong quá trình đào tạo, khi bạn rèn Golden Hammer , vì vậy nếu bạn quyết định tránh học DI, có khả năng bạn sẽ kết thúc việc thực hiện singletons mỗi khi bạn cần DI.


0

Tôi đồng ý với các câu trả lời khác rằng học DI là gì và cách sử dụng nó là một ý tưởng tốt.

Điều đó nói rằng, cảnh báo rằng những người độc thân làm cho việc kiểm tra quá khó thường được thực hiện bởi những người sử dụng các ngôn ngữ được nhập tĩnh (C ++, C #, Java, v.v.) .
Ngược lại trong một ngôn ngữ động (Javascript, PHP, Python, Ruby, v.v.) , thường khó thay thế một singleton bằng một triển khai cụ thể kiểm tra so với trong trường hợp bạn đang sử dụng DI.

Trong trường hợp đó, tôi khuyên bạn nên sử dụng thiết kế mang lại cảm giác tự nhiên hơn cho bạn và các nhà đồng phát triển, bởi vì điều đó sẽ có xu hướng tránh những sai lầm. Nếu điều đó dẫn đến singletons, vì vậy hãy là nó.

(Nhưng sau đó một lần nữa: Tìm hiểu DI trước khi bạn đưa ra quyết định.)

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.