ผ่านวัตถุสองครั้งเพื่อวิธีเดียวกันหรือรวมกับส่วนต่อประสานที่รวมกัน?


15

ฉันมีวิธีที่สร้างไฟล์ข้อมูลหลังจากคุยกับกระดานดิจิตอล:

CreateDataFile(IFileAccess boardFileAccess, IMeasurer boardMeasurer)

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

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

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


2
มันยากที่จะเป็นไปไม่ได้ที่จะแยกแยะรหัสของคุณออกจากชื่อที่คุณใช้ อินเทอร์เฟซชื่อ IDataFileCreator ที่ถูกส่งผ่านไปยังวิธีการที่ชื่อว่า CreateDataFile คือเหลือเชื่อ boggling พวกเขากำลังแข่งขันเพื่อความรับผิดชอบในการคงอยู่ของข้อมูลหรือไม่ CreateDataFile คลาสใดเป็นวิธีการต่อไปหรือไม่ การวัดไม่มีส่วนเกี่ยวข้องกับข้อมูลที่ยังคงมีอยู่ชัดเจนมาก คำถามของคุณไม่เกี่ยวกับปัญหาที่ใหญ่ที่สุดที่คุณมีกับรหัสของคุณ
Martin Maat

เป็นไปได้ไหมที่วัตถุเข้าถึงไฟล์ของคุณและวัตถุวัดของคุณอาจเป็นวัตถุสองชนิดที่แตกต่างกัน? ฉันจะบอกว่าใช่ หากคุณเปลี่ยนตอนนี้คุณจะต้องเปลี่ยนกลับเป็นเวอร์ชัน 2 ที่รองรับการวัดทั่วทั้งเครือข่าย
user253751

2
นี่คือคำถามอื่น - ทำไมการเข้าถึงไฟล์ข้อมูลและวัตถุการวัดเหมือนกันตั้งแต่แรก?
user253751

คำตอบ:


40

ไม่นี่เป็นเรื่องปกติอย่างสมบูรณ์แบบ มันเป็นเพียงการหมายความว่า API จะมากกว่าวิศวกรรมที่เกี่ยวกับการประยุกต์ใช้ในปัจจุบันของคุณ

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


7

เห็นด้วยกับคำตอบของ @ KilianFothว่าสิ่งนี้ดีมาก

ยังถ้าคุณต้องการคุณสามารถสร้างวิธีการที่วัตถุเดียวที่ใช้อินเตอร์เฟซทั้งสอง:

public object CreateDataFile<T_BoardInterface>(
             T_BoardInterface boardInterface
    )
    where T_BoardInterface : IFileAccess, IMeasurer
{
    return CreateDataFile(
                boardInterface
            ,   boardInterface
        );
}

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


4

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

ฉันคิดว่านี่เป็นปัญหาของคุณจริง ๆ แล้ว วิธีการไม่ได้ทำสิ่งหนึ่ง มันทำงานสองการทำงานที่แตกต่างกันซึ่งเกี่ยวข้องกับ I / O กับอุปกรณ์ต่าง ๆซึ่งทั้งสองอย่างนี้มันถูกโหลดไปที่วัตถุอื่น:

  • ดึงข้อมูลการวัด
  • บันทึกผลลัพธ์นั้นไปยังไฟล์ที่อื่น

นี่เป็นการดำเนินการ I / O ที่แตกต่างกันสองรายการ โดยเฉพาะอย่างยิ่งคนแรกไม่ได้กลายพันธุ์ระบบไฟล์ในทางใดทางหนึ่ง

อันที่จริงแล้วเราควรทราบว่ามีขั้นตอนกลางโดยนัย:

  • ดึงข้อมูลการวัด
  • จัดลำดับการวัดเป็นรูปแบบที่รู้จัก
  • บันทึกการวัดต่อเนื่องเป็นไฟล์

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

ตัวอย่างเช่นคุณอาจแยกการดำเนินการเช่นนี้

IMeasurer มีวิธีดึงข้อมูลการวัด:

public interface IMeasurer
{
    IMeasurement Measure(int someInput);
}

ประเภทการวัดของคุณก็อาจจะมีบางสิ่งบางอย่างที่เรียบง่ายเหมือนหรือstring decimalฉันไม่ได้ยืนยันว่าคุณต้องการอินเทอร์เฟซหรือคลาส แต่มันทำให้ตัวอย่างที่นี่มีความชัดเจนมากขึ้น

IFileAccess มีวิธีการในการบันทึกไฟล์:

interface IFileAccess
{
    void SaveFile(string fileContents);
}

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

interface IMeasurement
{
    // As part of the type
    string Serialize();
}

// Utility method. Makes more sense if the measurement is not a custom type.
public static string SerializeMeasurement(IMeasurement m)
{
    return ...
}

ยังไม่ชัดเจนว่าคุณมีการดำเนินการเป็นอนุกรมนี้หรือไม่

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

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

fileAccess.SaveFile(SerializeMeasurement(measurer.Measure()));

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


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

กรณีการใช้งานที่พบบ่อยที่สุดสำหรับผู้โทรของคุณคืออะไร

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

public class Board : IMeasurer, IFileAccess
{
    // Interface methods...

    /// <summary>
    /// Convenience method to measure and immediate record measurement in
    /// default location.
    /// </summary>
    public void ReadAndSaveMeasurement()
    {
        this.SaveFile(SerializeMeasurement(this.Measure()));
    }
}

หากวิธีนี้ไม่ช่วยเพิ่มความสะดวกสบายฉันก็ไม่ต้องกังวลกับวิธีการทั้งหมด


นี่เป็นวิธีการอำนวยความสะดวกที่นำมาซึ่งคำถามอีกข้อหนึ่ง

IFileAccessอินเตอร์เฟสควรรู้เกี่ยวกับประเภทการวัดและวิธีการทำให้เป็นอนุกรมหรือไม่ ถ้าเป็นเช่นนั้นคุณสามารถเพิ่มวิธีการไปที่IFileAccess:

interface IFileAccess
{
    void SaveFile(string fileContents);
    void SaveMeasurement(IMeasurement m);
}

ตอนนี้ผู้โทรทำสิ่งนี้:

fileAccess.SaveFile(measurer.Measure());

ซึ่งสั้นและอาจชัดเจนกว่าวิธีอำนวยความสะดวกของคุณตามที่คิดไว้ในคำถาม


2

ลูกค้าที่บริโภคไม่ควรต้องจัดการกับคู่ของรายการเมื่อรายการเดียวพอเพียง CreateDataFileในกรณีของคุณพวกเขาเกือบจะทำไม่ได้จนกว่าการอุทธรณ์ของ

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

อย่างไรก็ตามวิธีการอื่นที่มีข้อ จำกัด น้อยกว่าใน / กำหนดให้มีการใช้งานนั้นIFileAccessคือการจับคู่กับIMeasurerองค์ประกอบในดังนั้นหนึ่งในนั้นถูกผูกไว้กับและอ้างอิงอื่น ๆ (นี่เป็นการเพิ่มสิ่งที่เป็นนามธรรมของหนึ่งในพวกเขาเนื่องจากตอนนี้แสดงถึงการจับคู่เช่นกัน) จากนั้นCreateDataFileอาจใช้การอ้างอิงเดียวพูดIFileAccessและยังคงได้สิ่งอื่นตามที่ต้องการ การดำเนินงานปัจจุบันของคุณเป็นวัตถุที่ใช้การเชื่อมต่อทั้งจะเพียงreturn this;สำหรับการอ้างอิงองค์ประกอบที่นี่ทะเยอทะยานสำหรับในIMeasurerIFileAccess

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


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


0

บางคนนำมาซึ่งCreateDataFileการทำมากเกินไป ฉันอาจแนะนำว่าแทนที่จะBoardทำมากเกินไปเนื่องจากการเข้าถึงไฟล์ดูเหมือนเป็นข้อกังวลแยกจากบอร์ดส่วนที่เหลือ

อย่างไรก็ตามหากเราสมมติว่านี่ไม่ใช่ข้อผิดพลาดปัญหาที่ใหญ่กว่าก็คือว่าควรกำหนดส่วนต่อประสานกับลูกค้าในกรณีCreateDataFileนี้

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

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


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