Sự khác biệt giữa mô hình chiến lược và tiêm phụ thuộc là gì?


95

Mẫu chiến lược và Dependency Injection đều cho phép chúng ta thiết lập / chèn các đối tượng tại thời điểm chạy. Sự khác biệt giữa mô hình chiến lược và tiêm phụ thuộc là gì?


Chiến lược mô hình có thể sử dụng Dependency Injection
TechWisdom

Câu trả lời:


107

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.


Một giao diện được sử dụng trong DI với chỉ một cách triển khai là rất phổ biến - vậy DI là gì trong trường hợp cụ thể này?
Kalpesh Soni

3
Trích dẫn này về cơ bản giải thích tất cả:in a DI scenario it is more unusual that the dependencies of objects change during their lifetimes, while this is not uncommon with Strategy
Sergey Telshevsky

Chiến lược: Các lớp được thiết kế để chúng có thể được định cấu hình bằng một thuật toán tại thời điểm chạy. DI: Các lớp như vậy nhận được một thuật toán (một đối tượng Chiến lược) được đưa vào trong thời gian chạy. Từ Bộ nhớ các mẫu thiết kế của GoF tại w3sdesign.com .
GFranke

39

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.


28

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.


2
Tôi nghĩ rằng tôi hiểu ý chính của bạn, nhưng tôi không thể diễn đạt nó bằng từ ngữ một cách chính xác ... Vì vậy, câu nói của bạn DI là một mô hình triển khai trong khi chiến lược lại là một mô hình thiết kế, và một cách để thực hiện chiến lược là thông qua DI?
Robert Gould

1
Nghe có vẻ là một cách hay. DI không chỉ là một mô hình chiến lược. Tôi nhận thấy sự nhầm lẫn tương tự với AOP, nơi mọi người nghĩ rằng đó là một mẫu nhà máy. Tôi nghĩ DI có thể thực hiện mô hình chiến lược, vì vậy bản ghi lại của bạn sẽ có vẻ tuyệt vời. :)
James Black

14

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.


3
Tôi đặc biệt thích phần "công tử" trong lời giải thích của bạn. :-)
johey 17/07/19

7

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.


4

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.


1

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?


0

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: -

  1. Sự kiện
  2. Các tệp cấu hình của bản đồ cấu trúc / thống nhất (hoặc theo chương trình), v.v.
  3. Phương pháp mở rộng
  4. Mô hình nhà máy trừu tượng
  5. Đảo ngược mẫu điều khiển (được sử dụng bởi cả chiến lược và Nhà máy Tóm tắt)

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.


0

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


1
Tôi không chắc mình làm theo, bạn có thể nói rõ hơn về cách Chiến lược liên quan đến nguyên tắc Mở / Đóng và DI liên quan như thế nào đến DIP?
Adam Parkin
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.