อะไรคือความแตกต่างระหว่าง Strategy Pattern และ Dependency Injection?


96

รูปแบบกลยุทธ์และ Dependency Injection ช่วยให้เราสามารถตั้งค่า / ฉีดวัตถุในขณะทำงานได้ อะไรคือความแตกต่างระหว่าง Strategy Pattern และ Dependency Injection?


รูปแบบกลยุทธ์อาจใช้ Dependency Injection
TechWisdom

คำตอบ:


110

DI และ Strategy ทำงานในลักษณะเดียวกัน แต่ Strategy ใช้สำหรับการอ้างอิงที่ละเอียดกว่าและมีอายุสั้น

เมื่อวัตถุถูกกำหนดค่าด้วยกลยุทธ์ "คงที่" ตัวอย่างเช่นเมื่อสร้างวัตถุความแตกต่างระหว่างกลยุทธ์และ DI จะพร่ามัว แต่ในสถานการณ์ DI เป็นเรื่องผิดปกติมากกว่าที่การอ้างอิงของวัตถุจะเปลี่ยนไปในช่วงชีวิตของพวกมันในขณะที่กลยุทธ์นี้ไม่ใช่เรื่องแปลก

นอกจากนี้คุณสามารถส่งผ่านกลยุทธ์เป็นอาร์กิวเมนต์ไปยังวิธีการได้ในขณะที่แนวคิดที่เกี่ยวข้องของการฉีดอาร์กิวเมนต์วิธีการนั้นไม่แพร่หลายและส่วนใหญ่จะใช้ในบริบทของการทดสอบอัตโนมัติเท่านั้น

กลยุทธ์มุ่งเน้นไปที่เจตนาและสนับสนุนให้คุณสร้างอินเทอร์เฟซที่มีการใช้งานที่แตกต่างกันซึ่งเป็นไปตามสัญญาพฤติกรรมเดียวกัน DI เป็นข้อมูลเพิ่มเติมเกี่ยวกับการมีพฤติกรรมบางอย่างและนำเสนอ

ด้วย DI คุณสามารถแยกย่อยโปรแกรมของคุณด้วยเหตุผลอื่น ๆ นอกเหนือจากเพียงเพื่อให้สามารถสลับบางส่วนของการใช้งานได้ อินเทอร์เฟซที่ใช้ใน DI ที่มีการใช้งานเพียงครั้งเดียวเป็นเรื่องปกติมาก "กลยุทธ์" ที่มีการนำไปใช้อย่างเป็นรูปธรรมเพียงครั้งเดียว (เคย) ไม่ใช่ปัญหาที่แท้จริง แต่น่าจะใกล้เคียงกับ DI มากกว่า


อินเทอร์เฟซที่ใช้ใน DI ที่มีการใช้งานเพียงครั้งเดียวนั้นเป็นเรื่องปกติมากดังนั้น DI ในกรณีนี้คืออะไร?
Kalpesh Soni

3
คำพูดนี้อธิบายได้ทั้งหมด: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

กลยุทธ์: ชั้นเรียนได้รับการออกแบบเพื่อให้สามารถกำหนดค่าด้วยอัลกอริทึมในขณะรันไทม์ DI: คลาสดังกล่าวได้รับอัลกอริทึม (ออบเจ็กต์กลยุทธ์) ที่ฉีดเข้าไปในขณะรันไทม์ จาก GoF ออกแบบหน่วยความจำรูปแบบที่w3sdesign.com
GFranke

39

ความแตกต่างคือสิ่งที่พวกเขาพยายามบรรลุ รูปแบบกลยุทธ์ถูกใช้ในสถานการณ์ที่คุณรู้ว่าคุณต้องการเปลี่ยนการนำไปใช้งาน ตัวอย่างเช่นคุณอาจต้องการจัดรูปแบบข้อมูลด้วยวิธีต่างๆ - คุณสามารถใช้รูปแบบกลยุทธ์เพื่อสลับตัวจัดรูปแบบ XML หรือฟอร์แมตเตอร์ CSV เป็นต้น

Dependency Injection แตกต่างกันตรงที่ผู้ใช้ไม่ได้พยายามเปลี่ยนลักษณะการทำงานของรันไทม์ จากตัวอย่างด้านบนเราอาจสร้างโปรแกรมส่งออก XML ที่ใช้ฟอร์แมตเตอร์ XML แทนที่จะจัดโครงสร้างโค้ดเช่นนี้:

public class DataExporter() {
  XMLFormatter formatter = new XMLFormatter();
}

คุณจะ 'ฉีด' ฟอร์แมตเตอร์ในตัวสร้าง:

public class DataExporter {
  IFormatter formatter = null;

  public DataExporter(IDataFormatter dataFormatter) {
    this.formatter = dataFormatter;
  }
}

DataExporter exporter = new DataExporter(new XMLFormatter());

มีเหตุผลบางประการสำหรับ Dependency Injection แต่เหตุผลหลักสำหรับการทดสอบ คุณอาจมีกรณีที่คุณมีเครื่องมือคงอยู่ในบางประเภท (เช่นฐานข้อมูล) อย่างไรก็ตามการใช้ฐานข้อมูลจริงอาจเป็นเรื่องยากเมื่อคุณทำการทดสอบซ้ำ ๆ ดังนั้นสำหรับกรณีทดสอบของคุณคุณจะต้องฉีดฐานข้อมูลจำลองเพื่อที่คุณจะได้ไม่ต้องเสียค่าใช้จ่ายนั้น

เมื่อใช้ตัวอย่างนี้คุณจะเห็นความแตกต่าง: เรามักจะวางแผนโดยใช้กลยุทธ์การจัดเก็บข้อมูลและเป็นกลยุทธ์ที่เราส่งผ่าน (อินสแตนซ์ DB จริง) อย่างไรก็ตามในการพัฒนาและการทดสอบเราต้องการใช้การอ้างอิงที่แตกต่างกันดังนั้นเราจึงฉีดคอนกรีตที่แตกต่างกัน


28

คุณสามารถใช้ DI เป็นรูปแบบกลยุทธ์เพื่อให้คุณสามารถสลับอัลกอริทึมที่จำเป็นสำหรับลูกค้าแต่ละรายได้ แต่ DI สามารถไปได้ไกลกว่านั้นเนื่องจากเป็นเพียงวิธีการแยกส่วนของแอปพลิเคชันซึ่งจะไม่เป็นส่วนหนึ่งของ รูปแบบกลยุทธ์

มันจะมีความเสี่ยงที่จะบอกว่า DI เป็นเพียงรูปแบบกลยุทธ์ที่เปลี่ยนชื่อเนื่องจากมันเริ่มที่จะเจือจางรูปแบบกลยุทธ์ที่แท้จริงสำหรับ IMO


2
ฉันคิดว่าฉันเข้าใจส่วนสำคัญของคุณ แต่ฉันไม่สามารถพูดเป็นคำได้อย่างถูกต้อง ... ดังนั้นคำพูดของคุณ DI จึงเป็นรูปแบบการนำไปใช้มากกว่าในขณะที่กลยุทธ์เป็นรูปแบบการออกแบบมากกว่าและวิธีหนึ่งในการนำกลยุทธ์ไปใช้คือผ่าน DI?
Robert Gould

1
ฟังดูเหมือนเป็นวิธีที่ดีที่จะใส่มัน DI เป็นมากกว่ารูปแบบกลยุทธ์ ฉันพบความสับสนเหมือนกันกับ AOP ที่ผู้คนคิดว่าเป็นรูปแบบโรงงาน ฉันคิดว่า DI สามารถใช้รูปแบบกลยุทธ์ได้ดังนั้นการ rewording ของคุณจะดูดีมาก :)
James Black

15

Dude, dependency injection เป็นรูปแบบทั่วไปมากกว่าและเกี่ยวกับการพึ่งพานามธรรมไม่ใช่คอนกรีตและเป็นส่วนหนึ่งของทุกรูปแบบ แต่รูปแบบกลยุทธ์เป็นวิธีแก้ปัญหาที่เฉพาะเจาะจงมากขึ้น

นี่คือคำจำกัดความจาก wikipedia:

DI:

Dependency injection (DI) ในการเขียนโปรแกรมคอมพิวเตอร์เชิงวัตถุเป็นรูปแบบการออกแบบที่มีหลักการสำคัญในการแยกพฤติกรรมออกจากการแก้ปัญหาการพึ่งพา กล่าวอีกนัยหนึ่งคือเทคนิคในการแยกส่วนประกอบซอฟต์แวร์ที่ต้องพึ่งพาสูง

รูปแบบกลยุทธ์:

ในการเขียนโปรแกรมคอมพิวเตอร์รูปแบบกลยุทธ์ (หรือที่เรียกว่ารูปแบบนโยบาย) คือรูปแบบการออกแบบซอฟต์แวร์โดยเฉพาะโดยสามารถเลือกอัลกอริทึมในขณะรันไทม์ได้

รูปแบบกลยุทธ์มีจุดมุ่งหมายเพื่อให้วิธีการกำหนดกลุ่มของอัลกอริทึมห่อหุ้มแต่ละส่วนเป็นวัตถุและทำให้ใช้แทนกันได้ รูปแบบกลยุทธ์ช่วยให้อัลกอริทึมแตกต่างกันไปโดยไม่ขึ้นกับลูกค้าที่ใช้


4
ฉันชอบส่วน "เพื่อน" ในคำอธิบายของคุณเป็นพิเศษ :-)
johey

7

กลยุทธ์เป็นสิ่งระดับสูงที่ใช้ในการเปลี่ยนแปลงวิธีคำนวณสิ่งต่างๆ ด้วยการฉีดแบบพึ่งพาคุณสามารถเปลี่ยนไม่เพียงแค่วิธีคำนวณสิ่งต่าง ๆ เท่านั้น แต่ยังเปลี่ยนสิ่งที่มีอยู่ด้วย

สำหรับฉันมันชัดเจนเมื่อใช้การทดสอบหน่วย สำหรับการเรียกใช้รหัสการผลิตคุณมีข้อมูลทั้งหมดที่ซ่อนอยู่ (เช่นส่วนตัวหรือได้รับการป้องกัน) ในขณะที่การทดสอบหน่วยข้อมูลส่วนใหญ่เป็นแบบสาธารณะดังนั้นฉันจึงสามารถดูได้ด้วย Asserts


ตัวอย่างกลยุทธ์:

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 (...);
  }
}

สังเกตว่าไม่มีข้อมูลสาธารณะที่แตกต่างกันระหว่างกลยุทธ์ ไม่มีวิธีการอื่นใด ทั้งสองกลยุทธ์มีฟังก์ชันและลายเซ็นเหมือนกันทั้งหมด


ตอนนี้สำหรับการฉีดแบบพึ่งพา:

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 (...);
  }
}

ใช้:

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 ครั้งล่าสุด พวกเขาใช้ข้อมูลสาธารณะในการทดสอบสองครั้งที่ฉีดเข้าไปในชั้นเรียนที่กำลังทดสอบ ฉันไม่สามารถทำสิ่งนี้กับรหัสการผลิตได้เนื่องจากหลักการซ่อนข้อมูล ฉันไม่ต้องการให้ใส่รหัสการทดสอบวัตถุประสงค์พิเศษในรหัสการผลิต ข้อมูลสาธารณะต้องอยู่ในคลาสอื่น

ฉีดทดสอบสองครั้ง ซึ่งแตกต่างจากเพียงแค่กลยุทธ์เนื่องจากส่งผลกระทบต่อข้อมูลและไม่ใช่แค่หน้าที่เท่านั้น


4

Dependency injection เป็นการปรับแต่งรูปแบบกลยุทธ์ซึ่งผมจะอธิบายสั้น ๆ บ่อยครั้งที่จำเป็นต้องเลือกระหว่างโมดูลทางเลือกต่างๆในขณะรันไทม์ โมดูลเหล่านี้ล้วนใช้อินเทอร์เฟซทั่วไปเพื่อให้สามารถใช้แทนกันได้ จุดประสงค์ของรูปแบบกลยุทธ์คือการขจัดภาระในการตัดสินใจว่าจะใช้โมดูลใด (กล่าวคือ "กลยุทธ์ที่เป็นรูปธรรม" หรือการพึ่งพา) โดยการห่อหุ้มกระบวนการตัดสินใจไว้ในวัตถุแยกต่างหากซึ่งฉันจะเรียกว่าวัตถุกลยุทธ์

การฉีดแบบพึ่งพาจะปรับแต่งรูปแบบกลยุทธ์โดยไม่เพียง แต่ตัดสินใจว่าจะใช้กลยุทธ์ใดที่เป็นรูปธรรมเท่านั้น แต่ยังสร้างตัวอย่างของกลยุทธ์ที่เป็นรูปธรรมและ "ฉีด" กลับเข้าไปในโมดูลการเรียกใช้ สิ่งนี้มีประโยชน์แม้ว่าจะมีการพึ่งพาเพียงครั้งเดียวเนื่องจากความรู้ในการจัดการ (การเริ่มต้น ฯลฯ ) อินสแตนซ์กลยุทธ์ที่เป็นรูปธรรมยังสามารถซ่อนอยู่ในวัตถุกลยุทธ์ได้


1

ที่จริงแล้วการฉีดแบบพึ่งพายังมีลักษณะคล้ายกับรูปแบบสะพาน สำหรับฉัน (และตามคำจำกัดความ) รูปแบบ Bridge คือการรองรับการใช้งานเวอร์ชันต่างๆในขณะที่รูปแบบกลยุทธ์สำหรับตรรกะที่แตกต่างกันโดยสิ้นเชิง แต่โค้ดตัวอย่างดูเหมือนว่ากำลังใช้ DI ดังนั้น DI อาจเป็นเพียงเทคนิคหรือการนำไปใช้งาน?


0

กลยุทธ์เป็นเวทีในการใช้ทักษะการฉีดพึ่งพาของคุณ วิธีที่แท้จริงในการใช้การฉีดการพึ่งพามีดังนี้: -

  1. เหตุการณ์
  2. ไฟล์คอนฟิกูเรชันของเอกภาพ / แผนผังโครงสร้าง (หรือทางโปรแกรม) เป็นต้น
  3. วิธีการขยาย
  4. รูปแบบโรงงานนามธรรม
  5. รูปแบบการควบคุมแบบผกผัน (ใช้ทั้งกลยุทธ์และโรงงานนามธรรม)

มีสิ่งหนึ่งที่ทำให้กลยุทธ์แตกต่างกัน ดังที่คุณทราบใน Unity เมื่อแอปพลิเคชันเริ่มต้นขึ้นการอ้างอิงทั้งหมดถูกตั้งค่าและเราไม่สามารถเปลี่ยนแปลงได้เพิ่มเติม แต่กลยุทธ์รองรับการเปลี่ยนแปลงการพึ่งพารันไทม์ แต่เราต้องจัดการ / อัดฉีดการพึ่งพาไม่ใช่ความรับผิดชอบของ Strategy!

จริงๆแล้วกลยุทธ์ไม่ได้พูดถึงการฉีดแบบพึ่งพา หากต้องการก็สามารถทำได้ผ่าน Abstract Factory ภายในรูปแบบกลยุทธ์ กลยุทธ์พูดถึงการสร้างกลุ่มคลาสที่มีอินเทอร์เฟซและ 'เล่น' กับมันเท่านั้น ในขณะที่เล่นหากเราพบว่าคลาสอยู่ในระดับอื่นเราจะต้องฉีดมันเอง แต่ไม่ใช่งานของกลยุทธ์


0

หากเราพิจารณาหลักการของ SOLID - เราใช้รูปแบบกลยุทธ์สำหรับหลักการปิดแบบเปิดและการฉีดพึ่งพาสำหรับหลักการผกผันการพึ่งพา


1
ฉันไม่แน่ใจว่าฉันทำตามคุณช่วยอธิบายให้ละเอียดได้ไหมว่ากลยุทธ์เกี่ยวข้องกับหลักการเปิด / ปิดอย่างไรและ DI เกี่ยวข้องกับ DIP อย่างไร
Adam Parkin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.