รูปแบบกลยุทธ์และ Dependency Injection ช่วยให้เราสามารถตั้งค่า / ฉีดวัตถุในขณะทำงานได้ อะไรคือความแตกต่างระหว่าง Strategy Pattern และ Dependency Injection?
รูปแบบกลยุทธ์และ Dependency Injection ช่วยให้เราสามารถตั้งค่า / ฉีดวัตถุในขณะทำงานได้ อะไรคือความแตกต่างระหว่าง Strategy Pattern และ Dependency Injection?
คำตอบ:
DI และ Strategy ทำงานในลักษณะเดียวกัน แต่ Strategy ใช้สำหรับการอ้างอิงที่ละเอียดกว่าและมีอายุสั้น
เมื่อวัตถุถูกกำหนดค่าด้วยกลยุทธ์ "คงที่" ตัวอย่างเช่นเมื่อสร้างวัตถุความแตกต่างระหว่างกลยุทธ์และ DI จะพร่ามัว แต่ในสถานการณ์ DI เป็นเรื่องผิดปกติมากกว่าที่การอ้างอิงของวัตถุจะเปลี่ยนไปในช่วงชีวิตของพวกมันในขณะที่กลยุทธ์นี้ไม่ใช่เรื่องแปลก
นอกจากนี้คุณสามารถส่งผ่านกลยุทธ์เป็นอาร์กิวเมนต์ไปยังวิธีการได้ในขณะที่แนวคิดที่เกี่ยวข้องของการฉีดอาร์กิวเมนต์วิธีการนั้นไม่แพร่หลายและส่วนใหญ่จะใช้ในบริบทของการทดสอบอัตโนมัติเท่านั้น
กลยุทธ์มุ่งเน้นไปที่เจตนาและสนับสนุนให้คุณสร้างอินเทอร์เฟซที่มีการใช้งานที่แตกต่างกันซึ่งเป็นไปตามสัญญาพฤติกรรมเดียวกัน DI เป็นข้อมูลเพิ่มเติมเกี่ยวกับการมีพฤติกรรมบางอย่างและนำเสนอ
ด้วย DI คุณสามารถแยกย่อยโปรแกรมของคุณด้วยเหตุผลอื่น ๆ นอกเหนือจากเพียงเพื่อให้สามารถสลับบางส่วนของการใช้งานได้ อินเทอร์เฟซที่ใช้ใน DI ที่มีการใช้งานเพียงครั้งเดียวเป็นเรื่องปกติมาก "กลยุทธ์" ที่มีการนำไปใช้อย่างเป็นรูปธรรมเพียงครั้งเดียว (เคย) ไม่ใช่ปัญหาที่แท้จริง แต่น่าจะใกล้เคียงกับ 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
ความแตกต่างคือสิ่งที่พวกเขาพยายามบรรลุ รูปแบบกลยุทธ์ถูกใช้ในสถานการณ์ที่คุณรู้ว่าคุณต้องการเปลี่ยนการนำไปใช้งาน ตัวอย่างเช่นคุณอาจต้องการจัดรูปแบบข้อมูลด้วยวิธีต่างๆ - คุณสามารถใช้รูปแบบกลยุทธ์เพื่อสลับตัวจัดรูปแบบ 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 จริง) อย่างไรก็ตามในการพัฒนาและการทดสอบเราต้องการใช้การอ้างอิงที่แตกต่างกันดังนั้นเราจึงฉีดคอนกรีตที่แตกต่างกัน
คุณสามารถใช้ DI เป็นรูปแบบกลยุทธ์เพื่อให้คุณสามารถสลับอัลกอริทึมที่จำเป็นสำหรับลูกค้าแต่ละรายได้ แต่ DI สามารถไปได้ไกลกว่านั้นเนื่องจากเป็นเพียงวิธีการแยกส่วนของแอปพลิเคชันซึ่งจะไม่เป็นส่วนหนึ่งของ รูปแบบกลยุทธ์
มันจะมีความเสี่ยงที่จะบอกว่า DI เป็นเพียงรูปแบบกลยุทธ์ที่เปลี่ยนชื่อเนื่องจากมันเริ่มที่จะเจือจางรูปแบบกลยุทธ์ที่แท้จริงสำหรับ IMO
Dude, dependency injection เป็นรูปแบบทั่วไปมากกว่าและเกี่ยวกับการพึ่งพานามธรรมไม่ใช่คอนกรีตและเป็นส่วนหนึ่งของทุกรูปแบบ แต่รูปแบบกลยุทธ์เป็นวิธีแก้ปัญหาที่เฉพาะเจาะจงมากขึ้น
นี่คือคำจำกัดความจาก wikipedia:
DI:
Dependency injection (DI) ในการเขียนโปรแกรมคอมพิวเตอร์เชิงวัตถุเป็นรูปแบบการออกแบบที่มีหลักการสำคัญในการแยกพฤติกรรมออกจากการแก้ปัญหาการพึ่งพา กล่าวอีกนัยหนึ่งคือเทคนิคในการแยกส่วนประกอบซอฟต์แวร์ที่ต้องพึ่งพาสูง
รูปแบบกลยุทธ์:
ในการเขียนโปรแกรมคอมพิวเตอร์รูปแบบกลยุทธ์ (หรือที่เรียกว่ารูปแบบนโยบาย) คือรูปแบบการออกแบบซอฟต์แวร์โดยเฉพาะโดยสามารถเลือกอัลกอริทึมในขณะรันไทม์ได้
รูปแบบกลยุทธ์มีจุดมุ่งหมายเพื่อให้วิธีการกำหนดกลุ่มของอัลกอริทึมห่อหุ้มแต่ละส่วนเป็นวัตถุและทำให้ใช้แทนกันได้ รูปแบบกลยุทธ์ช่วยให้อัลกอริทึมแตกต่างกันไปโดยไม่ขึ้นกับลูกค้าที่ใช้
กลยุทธ์เป็นสิ่งระดับสูงที่ใช้ในการเปลี่ยนแปลงวิธีคำนวณสิ่งต่างๆ ด้วยการฉีดแบบพึ่งพาคุณสามารถเปลี่ยนไม่เพียงแค่วิธีคำนวณสิ่งต่าง ๆ เท่านั้น แต่ยังเปลี่ยนสิ่งที่มีอยู่ด้วย
สำหรับฉันมันชัดเจนเมื่อใช้การทดสอบหน่วย สำหรับการเรียกใช้รหัสการผลิตคุณมีข้อมูลทั้งหมดที่ซ่อนอยู่ (เช่นส่วนตัวหรือได้รับการป้องกัน) ในขณะที่การทดสอบหน่วยข้อมูลส่วนใหญ่เป็นแบบสาธารณะดังนั้นฉันจึงสามารถดูได้ด้วย 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 ครั้งล่าสุด พวกเขาใช้ข้อมูลสาธารณะในการทดสอบสองครั้งที่ฉีดเข้าไปในชั้นเรียนที่กำลังทดสอบ ฉันไม่สามารถทำสิ่งนี้กับรหัสการผลิตได้เนื่องจากหลักการซ่อนข้อมูล ฉันไม่ต้องการให้ใส่รหัสการทดสอบวัตถุประสงค์พิเศษในรหัสการผลิต ข้อมูลสาธารณะต้องอยู่ในคลาสอื่น
ฉีดทดสอบสองครั้ง ซึ่งแตกต่างจากเพียงแค่กลยุทธ์เนื่องจากส่งผลกระทบต่อข้อมูลและไม่ใช่แค่หน้าที่เท่านั้น
Dependency injection เป็นการปรับแต่งรูปแบบกลยุทธ์ซึ่งผมจะอธิบายสั้น ๆ บ่อยครั้งที่จำเป็นต้องเลือกระหว่างโมดูลทางเลือกต่างๆในขณะรันไทม์ โมดูลเหล่านี้ล้วนใช้อินเทอร์เฟซทั่วไปเพื่อให้สามารถใช้แทนกันได้ จุดประสงค์ของรูปแบบกลยุทธ์คือการขจัดภาระในการตัดสินใจว่าจะใช้โมดูลใด (กล่าวคือ "กลยุทธ์ที่เป็นรูปธรรม" หรือการพึ่งพา) โดยการห่อหุ้มกระบวนการตัดสินใจไว้ในวัตถุแยกต่างหากซึ่งฉันจะเรียกว่าวัตถุกลยุทธ์
การฉีดแบบพึ่งพาจะปรับแต่งรูปแบบกลยุทธ์โดยไม่เพียง แต่ตัดสินใจว่าจะใช้กลยุทธ์ใดที่เป็นรูปธรรมเท่านั้น แต่ยังสร้างตัวอย่างของกลยุทธ์ที่เป็นรูปธรรมและ "ฉีด" กลับเข้าไปในโมดูลการเรียกใช้ สิ่งนี้มีประโยชน์แม้ว่าจะมีการพึ่งพาเพียงครั้งเดียวเนื่องจากความรู้ในการจัดการ (การเริ่มต้น ฯลฯ ) อินสแตนซ์กลยุทธ์ที่เป็นรูปธรรมยังสามารถซ่อนอยู่ในวัตถุกลยุทธ์ได้
ที่จริงแล้วการฉีดแบบพึ่งพายังมีลักษณะคล้ายกับรูปแบบสะพาน สำหรับฉัน (และตามคำจำกัดความ) รูปแบบ Bridge คือการรองรับการใช้งานเวอร์ชันต่างๆในขณะที่รูปแบบกลยุทธ์สำหรับตรรกะที่แตกต่างกันโดยสิ้นเชิง แต่โค้ดตัวอย่างดูเหมือนว่ากำลังใช้ DI ดังนั้น DI อาจเป็นเพียงเทคนิคหรือการนำไปใช้งาน?
กลยุทธ์เป็นเวทีในการใช้ทักษะการฉีดพึ่งพาของคุณ วิธีที่แท้จริงในการใช้การฉีดการพึ่งพามีดังนี้: -
มีสิ่งหนึ่งที่ทำให้กลยุทธ์แตกต่างกัน ดังที่คุณทราบใน Unity เมื่อแอปพลิเคชันเริ่มต้นขึ้นการอ้างอิงทั้งหมดถูกตั้งค่าและเราไม่สามารถเปลี่ยนแปลงได้เพิ่มเติม แต่กลยุทธ์รองรับการเปลี่ยนแปลงการพึ่งพารันไทม์ แต่เราต้องจัดการ / อัดฉีดการพึ่งพาไม่ใช่ความรับผิดชอบของ Strategy!
จริงๆแล้วกลยุทธ์ไม่ได้พูดถึงการฉีดแบบพึ่งพา หากต้องการก็สามารถทำได้ผ่าน Abstract Factory ภายในรูปแบบกลยุทธ์ กลยุทธ์พูดถึงการสร้างกลุ่มคลาสที่มีอินเทอร์เฟซและ 'เล่น' กับมันเท่านั้น ในขณะที่เล่นหากเราพบว่าคลาสอยู่ในระดับอื่นเราจะต้องฉีดมันเอง แต่ไม่ใช่งานของกลยุทธ์
หากเราพิจารณาหลักการของ SOLID - เราใช้รูปแบบกลยุทธ์สำหรับหลักการปิดแบบเปิดและการฉีดพึ่งพาสำหรับหลักการผกผันการพึ่งพา