Dependency Injection: Tôi có nên tạo một lớp Car để chứa tất cả các bộ phận của nó không?


10

Tôi có nhiều chiếc xe trong ứng dụng C ++ của mình, tất cả đều chứa một chiếc RaceTrack.

Mỗi chiếc xe gồm hàng trăm bộ phận. Mỗi phần phụ thuộc vào một hoặc hai phần khác.

Tôi đã đọc rất nhiều câu hỏi SO trên cuốn sách của DI và Mark Seemann và có vẻ như tôi không nên định nghĩa một lớp Xe hơi chỉ để giữ các bộ phận xe hơi bởi vì tất cả các bộ phận xe hơi sẽ phụ thuộc vào nhau và súp phụ tùng này là một chiếc xe hơi. Tôi có đúng không

Vì vậy, khi tôi đặt tất cả các xe đua của mình vào RaceTrack, sẽ không có các thực thể xe hơi mà có rất nhiều bộ phận xe hơi phụ thuộc vào nhau đua trên đường đua ?? Tôi có đúng không

BIÊN TẬP:

Từ lâu tôi đã lập trình các lớp Xe hơi vì rõ ràng tôi cần một lớp Xe hơi nếu tôi lập trình logic xe hơi. Nhưng với DI nó không quá rõ ràng với tôi. Vẫn còn băn khoăn rằng DI không tạo ra một lớp Xe hơi nếu tôi không có vai trò xác định cho nó?

Ý tôi là có ổn không khi có Chỉ đạo lái xe, BoltsAndNuts trên bánh xe cho pit crew và tất cả các loại giao diện hài hước khác mà không có một ví dụ đại diện cho một chiếc xe?

Câu trả lời:


24

Có gì tốt khi một loạt các bộ phận xe hơi ngồi trên đường đua làm bất cứ ai?

Nếu tất cả các lớp xe của bạn làm là giữ các bộ phận xe hơi thì nó hữu ích như một túi phụ tùng ướt.

Sử dụng

Là người lái xe, những gì tôi muốn là thứ tôi có thể kiểm soát. Điều đó đáp ứng khi tôi yêu cầu tốc độ. Điều đó xử lý như trên đường ray. Điều đó có thể dừng lại trên một xu.

Những gì tôi muốn là một lớp xe có thể sử dụng. Điều đó tôi có thể nói để làm mọi thứ mà không phải suy nghĩ về cách thức hoạt động của bộ chế hòa khí. Tôi chỉ nghĩ về bàn đạp ga. Làm thế nào những cái đó được kết nối không phải là điều tôi lo lắng. Đó là sự trừu tượng.

Phụ thuộc tiêm

Tiêm phụ thuộc không có gì để làm với điều đó. Khi tôi lái xe, tôi không nghĩ về cách nó được chế tạo. Miễn là nó hoạt động, tôi không quan tâm làm thế nào họ đặt nó lại với nhau.

Không, DI là thứ cho phép phi hành đoàn của tôi nhanh chóng trao đổi lốp xe của mình để lấy lốp tốt hơn khi trời bắt đầu mưa trên đường đua. Thật tuyệt khi có thể làm điều đó mà không cần phải nhảy vào một chiếc xe hoàn toàn khác.

DI thực sự là về một nguyên tắc: Sử dụng riêng biệt từ xây dựng.

Một chiếc xe có thể cài đặt lốp xe mới tại 90 dặm một giờ có vẻ mát mẻ nhưng tôi không nghĩ rằng nó sẽ giành chiến thắng bất kỳ cuộc đua với một contraption lốp cài đặt trên đó.

DI là về việc cài đặt các bộ phận của bạn theo cách mà cho phép phi hành đoàn của bạn đến với họ. Khi bạn sử dụng newở cùng một nơi, bạn lập trình hành vi, giống như bạn đang hàn bộ chế hòa khí vào vị trí. Chắc chắn một ngọn đuốc acetylene có thể loại bỏ nó nhưng trước tiên hãy xem xét sử dụng đai ốc và bu lông.

Đó là những gì DI là. Chắc chắn nó không dễ dàng như newlấy một cái gì đó ngay khi bạn nhận ra bạn muốn nó. Thay vào đó bạn phải viết mã riêng biệt để biết cách xây dựng chiếc xe của bạn. Nhưng nó chắc chắn làm cho nó dễ dàng hơn để thay đổi mọi thứ sau này. Và điều đó có nghĩa là bạn không phải kéo một nhà máy lắp ráp ô tô quanh đường đua với bạn.

Xây dựng

Một cái gì đó, ở đâu đó, phải biết nếu lốp xe là Goodyear. Trường hợp sau đó để đặt mã xây dựng xe? Nếu không phải là xe thì đội pit? Không. Theo dõi? Không. Tất cả những người có mã hành vi. Mã phải thực hiện trong cuộc đua. Xây dựng chiếc xe nên xảy ra trước khi cuộc đua ở một nơi được loại bỏ khỏi mã hành vi. Mark Seemans gọi nơi này là Thành phần gốc . Hầu hết mọi người gọi nó là chính.

Đó là một mô hình đơn giản. Trong main, xây dựng biểu đồ đối tượng sau đó gọi một phương thức hành vi trên một đối tượng trong biểu đồ đối tượng. Đó là nó. Đây là CHỈ nơi xây dựng và hành vi phải ở bên nhau.

Điều đó không có nghĩa là việc xây dựng phải là một đống mã thủ tục được trình bày theo trình tự chính. Bạn có thể tự do sử dụng mọi công cụ trong ngôn ngữ để thi công. Chỉ cần không trộn nó với hành vi.

Làm điều này bằng ngôn ngữ và không sử dụng một số khung DI hoặc bộ chứa IoC được gọi là DI thuần túy . Nó hoạt động rất tốt. Đã từ lâu. Chúng tôi thường chỉ gọi nó là tham chiếu đi qua .

Công cụ DI

Những gì một công cụ DI mua cho bạn là các chi tiết xây dựng được chuyển sang một ngôn ngữ khác (xml, json, bất cứ thứ gì) thực thi sự tách biệt giữa xây dựng và hành vi. Nếu bạn không tin tưởng các lập trình viên đồng nghiệp của bạn không sử dụng newkhi họ không nên hấp dẫn.

Mặt hạn chế là nó rất hấp dẫn khi để các chi tiết của công cụ DI lan rộng khắp cơ sở mã. Đôi khi lây nhiễm cơ sở mã với các chú thích độc quyền. Các ví dụ họ cung cấp chắc chắn khuyến khích điều này. Công cụ có xu hướng di chuyển vào không gian ngôn ngữ cho đến khi bạn không thể chỉ quảng cáo công việc như một công việc lập trình Java mà là công việc lập trình Java / Spring.

Nguyên tắc thiết kế

Từ lâu tôi đã lập trình các lớp Xe hơi vì rõ ràng tôi cần một lớp Xe hơi nếu tôi lập trình logic xe hơi. Nhưng với DI nó không quá rõ ràng với tôi. Vẫn còn băn khoăn rằng DI không tạo ra một lớp Xe hơi nếu tôi không có vai trò xác định cho nó?

Tôi nghĩ rằng bạn đang học về sự trừu tượng và thay đổi cách bạn quyết định một lớp học là cần thiết. Điều đó thật tốt. Nhưng đó không phải là về DI. DI không giúp bạn quyết định nếu bạn cần một lớp xe. DI giúp bạn giữ cho chiếc xe không biết và chăm sóc, nếu lốp xe là lốp Goodyear. DI giúp bạn theo dõi để biết nếu những chiếc xe được sản xuất tại Nhật Bản.

Một trong những câu hỏi cơ bản nhất trong thiết kế phần mềm là "những gì biết về cái gì?" Đó là điều chính mà sơ đồ UML cho bạn thấy. Khi bạn làm mới một cái gì đó bạn đang vượt qua giao diện của nó với thứ cụ thể mà bạn hiện đang gắn kết. Chiếc xe bây giờ phải biết rằng lốp xe là Goodyear. Thật tệ nếu Michelin muốn tài trợ cho bạn.

Tránh làm điều đó được gọi là theo nguyên tắc đảo ngược phụ thuộc . Chính thức, một mô-đun cấp cao (như lớp xe hơi) không nên phụ thuộc trực tiếp vào các mô-đun cấp thấp (như lớp GoodyearTire). Nó nên phụ thuộc vào sự trừu tượng hóa (như giao diện Tyre).

Một cách để tránh làm điều đó được gọi là Đảo ngược kiểm soát . Ở đây nhấn mạnh vào việc thay đổi dòng kiểm soát. Làm lốp xe di chuyển xe hay xe di chuyển lốp? Suy nghĩ về điều này đúng cách cho phép chúng ta không tĩnh đôi xe và lốp xe với nhau. DI là một cách đặc biệt để theo Inversion of Control.

Không ai trong số này cho bạn biết nếu bạn cần một lớp xe. Nếu bạn đang lập trình "logic xe hơi" thì thật tuyệt nếu có một nơi để giữ nó chứ không phải phân tán nó mọi nơi. Đừng dại dột nghĩ rằng logic xây dựng xe hơi cũng giống như logic hành vi của xe hơi nên tất cả phải sống ở cùng một nơi. Nhưng nếu bạn không có cuộn xác định cho một chiếc xe thì bạn cũng không cần. Đua xe máy quanh đường đua nếu bạn thích.

Ý tôi là có ổn không khi có Chỉ đạo lái xe, BoltsAndNuts trên bánh xe cho pit crew và tất cả các loại giao diện hài hước khác mà không có một ví dụ đại diện cho một chiếc xe?

DI hoặc không có DI, thật tốt khi có một ví dụ đại diện cho toàn bộ chiếc xe, nhưng trường hợp đó không phải là điều tôi muốn biết trực tiếp nếu tôi không phải làm thế. Hãy cho tôi một chiếc xe trừu tượng để tôi không phải quan tâm nếu nó chạy bằng xăng, dầu diesel hoặc điện khi tôi sử dụng nó. Đó chỉ là điều tôi nên quan tâm khi tôi xây dựng nó hoặc duy trì nó. Thật tuyệt nếu mã sử dụng xe hơi không cần phải biết hoặc quan tâm đến cách thức hoạt động của nó. "Tôi không biết. Tôi không muốn biết."


Sẽ thật tuyệt nếu bạn không sử dụng phép ẩn dụ của xe và pit crew cho một câu hỏi sử dụng chúng để đại diện cho mã. Nhưng vẫn là một câu trả lời tốt.
S. Tarık Çetin

6
Và tôi luôn nghĩ Inversion of Control là khi bạn lái trái nhưng xe rẽ phải ...
mkrieger1

CandiedOrange, vậy không có lớp Xe nào nếu nó không có giao diện có ý nghĩa và trách nhiệm được xác định rõ? Và không có Car.h trong dự án của tôi. Và đồng nghiệp nhìn vào mã của tôi và tự hỏi đó là một cửa hàng phụ tùng xe hơi hoặc ứng dụng quản lý nhà để xe? :)
Anton Petrov

@AntonPetrov "nếu nó trông giống như một con vịt và quạ giống như một con vịt, nhưng cần pin, bạn có thể có sự trừu tượng sai". Tên tốt là rất quan trọng và có thể khó nghĩ nhưng nên được thay đổi để phản ánh trách nhiệm được xác định rõ và giao diện có ý nghĩa để chúng không gây ngạc nhiên. Nếu tôi đọc tên thì nhìn vào giao diện và nghĩ rằng "đó là những gì tôi mong đợi" thì bạn có một cái tên hay.
candied_orange

15

Có vẻ như tôi không nên định nghĩa một lớp Xe hơi chỉ để giữ các bộ phận xe hơi bởi vì tất cả các bộ phận xe hơi sẽ phụ thuộc vào nhau và súp phụ tùng này là một chiếc xe hơi. Tôi có đúng không

Không.

Điểm tiêm phụ thuộc hoàn toàn không phải là để ngăn cản sự phụ thuộc. Hoàn toàn ngược lại.

Phụ thuộc tiêm là về câu hỏi: chiếc xe lấy phụ tùng từ đâu? Chúng được tạo ra ở đâu và như thế nào, và chiếc xe có được tài liệu tham khảo đến chúng như thế nào?

Ngây thơ, chiếc xe sẽ tự tạo ra các bộ phận của nó bằng cách gọi new. Điều này là dễ dàng và đơn giản, nhưng nó rất không linh hoạt. Chiếc xe phải biết tất cả mọi thứ cần thiết để tạo ra các bộ phận, và nếu bạn muốn có hai chiếc xe với các bộ phận khác nhau, logic đó cũng phải đi vào trong xe.

Với tiêm phụ thuộc, tất cả logic đó được đưa ra khỏi xe và đưa vào một thành phần cấu hình, tạo ra tất cả các bộ phận và nối dây các tham chiếu. Các phụ thuộc được cấu hình đầy đủ được đưa vào xe - và nhau.


1
Cuối cùng, một lời giải thích lành mạnh về dependency injectionkhái niệm.
anatoly techtonik

1
Tôi muốn nói rằng hệ thống phổ biến hơn cho cách tiếp cận "ngây thơ" không phải là chiếc xe sẽ gọi mới, mà là người sử dụng lớp xe sẽ gán các bộ phận của nó cho các thuộc tính của nó, và trong khi đó, lớp xe sẽ ở trong một trạng thái không xác định. DI cung cấp một phương tiện để đảm bảo tính hợp lệ của lớp.
whatsisname

2
@whatsisname Tôi có thể dễ dàng sử dụng DI để tạo các đối tượng khởi tạo không hợp lệ và một nửa. Xác nhận không phải là trách nhiệm DI. Cũng không phải là bất biến. DI có thể đảm nhận công việc xây dựng các đối tượng hợp lệ và bất biến. DI không có cách nào để thực thi rằng đó là những cách duy nhất những vật thể đó được xây dựng và sử dụng. Các đối tượng phải thực thi điều này.
candied_orange

@CandiedOrange: vì vậy bạn có thể dễ dàng sử dụng bất kỳ kỹ thuật nào để tạo ra một thứ gì đó nửa vời, vậy thì sao? Yêu cầu sự phụ thuộc tại nhà xây dựng và biến chúng thành bất biến, không phải là viên đạn bạc, nhưng dù sao đó cũng là một cách rất hữu ích để ngăn chặn nhiều tình huống xấu phổ biến.
whatsisname

0

Vì vậy, khi tôi đặt tất cả các xe đua của mình vào RaceTrack, sẽ không có các thực thể xe hơi mà có rất nhiều bộ phận xe hơi phụ thuộc vào nhau đua trên đường đua ?? Tôi có đúng không

Tốt. Có và không. Một thực thể xe hơi vẫn còn hiệu lực để có. Và một chiếc xe là nhiều hơn tổng số các bộ phận của nó. Bạn cũng cần một tài xế (trong thời gian này). Nhiều lần xe đua cũng có tên, chưa kể Make and Model của xe.

Có những chiếc xe về cơ bản là một bộ phận của các bộ phận, nhưng chính sự đóng gói và hợp tác của các bộ phận tạo nên một thứ lớn hơn: một chiếc xe hơi.

Vì vậy, thực sự, không. Một chiếc xe không chỉ là một túi kim loại và nhựa ngẫu nhiên. Đó là một cái máy.

Phụ thuộc tiêm không quá mức trong trường hợp này. Một cái gì đó khác phải xây dựng chiếc xe, đó sẽ là một lớp nhà máy hoặc đối tượng Builder. Chiếc xe không nên biết cách tự chế tạo.


2
Bạn có thể có DI mà không cần nhà máy hoặc đối tượng xây dựng. Các tham số constructor đơn giản là một phương thức hợp lệ hoạt động trong nhiều trường hợp.
whatsisname
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.