ผู้เขียนหมายถึงอะไรโดยการส่งอินเทอร์เฟซอ้างอิงไปยังการใช้งานใด ๆ


17

ผมอยู่ในขั้นตอนของการพยายามที่จะโท C # ดังนั้นฉันอ่านรหัสผ่าน Adaptive C #โดยแกรี่แมคลีนฮอลล์

เขาเขียนเกี่ยวกับรูปแบบและรูปแบบการต่อต้าน ในการใช้งานกับส่วนต่อประสานเขาเขียนต่อไปนี้:

นักพัฒนาที่ยังใหม่กับแนวคิดของการเขียนโปรแกรมไปยังอินเทอร์เฟซมักมีปัญหาในการปล่อยสิ่งที่อยู่เบื้องหลังอินเทอร์เฟซ

ณ เวลารวบรวมไคลเอนต์ของอินเทอร์เฟซใด ๆ ควรมีความคิดที่ไม่มีการใช้งานของอินเทอร์เฟซที่ใช้ ความรู้ดังกล่าวสามารถนำไปสู่ข้อสันนิษฐานที่ไม่ถูกต้องซึ่งทำให้คู่ลูกค้าใช้งานอินเทอร์เฟซเฉพาะ

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

อาจเป็นกำแพงภาษาหรือการขาดประสบการณ์ของฉัน แต่ฉันไม่เข้าใจความหมาย นี่คือสิ่งที่ฉันเข้าใจ:

ฉันมีโครงการสนุก ๆ เพื่อฝึก C # ที่นั่นฉันมีชั้นเรียน:

public class SomeClass...

คลาสนี้ถูกใช้ในหลายสถานที่ ในขณะที่เรียนรู้ C # ฉันอ่านว่ามันเป็นนามธรรมที่ดีกว่าด้วยส่วนต่อประสานดังนั้นฉันจึงทำสิ่งต่อไปนี้

public interface ISomeClass <- Here I made a "contract" of all the public methods and properties SomeClass needs to have.

public class SomeClass : ISomeClass <- Same as before. All implementation here.

ดังนั้นฉันจึงไปอ้างอิงคลาสทั้งหมดและแทนที่ด้วย ISomeClass

ยกเว้นในการก่อสร้างที่ฉันเขียน:

ISomeClass myClass = new SomeClass();

ฉันเข้าใจถูกต้องไหมว่านี่ผิด ถ้าใช่ทำไมเป็นเช่นนั้นและฉันควรทำอย่างไร


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

1
คุณหมายถึงอะไร "ในคอนสตรัคเตอร์ที่ฉันเขียนISomeClass myClass = new SomeClass();ถ้าคุณหมายถึงจริงๆนั่นคือการเรียกซ้ำในคอนสตรัคเตอร์ซึ่งอาจไม่ใช่สิ่งที่คุณต้องการหวังว่าคุณหมายถึงใน" การก่อสร้าง "นั่นคือการจัดสรรบางทีอาจจะไม่ใช่ ?
Erik Eidt

@Erik: ใช่ ในการก่อสร้าง คุณถูก. จะแก้ไขคำถาม ขอบคุณ
มาร์แชลล์

ความจริงแล้วสนุก: F # มีเรื่องราวที่ดีกว่า C # ในเรื่องนั้น - มันไม่ได้เกิดขึ้นกับการใช้งานอินเตอร์เฟสโดยปริยายดังนั้นเมื่อใดก็ตามที่คุณต้องการเรียกใช้วิธีการเชื่อมต่อ สิ่งนี้ทำให้มันชัดเจนมากเวลาและวิธีที่คุณใช้อินเทอร์เฟซในรหัสของคุณและทำให้การเขียนโปรแกรมไปยังอินเทอร์เฟซฝังแน่นมากขึ้นในภาษา
scrwtp

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

คำตอบ:


37

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

ดังนั้นบางทีSomeClassและISomeClassเป็นตัวอย่างที่ไม่ดีเพราะมันจะเหมือนมีOracleObjectSerializerชั้นเรียนและIOracleObjectSerializerอินเตอร์เฟซ

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

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

มีสองวิธีที่จะไปเกี่ยวกับเรื่องนี้คือวิธีที่ไม่ถูกต้องและLiskov เปลี่ยนตัวหลักการวิธีการ

วิธีที่ไม่ถูกต้อง

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

หลักการทดแทน Liskov

Liskov กล่าวว่าคุณไม่ควรต้องการวิธีการต่าง ๆ เช่นsetPropertyในคลาสการนำไปปฏิบัติเช่นเดียวกับในกรณีของฉันOracleObjectSerializerหากทำอย่างถูกต้อง หากคุณระดับนามธรรมOracleObjectSerializerเพื่อIObjectSerializerคุณควรครอบคลุมวิธีการทั้งหมดที่จำเป็นในการใช้คลาสนั้นและถ้าคุณไม่สามารถแล้วสิ่งที่ผิดกับสิ่งที่เป็นนามธรรมของคุณ (พยายามที่จะทำให้Dogการทำงานระดับเป็นIPersonการดำเนินงานเป็นต้น)

แนวทางที่ถูกต้องที่จะให้วิธีการในการsetProperty IObjectSerializerวิธีการที่คล้ายกันในSQLServerObjectSerializerความนึกคิดจะทำงานผ่านsetPropertyวิธีนี้ ยังดีกว่าคุณสร้างมาตรฐานชื่อคุณสมบัติผ่านEnumที่แต่ละการใช้งานแปลว่า enum เทียบเท่ากับคำศัพท์ฐานข้อมูลของตัวเอง

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


1
ดูเหมือนว่าผมว่าถ้าคุณกำลังจะโยนIObjectSerializerไปOracleObjectSerializerเพราะคุณ“รู้” ว่านี่คือสิ่งที่มันเป็นแล้วคุณควรจะซื่อสัตย์กับตัวเอง (และที่สำคัญมากขึ้นกับคนอื่น ๆ ที่อาจรักษารหัสนี้ซึ่งอาจรวมถึงในอนาคตของคุณ ตนเอง) และใช้OracleObjectSerializerตลอดทางจนถึงจุดที่มันถูกสร้างขึ้นมาจนถึงที่ ๆ มันถูกใช้ สิ่งนี้ทำให้เป็นที่เปิดเผยต่อสาธารณะและชัดเจนว่าคุณกำลังแนะนำการพึ่งพาการใช้งานเฉพาะ - และงานและความน่าเกลียดที่เกี่ยวข้องกับการทำเช่นนั้นจะกลายเป็นคำแนะนำที่แข็งแกร่งว่ามีบางอย่างผิดปกติ
KRyan

(และถ้าด้วยเหตุผลบางอย่างคุณจริงๆไม่ต้องพึ่งพาการดำเนินงานโดยเฉพาะอย่างยิ่งมันจะกลายเป็นชัดเจนมากว่านี่คือสิ่งที่คุณกำลังทำและที่คุณกำลังทำมันด้วยความตั้งใจและวัตถุประสงค์. นี้“ควรจะ” ไม่เกิดขึ้นแน่นอน และ 99% ของเวลาที่ดูเหมือนว่ามันเกิดขึ้นจริง ๆ แล้วไม่ใช่และคุณควรแก้ไขสิ่งต่าง ๆ แต่ไม่มีอะไรแน่นอน 100% หรือสิ่งที่ควรจะเป็น)
KRyan

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

29

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

ISomeClass myClass = new SomeClass();

ในวงกว้างการพูดนี้ไม่น่ากลัว สิ่งที่ควรหลีกเลี่ยงเมื่อเป็นไปได้คือทำสิ่งนี้:

void someMethod(ISomeClass interface){
    SomeClass cast = (SomeClass)interface;
}

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

ทีนี้มองย้อนกลับไปที่การเรียกคอนสตรัคเตอร์ของคุณ

ISomeClass myClass = new SomeClass();

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


1
Apache Math ทำสิ่งนี้กับ Cartesian3D Source บรรทัด 286ซึ่งน่ารำคาญจริงๆ
J_F_B_M

1
นี่คือคำตอบที่ถูกต้องจริงที่ตอบคำถามเดิม
Benjamin Gruenbaum

2

ฉันคิดว่ามันง่ายกว่าที่จะแสดงรหัสของคุณด้วยตัวอย่างที่ไม่ดี:

public interface ISomeClass
{
    void DoThing();
}

public class SomeClass : ISomeClass
{
    public void DoThing()
    {
       // Mine for BitCoin
    }

}

public class AnotherClass : ISomeClass
{
    public void DoThing()
    {
        // Mine for oil
    }
    public Decimal Depth;
 }

 void main()
 {
     ISomeClass task = new SomeClass();

     task.DoThing(); //  This is good

     Console.WriteLine("Depth = {0}", ((AnotherClass)task).Depth); <-- The task object will not have this field
 }

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


ทำไมสวัสดีครับ ใครเคยบอกคุณว่าคุณหล่อแค่ไหน?
Neil

2

เพื่อความชัดเจนลองกำหนดการคัดเลือกนักแสดง

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

นี่คือตัวอย่างของการหล่อจากไมโครซอฟท์เอกสารหน้านี้

// Create a new derived type.  
Giraffe g = new Giraffe();  

// Implicit conversion to base type is safe.  
Animal a = g;  

// Explicit conversion is required to cast back  
// to derived type. Note: This will compile but will  
// throw an exception at run time if the right-side  
// object is not in fact a Giraffe.  
Giraffe g2 = (Giraffe) a;  

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


1
"แคสติ้งกำลังแปลงบางอย่างจากประเภทหนึ่งเป็นประเภทอื่น" - ไม่การคัดเลือกนักแสดงกำลังแปลงบางอย่างจากประเภทหนึ่งไปอีกประเภทหนึ่งอย่างชัดเจน (โดยเฉพาะ "cast" เป็นชื่อสำหรับไวยากรณ์ที่ใช้ในการระบุการแปลง) การแปลงโดยนัยไม่ได้ปลดเปลื้อง "อาจมีการระบุการแปลงเฉพาะเมื่อทำการส่งสัญญาณ แต่ค่าเริ่มต้นคือการตีความบิตใหม่" - ไม่แน่นอน มีการแปลงเป็นจำนวนมากทั้งโดยนัยและชัดเจนซึ่งเกี่ยวข้องกับการเปลี่ยนแปลงที่สำคัญในรูปแบบบิต
hvd

@hvd ฉันได้ทำการแก้ไขตอนนี้เกี่ยวกับความชัดเจนในการคัดเลือกนักแสดง เมื่อฉันพูดว่าค่าเริ่มต้นคือการตีความบิตใหม่อีกครั้งฉันพยายามแสดงว่าถ้าคุณสร้างประเภทของคุณเองจากนั้นในกรณีที่การโยนถูกกำหนดโดยอัตโนมัติเมื่อคุณส่งไปยังอีกประเภทหนึ่งบิตจะตีความใหม่ . ในAnimal/ Giraffeตัวอย่างข้างต้นหากคุณต้องทำAnimal a = (Animal)g;บิตจะตีความใหม่ (ข้อมูลยีราฟใด ๆ ที่เฉพาะเจาะจงจะถูกตีความว่าเป็น "ไม่ได้เป็นส่วนหนึ่งของวัตถุนี้")
Ryan1729

แม้จะมีสิ่งที่ hvd กล่าวว่าคนมักใช้คำว่า "cast" ในการอ้างอิงถึงการแปลงโดยนัย; เห็นเช่นhttps://www.google.com/search?q="implicit+cast"&tbm=bks ในทางเทคนิคแล้วฉันคิดว่ามันถูกต้องมากกว่าที่จะจองคำว่า "cast" สำหรับการแปลงที่ชัดเจนตราบใดที่คุณไม่สับสนเมื่อคนอื่นใช้มันแตกต่างกัน
ruakh

0

5 เซนต์ของฉัน:

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

ฉันไม่รู้ C # ดังนั้นฉันจะให้ตัวอย่างนามธรรม (ผสมระหว่าง Java และ C ++) หวังว่าไม่เป็นไร

สมมติว่าคุณมีส่วนต่อประสานiList:

interface iList<Key,Value>{
   bool add(Key k, Value v);
   bool remove(Element e);
   Value get(Key k);
}

ตอนนี้สมมติว่ามีการใช้งานมากมาย:

  • DynamicArrayList - ใช้แบนอาร์เรย์รวดเร็วในการแทรกและลบในตอนท้าย
  • LinkedList - ใช้ลิสต์ที่ลิงก์สองอันรวดเร็วเพื่อแทรกไว้ด้านหน้าและส่วนท้าย
  • AVLTreeList - ใช้ AVL Tree รวดเร็วในการทำทุกอย่าง แต่ใช้หน่วยความจำจำนวนมาก
  • SkipList - ใช้ SkipList รวดเร็วในการทำทุกอย่างช้ากว่าต้นไม้ AVL แต่ใช้หน่วยความจำน้อยกว่า
  • HashList - ใช้ HashTable

สามารถนึกถึงการใช้งานที่แตกต่างกันมากมาย

ตอนนี้สมมติว่าเรามีรหัสต่อไปนี้:

uint begin_size = 1000;
iList list = new DynamicArrayList(begin_size);

iListมันแสดงให้เห็นชัดเจนความตั้งใจของเราที่เราต้องการใช้ แน่นอนว่าเราไม่สามารถที่จะดำเนินDynamicArrayListการดำเนินงานเฉพาะ iListแต่เราจำเป็นต้องมี

พิจารณารหัสต่อไปนี้:

iList list = factory.getList();

ตอนนี้เราไม่รู้ด้วยซ้ำว่าการใช้งานคืออะไร ตัวอย่างสุดท้ายนี้มักใช้ในการประมวลผลภาพเมื่อคุณโหลดไฟล์บางไฟล์จากดิสก์และคุณไม่จำเป็นต้องใช้ไฟล์ประเภทนี้ (gif, jpeg, png, bmp ... ) แต่สิ่งที่คุณต้องการคือการจัดการภาพ (พลิก สเกลบันทึกเป็น png ที่ส่วนท้าย)


0

คุณมีอินเทอร์เฟซ ISomeClass และวัตถุ myObject ที่คุณไม่รู้อะไรจากรหัสของคุณยกเว้นว่ามันถูกประกาศให้ใช้ ISomeClass

คุณมีคลาส SomeClass ซึ่งคุณรู้ว่าจะใช้อินเทอร์เฟซ ISomeClass คุณรู้ว่าเพราะมันถูกประกาศให้ใช้ ISomeClass หรือคุณใช้มันด้วยตัวเองเพื่อใช้ ISomeClass

เกิดอะไรขึ้นกับการส่ง myClass ไปยัง SomeClass มีสองสิ่งที่ผิด ข้อแรกคุณไม่รู้จริง ๆ ว่า myClass เป็นสิ่งที่สามารถแปลงเป็น SomeClass (ตัวอย่างของ SomeClass หรือคลาสย่อยของ SomeClass) ดังนั้นผู้ส่งอาจผิดพลาด สองคุณไม่ควรทำเช่นนี้ คุณควรทำงานกับ myClass ที่ประกาศเป็น iSomeClass และใช้เมธอด ISomeClass

จุดที่คุณได้รับวัตถุ SomeClass คือเมื่อมีการเรียกวิธีการอินเทอร์เฟซ ในบางจุดที่คุณเรียก myClass.myMethod () ซึ่งประกาศในอินเตอร์เฟส แต่มีการนำไปใช้ใน SomeClass และแน่นอนอาจเป็นไปได้ในคลาสอื่น ๆ ที่ใช้ ISomeClass หากการโทรสิ้นสุดลงในรหัส SomeClass.myMethod ของคุณคุณจะรู้ว่าตนเองเป็นตัวอย่างของ SomeClass และ ณ จุดนั้นการปรับจะใช้งานได้จริงและถูกต้องจริงเพื่อใช้เป็นวัตถุ SomeClass แน่นอนว่าถ้ามันเป็นตัวอย่างของ OtherClass ไม่ใช่ SomeClass คุณจะไม่ได้รับรหัส SomeClass

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