Câu trả lời:
DI và Chiến lược hoạt động theo cùng một cách, nhưng Chiến lược được sử dụng cho các phụ thuộc chi tiết và ngắn hạn hơn.
Khi một đối tượng được định cấu hình với Chiến lược "cố định", chẳng hạn như khi đối tượng được xây dựng, sự phân biệt giữa Chiến lược và DI bị mờ. Nhưng trong kịch bản DI, điều bất thường hơn là sự phụ thuộc của các đối tượng thay đổi trong vòng đời của chúng, trong khi điều này không phải là hiếm với Strategy.
Ngoài ra, bạn có thể chuyển các chiến lược dưới dạng đối số cho các phương thức, trong khi khái niệm liên quan về tiêm đối số phương thức không phổ biến và hầu như chỉ được sử dụng trong bối cảnh kiểm thử tự động.
Chiến lược tập trung vào mục đích và khuyến khích bạn tạo một giao diện với các cách triển khai khác nhau tuân theo cùng một hợp đồng hành vi. DI thiên về việc thực hiện một số hành vi và cung cấp nó.
Với DI, bạn có thể phân rã chương trình của mình vì những lý do khác chứ không chỉ để có thể hoán đổi các phần của quá trình triển khai. Một giao diện được sử dụng trong DI với chỉ một triển khai là rất phổ biến. Một "Chiến lược" chỉ có một lần triển khai cụ thể (từ trước đến nay) không phải là một vấn đề thực sự nhưng có lẽ gần với DI.
in a DI scenario it is more unusual that the dependencies of objects change during their lifetimes, while this is not uncommon with Strategy
Sự khác biệt là những gì họ đang cố gắng đạt được. Mẫu Chiến lược được sử dụng trong các tình huống mà bạn biết rằng bạn muốn hoán đổi các triển khai. Ví dụ: bạn có thể muốn định dạng dữ liệu theo nhiều cách khác nhau - bạn có thể sử dụng mẫu chiến lược để hoán đổi một định dạng XML hoặc định dạng CSV, v.v.
Dependency Injection khác biệt ở chỗ người dùng không cố gắng thay đổi hành vi thời gian chạy. Theo ví dụ trên, chúng ta có thể đang tạo một chương trình xuất XML sử dụng trình định dạng XML. Thay vì cấu trúc mã như thế này:
public class DataExporter() {
XMLFormatter formatter = new XMLFormatter();
}
bạn sẽ 'đưa' định dạng vào hàm tạo:
public class DataExporter {
IFormatter formatter = null;
public DataExporter(IDataFormatter dataFormatter) {
this.formatter = dataFormatter;
}
}
DataExporter exporter = new DataExporter(new XMLFormatter());
Có một vài lý do cho Dependency Injection, nhưng lý do chính là để thử nghiệm. Bạn có thể gặp trường hợp bạn có một công cụ liên tục thuộc một số loại (chẳng hạn như cơ sở dữ liệu). Tuy nhiên, bạn có thể gặp khó khăn khi sử dụng cơ sở dữ liệu thực khi bạn đang chạy thử nghiệm liên tục. Vì vậy, đối với các trường hợp thử nghiệm của bạn, bạn sẽ đưa vào một cơ sở dữ liệu giả để không phải chịu chi phí đó.
Sử dụng ví dụ này, bạn có thể thấy sự khác biệt: chúng tôi luôn có kế hoạch sử dụng chiến lược lưu trữ dữ liệu và đó là chiến lược mà chúng tôi chuyển vào (phiên bản DB thực). Tuy nhiên, trong quá trình phát triển và thử nghiệm, chúng tôi muốn sử dụng các phụ thuộc khác nhau, vì vậy chúng tôi đưa các cụ thể khác nhau vào.
Bạn có thể sử dụng DI làm mẫu chiến lược, vì vậy bạn có thể hoán đổi thuật toán cần thiết cho từng khách hàng, nhưng DI có thể vượt xa điều đó vì nó là một cách để chỉ tách các phần của một ứng dụng, mà không phải là một phần của mô hình chiến lược.
Sẽ rất rủi ro nếu nói rằng DI chỉ là một mô hình chiến lược được đổi tên vì điều đó bắt đầu làm loãng những gì mà mô hình chiến lược thực sự dành cho, IMO.
Anh bạn, tiêm phụ thuộc là một mẫu tổng quát hơn, và nó nói về sự phụ thuộc vào những cái trừu tượng chứ không phải cụ thể và nó là một phần của mọi mẫu, nhưng Mẫu chiến lược là một giải pháp cho những vấn đề cụ thể hơn
đây là định nghĩa từ wikipedia:
DI:
Dependency injection (DI) trong lập trình máy tính hướng đối tượng là một mẫu thiết kế với nguyên tắc cốt lõi là tách hành vi khỏi phân giải phụ thuộc. Nói cách khác: một kỹ thuật tách rời các thành phần phần mềm phụ thuộc nhiều.
Mô hình chiến lược:
Trong lập trình máy tính, mẫu chiến lược (còn được gọi là mẫu chính sách) là một mẫu thiết kế phần mềm cụ thể, theo đó các thuật toán có thể được chọn trong thời gian chạy.
Mẫu chiến lược nhằm cung cấp một phương tiện để xác định một họ các thuật toán, đóng gói mỗi một thuật toán như một đối tượng và làm cho chúng có thể hoán đổi cho nhau. Mô hình chiến lược cho phép các thuật toán thay đổi độc lập với các khách hàng sử dụng chúng.
Chiến lược là những thứ cấp cao hơn được sử dụng để thay đổi cách tính toán mọi thứ. Với việc tiêm phụ thuộc, bạn không chỉ có thể thay đổi cách mọi thứ được tính toán mà còn thay đổi những gì đang có.
Đối với tôi, điều đó trở nên rõ ràng khi sử dụng các bài kiểm tra đơn vị. Để thực thi mã sản xuất, bạn phải ẩn tất cả dữ liệu (tức là riêng tư hoặc được bảo vệ); trong khi đó, với các bài kiểm tra đơn vị, hầu hết dữ liệu là công khai nên tôi có thể xem nó bằng Asserts.
Ví dụ về chiến lược:
public class Cosine {
private CalcStrategy strat;
// Constructor - strategy passed in as a type of DI
public Cosine(CalcStrategy s) {
strat = s;
}
}
public abstract class CalcStrategy {
public double goFigure(double angle);
}
public class RadianStrategy extends CalcStrategy {
public double goFigure(double angle) {
return (...);
}
}
public class DegreeStrategy extends CalcStrategy {
public double goFigure(double angle) {
return (...);
}
}
Lưu ý rằng không có dữ liệu công khai nào khác biệt giữa các chiến lược. Cũng không có bất kỳ phương pháp nào khác nhau. Cả hai chiến lược đều chia sẻ tất cả các chức năng và chữ ký giống nhau.
Bây giờ để tiêm phụ thuộc:
public class Cosine {
private Calc strat;
// Constructor - Dependency Injection.
public Cosine(Calc s) {
strat = s;
}
}
public class Calc {
private int numPasses = 0;
private double total = 0;
private double intermediate = 0;
public double goFigure(double angle) {
return(...);
}
public class CalcTestDouble extends Calc {
// NOTICE THE PUBLIC DATA.
public int numPasses = 0;
public double total = 0;
public double intermediate = 0;
public double goFigure(double angle) {
return (...);
}
}
Sử dụng:
public CosineTest {
@Test
public void testGoFigure() {
// Setup
CalcTestDouble calc = new CalcTestDouble();
Cosine instance = new Cosine(calc);
// Exercise
double actualAnswer = instance.goFigure(0.0);
// Verify
double tolerance = ...;
double expectedAnswer = ...;
assertEquals("GoFigure didn't work!", expectedAnswer,
actualAnswer, tolerance);
int expectedNumPasses = ...;
assertEquals("GoFigure had wrong number passes!",
expectedNumPasses, calc.numPasses);
double expectedIntermediate = ...;
assertEquals("GoFigure had wrong intermediate values!",
expectedIntermediate, calc.intermediate, tolerance);
}
}
Để ý 2 lần kiểm tra cuối cùng. Họ đã sử dụng dữ liệu công khai trong bài kiểm tra kép được đưa vào lớp được kiểm tra. Tôi không thể làm điều này với mã sản xuất vì nguyên tắc ẩn dữ liệu. Tôi không muốn chèn mã kiểm tra mục đích đặc biệt vào mã sản xuất. Dữ liệu công khai phải ở một lớp khác.
Thử nghiệm kép đã được tiêm. Điều đó khác với chỉ một chiến lược vì nó ảnh hưởng đến dữ liệu chứ không chỉ các chức năng.
Sự phụ thuộc là một sự cải tiến của mô hình chiến lược mà tôi sẽ giải thích ngắn gọn. Thường cần phải chọn giữa một số mô-đun thay thế trong thời gian chạy. Tất cả các mô-đun này đều triển khai một giao diện chung để chúng có thể được sử dụng thay thế cho nhau. Mục đích của mô hình chiến lược là để loại bỏ gánh nặng quyết định sử dụng mô-đun nào (tức là "chiến lược cụ thể" hoặc phụ thuộc nào) bằng cách gói gọn quá trình ra quyết định thành một đối tượng riêng biệt mà tôi sẽ gọi là đối tượng chiến lược.
Việc tiêm phụ thuộc sẽ tinh chỉnh mô hình chiến lược bằng cách không chỉ quyết định sử dụng chiến lược cụ thể nào mà còn tạo ra một phiên bản của chiến lược cụ thể và "đưa" nó trở lại mô-đun gọi. Điều này hữu ích ngay cả khi chỉ có một sự phụ thuộc duy nhất vì kiến thức về cách quản lý (khởi tạo, v.v.) cá thể chiến lược cụ thể cũng có thể bị ẩn trong đối tượng chiến lược.
Trên thực tế, việc tiêm phụ thuộc cũng trông rất giống với mẫu Cầu. Đối với tôi (và theo định nghĩa), mẫu Bridge là để điều chỉnh các phiên bản triển khai khác nhau , trong khi mẫu Strategy dành cho logic hoàn toàn khác. Nhưng mã mẫu có vẻ như nó đang sử dụng DI. Vì vậy, có thể DI chỉ là một kỹ thuật hoặc thực hiện?
Chiến lược là một đấu trường để sử dụng kỹ năng tiêm phụ thuộc của bạn. Các cách thực sự để thực hiện tiêm phụ thuộc như sau: -
Tuy nhiên, có một điều khiến chiến lược trở nên khác biệt. Như bạn đã biết trong Unity khi ứng dụng khởi động tất cả các phụ thuộc đã được thiết lập và chúng tôi không thể thay đổi thêm. Nhưng chiến lược hỗ trợ thay đổi phụ thuộc thời gian chạy. Nhưng CHÚNG TÔI phải quản lý / tiêm vào sự phụ thuộc, không phải trách nhiệm của Chiến lược!
Trên thực tế, chiến lược không nói về tiêm phụ thuộc. Nếu cần, nó có thể được thực hiện thông qua Abstract Factory bên trong một mẫu Chiến lược. Strategy chỉ nói về việc tạo một nhóm các lớp có giao diện và 'chơi' với nó. Trong khi chơi, nếu chúng ta thấy các lớp nằm ở một tầng khác nhau thì chúng ta phải tự tiêm nó chứ không phải việc của Chiến lược.
Nếu chúng ta xem xét các nguyên tắc SOLID - Chúng ta sử dụng Mô hình chiến lược cho Nguyên tắc đóng mở và tiêm phụ thuộc cho Nguyên tắc đảo ngược phụ thuộc