บริบทแวดล้อมกับการสร้างคอนสตรัคชัน


9

ฉันมีคลาสแกนกลางจำนวนมากที่ต้องการ ISessionContext ของฐานข้อมูล ILogManager สำหรับบันทึกและ IService ที่ใช้สำหรับสื่อสารกับบริการอื่น ฉันต้องการใช้การฉีดพึ่งพาสำหรับชั้นนี้ใช้โดยชั้นเรียนหลักทั้งหมด

ฉันใช้งานได้สองแบบ คลาสหลักที่ยอมรับ IAmbientContext กับคลาสทั้งหมดสามคลาสหรือฉีดสำหรับคลาสทั้งหมดสามคลาส

public interface ISessionContext 
{
    ...
}

public class MySessionContext: ISessionContext 
{
    ...
}

public interface ILogManager 
{

}

public class MyLogManager: ILogManager 
{
    ...
}

public interface IService 
{
    ...
}

public class MyService: IService
{
    ...
}

วิธีแก้ปัญหาแรก:

public class AmbientContext
{
    private ISessionContext sessionContext;
    private ILogManager logManager;
    private IService service;

    public AmbientContext(ISessionContext sessionContext, ILogManager logManager, IService service)
    {
        this.sessionContext = sessionContext;
        this.logManager = logManager;
        this.service = service;
    }
}


public class MyCoreClass(AmbientContext ambientContext)
{
    ...
}

วิธีที่สอง (ไม่มีบริบท)

public MyCoreClass(ISessionContext sessionContext, ILogManager logManager, IService service)
{
    ...
}

ในกรณีนี้คือทางออกที่ดีที่สุด?


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

คำตอบ:


4

"ดีที่สุด" อยู่ที่นี่มากเกินไป เช่นเดียวกับการตัดสินใจทั่วไปมันเป็นการแลกเปลี่ยนระหว่างสองวิธีที่ถูกต้องในการบรรลุเป้าหมาย

ถ้าคุณสร้างAmbientContextและฉีดเข้าไปในชั้นเรียนหลายท่านอาจจะให้ข้อมูลเพิ่มเติมเพื่อแต่ละของพวกเขามากกว่าที่พวกเขาจำเป็นต้อง (เช่นระดับFooเพียงอาจจะใช้ISessionContextแต่จะถูกบอกเล่าเกี่ยวกับILogManagerและISessionมากเกินไป)

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

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


4

คำศัพท์จากคำถามไม่ตรงกับรหัสตัวอย่างจริง ๆ นี่Ambient Contextเป็นรูปแบบที่ใช้ในการคว้าการพึ่งพาจากคลาสใด ๆ ในโมดูลใด ๆ ให้ง่ายที่สุดโดยไม่สร้างมลพิษให้ทุกคลาสเพื่อยอมรับส่วนต่อประสานของผู้พึ่งพา แต่ยังคงรักษาแนวคิดของการควบคุมแบบผกผัน การพึ่งพาดังกล่าวมักจะอุทิศให้กับการบันทึกการรักษาความปลอดภัยการจัดการเซสชั่นการทำธุรกรรมแคชการตรวจสอบเพื่อข้ามความกังวลใด ๆ ในใบสมัคร มันน่ารำคาญอย่างใดที่จะเพิ่มILogging, ISecurity, ITimeProviderการก่อสร้างและใช้เวลาส่วนใหญ่ไม่ได้เรียนทุกคนต้องทั้งหมดในเวลาเดียวกันดังนั้นผมเข้าใจความต้องการของคุณ

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

IAmbientContextในคำถามไม่ได้แก้ปัญหาของการไม่ก่อให้เกิดมลพิษทุกสร้าง คุณยังต้องใช้มันในลายเซ็นคอนสตรัคเตอร์แน่นอนหนึ่งครั้งเท่านั้นในเวลานี้

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

MyCore ตอนนี้ดูเหมือนบางอย่าง

public class MyCoreClass
{
    public void BusinessFeature(string data)
    {
        LoggerContext.Current.Log(data);

        _repository.SaveProcessedData();

        SessionContext.Current.SetData(data);
        ...etc
    }
}

รูปแบบนี้และการใช้งานที่เป็นไปได้ได้รับการอธิบายในรายละเอียดโดยมาร์ค Seemann ในนี้บทความ อาจมีการใช้งานที่ต้องอาศัยคอนเทนเนอร์ IoC เองที่คุณใช้

คุณต้องการที่จะหลีกเลี่ยงการAmbientContext.Current.Logger, AmbientContext.Current.Sessionสำหรับเหตุผลเดียวกับที่กล่าวไว้ข้างต้น

แต่คุณมีตัวเลือกอื่น ๆ ในการแก้ปัญหานี้: ใช้เครื่องมือตกแต่ง, การสกัดกั้นแบบไดนามิกหากคอนเทนเนอร์ของคุณมีความสามารถนี้หรือ AOP บริบทแวดล้อมควรเป็นทางเลือกสุดท้ายเนื่องจากลูกค้าซ่อนการอ้างอิงผ่านมัน ฉันจะยังคงใช้ Ambient Context ถ้าส่วนต่อประสานนั้นเลียนแบบแรงกระตุ้นของฉันที่จะใช้การพึ่งพาแบบคงที่เช่นDateTime.NowหรือConfigurationManager.AppSettingsและความต้องการนี้เพิ่มขึ้นค่อนข้างบ่อย แต่ในที่สุดการฉีดคอนสตรัคเตอร์อาจไม่เป็นความคิดที่ดีที่จะได้การพึ่งพาที่แพร่หลายเหล่านี้


3

AmbientContextฉันต้องการหลีกเลี่ยง

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

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

ข้อที่สามการล้อเลียนการพึ่งพาAmbientContextนั้นยากขึ้นเพราะคุณต้องคิดออกในแต่ละกรณีว่าจะแกล้งสมาชิกทั้งหมดหรือแค่สมาชิกที่คุณต้องการจากนั้นตั้งค่าการเยาะเย้ยที่ส่งกลับ mocks เหล่านั้น (หรือทดสอบเป็นสองเท่า) หน่วยของคุณทดสอบเขียนอ่านและบำรุงรักษาได้ยากขึ้น

ประการที่สี่มันขาดการเชื่อมโยงและฝ่าฝืนหลักการความรับผิดชอบเดี่ยว นั่นเป็นสาเหตุที่มันมีชื่อเช่น "AmbientContext" เพราะมันทำสิ่งต่าง ๆ ที่ไม่เกี่ยวข้องกันมากมายและไม่มีทางที่จะตั้งชื่อตามสิ่งที่มันทำ

และอาจละเมิดหลักการแบ่งส่วนต่อประสานโดยการแนะนำสมาชิกส่วนต่อประสานในคลาสที่ไม่ต้องการใช้


2

ตัวที่สอง (ไม่มีตัวตัดส่วนต่อประสาน)

เว้นแต่จะมีการโต้ตอบระหว่างบริการต่างๆที่ต้องการการห่อหุ้มในระดับกลางมันจะทำให้โค้ดของคุณซับซ้อนและจำกัดความยืดหยุ่นเมื่อคุณแนะนำ 'อินเตอร์เฟสของอินเตอร์เฟส'

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