สำหรับฉันเมื่อเริ่มต้นจุดเหล่านี้ชัดเจนเมื่อคุณหยุดดูพวกเขาเป็นสิ่งต่าง ๆ เพื่อให้โค้ดของคุณง่ายขึ้น / เร็วขึ้นในการเขียน - นี่ไม่ใช่จุดประสงค์ของพวกเขา พวกเขามีประโยชน์หลายอย่าง:
(สิ่งนี้จะสูญเสียการเปรียบเทียบพิซซ่าเนื่องจากมันไม่ง่ายเลยที่จะเห็นภาพการใช้สิ่งนี้)
สมมติว่าคุณกำลังสร้างเกมง่ายๆบนหน้าจอและมันจะมีสิ่งมีชีวิตที่คุณโต้ตอบ
ตอบ: พวกเขาสามารถทำให้รหัสของคุณง่ายต่อการบำรุงรักษาในอนาคตโดยการแนะนำการเชื่อมต่อระหว่างส่วนหน้าของคุณและการใช้งานส่วนหลังของคุณ
คุณสามารถเขียนสิ่งนี้เพื่อเริ่มต้นเนื่องจากมีเพียงจะหมุนรอบ:
// This is our back-end implementation of a troll
class Troll
{
void Walk(int distance)
{
//Implementation here
}
}
ส่วนหน้า:
function SpawnCreature()
{
Troll aTroll = new Troll();
aTroll.Walk(1);
}
สองสัปดาห์ที่ผ่านมาการตลาดตัดสินใจว่าคุณต้องมี Orcs เช่นเดียวกับที่พวกเขาอ่านเกี่ยวกับพวกเขาใน twitter ดังนั้นคุณจะต้องทำอะไรเช่น:
class Orc
{
void Walk(int distance)
{
//Implementation (orcs are faster than trolls)
}
}
ส่วนหน้า:
void SpawnCreature(creatureType)
{
switch(creatureType)
{
case Orc:
Orc anOrc = new Orc();
anORc.Walk();
case Troll:
Troll aTroll = new Troll();
aTroll.Walk();
}
}
และคุณสามารถดูว่าสิ่งนี้เริ่มยุ่ง คุณสามารถใช้อินเทอร์เฟซที่นี่เพื่อให้ส่วนหน้าของคุณจะถูกเขียนหนึ่งครั้งและ (นี่คือบิตที่สำคัญ) ทดสอบแล้วคุณสามารถเชื่อมต่อรายการส่วนหลังเพิ่มเติมตามที่ต้องการ:
interface ICreature
{
void Walk(int distance)
}
public class Troll : ICreature
public class Orc : ICreature
//etc
ส่วนหน้าคือ:
void SpawnCreature(creatureType)
{
ICreature creature;
switch(creatureType)
{
case Orc:
creature = new Orc();
case Troll:
creature = new Troll();
}
creature.Walk();
}
ตอนนี้ส่วนหน้าสนใจเฉพาะส่วนต่อประสาน ICreature - มันไม่ได้กังวลเกี่ยวกับการใช้งานภายในของ troll หรือ orc แต่เพียงความจริงที่ว่าพวกเขาใช้ ICreature
จุดสำคัญที่ควรทราบเมื่อดูจากมุมมองนี้คือคุณสามารถใช้คลาสสิ่งมีชีวิตที่เป็นนามธรรมได้อย่างง่ายดายและจากมุมมองนี้สิ่งนี้มีผลเหมือนกัน
และคุณสามารถแยกสิ่งสร้างออกจากโรงงานได้:
public class CreatureFactory {
public ICreature GetCreature(creatureType)
{
ICreature creature;
switch(creatureType)
{
case Orc:
creature = new Orc();
case Troll:
creature = new Troll();
}
return creature;
}
}
และส่วนหน้าของเราก็จะกลายเป็น:
CreatureFactory _factory;
void SpawnCreature(creatureType)
{
ICreature creature = _factory.GetCreature(creatureType);
creature.Walk();
}
ส่วนหน้าตอนนี้ไม่จำเป็นต้องมีการอ้างอิงไปยังห้องสมุดที่มีการใช้งาน Troll และ Orc (ให้โรงงานอยู่ในห้องสมุดแยก) - มันไม่จำเป็นต้องรู้อะไรเกี่ยวกับพวกมันเลย
B: สมมติว่าคุณมีฟังก์ชั่นที่มีสิ่งมีชีวิตเพียงบางส่วนเท่านั้นที่จะมีในโครงสร้างข้อมูลที่เป็นเนื้อเดียวกันเช่น
interface ICanTurnToStone
{
void TurnToStone();
}
public class Troll: ICreature, ICanTurnToStone
ส่วนหน้าอาจเป็น:
void SpawnCreatureInSunlight(creatureType)
{
ICreature creature;
switch(creatureType)
{
case Orc:
creature = new Orc();
case Troll:
creature = new Troll();
}
creature.Walk();
if (creature is ICanTurnToStone)
{
(ICanTurnToStone)creature.TurnToStone();
}
}
C: การใช้งานสำหรับการฉีดพึ่งพา
กรอบการฉีดพึ่งพาส่วนใหญ่จะง่ายต่อการทำงานกับเมื่อมีการแต่งงานกันอย่างหลวม ๆ ระหว่างรหัสหน้าและการใช้งานปลายด้านหลัง ถ้าเราใช้ตัวอย่างโรงงานของเราด้านบนและให้โรงงานของเราใช้อินเทอร์เฟซ:
public interface ICreatureFactory {
ICreature GetCreature(string creatureType);
}
ส่วนหน้าของเราสามารถทำการฉีดได้ (เช่นตัวควบคุม MVC API) ผ่านตัวสร้าง (โดยทั่วไป):
public class CreatureController : Controller {
private readonly ICreatureFactory _factory;
public CreatureController(ICreatureFactory factory) {
_factory = factory;
}
public HttpResponseMessage TurnToStone(string creatureType) {
ICreature creature = _factory.GetCreature(creatureType);
creature.TurnToStone();
return Request.CreateResponse(HttpStatusCode.OK);
}
}
ด้วยโครงสร้าง DI ของเรา (เช่น Ninject หรือ Autofac) เราสามารถตั้งค่าเพื่อให้สร้างอินสแตนซ์ของ CreatureFactory ที่รันไทม์เมื่อใดก็ตามที่จำเป็นต้องมี ICreatureFactory ใน Constructor - สิ่งนี้ทำให้โค้ดของเราดีและเรียบง่าย
นอกจากนี้ยังหมายความว่าเมื่อเราเขียนการทดสอบหน่วยสำหรับตัวควบคุมของเราเราสามารถให้ ICreatureFactory ที่เยาะเย้ย (เช่นหากการดำเนินการที่เป็นรูปธรรมจำเป็นต้องมีการเข้าถึงฐานข้อมูลเราไม่ต้องการการทดสอบหน่วยของเราขึ้นอยู่กับว่า) และทดสอบรหัสในคอนโทรลเลอร์ของเรา .
D: มีการใช้งานอื่น ๆ เช่นคุณมีสองโครงการ A และ B ที่ด้วยเหตุผล 'มรดก' ไม่ได้มีโครงสร้างที่ดีและ A มีการอ้างอิงถึง B
จากนั้นคุณจะพบฟังก์ชันการทำงานใน B ที่จำเป็นต้องเรียกใช้วิธีการใน A แล้วคุณไม่สามารถทำได้โดยใช้การติดตั้งที่เป็นรูปธรรมเมื่อคุณได้รับการอ้างอิงแบบวงกลม
คุณสามารถมีอินเตอร์เฟสที่ประกาศใน B ว่าคลาสใน A นั้นนำมาใช้ วิธีการของคุณใน B สามารถส่งผ่านอินสแตนซ์ของคลาสที่ใช้อินเทอร์เฟซโดยไม่มีปัญหาแม้ว่าวัตถุคอนกรีตเป็นชนิดใน A.