วิธีการที่มากไปเหมาะสมเมื่อใด


10

สมมติว่าฉันกำลังทำงานกับระบบที่มีอยู่และมีขนาดใหญ่พอสมควร ฉันมีวัตถุmyObjectของคลาสMyClass(เพื่อประโยชน์ของตัวอย่างสมมติว่าฉันทำงานใน Java) myObjectเป็นองค์ประกอบที่ประกอบด้วย a Collection, say, a Listและวัตถุอื่น ๆ ที่ (ฉันคิดว่า) ไม่เกี่ยวข้อง มันมีวิธีการมอบหมายซึ่งเพียงแค่ให้บริการที่จะเรียกร้องให้วิธีการของListมันประกอบด้วยเพื่อให้แน่ใจว่าListมันจะไม่ได้สัมผัส (ขออภัยถ้าฉันมีคำศัพท์ของฉันผิด)

Let 's พูดที่ว่านี้Listเป็นแต่ด้วยเหตุผลบางอย่างที่วิธีการเข้าถึงหลักเป็นวิธีหน้ากากสำหรับการเรียนList<String> SomeOtherClassถ้าผมอยากจะใส่คู่ค่าใหม่เข้าไปในของฉันListแล้วฉันมีวัตถุของที่เรียกว่าSomeOtherClass someObjectฉันต้องการโทรmyObject.insert(someObject)และภายในinsertวิธีการจะมีความมหัศจรรย์บางอย่างที่จะดึงข้อมูลที่จะใส่ลงไปในStringList<String>

สมมติว่าตอนนี้ฉันมีStringค่าและไม่มีSomeOtherClassวัตถุที่จะแทรก สมมติว่าฉันไม่สามารถปรับเปลี่ยนinsertวิธีการเพราะมันจะทำลายทุกอย่างในระบบนี้ จากนั้นฉันควรใช้insertวิธีมากเกินไปหรือไม่ หรือฉันควรสร้างวัตถุใหม่ของSomeOtherClassเวลาที่ฉันต้องการที่จะเรียกทุกคนinsert?

ฉันเดาว่าถ้าฉันทำมากไปมันจะมีลักษณะเช่นนี้ ...

public void insert(String s) {
    ...
}

public void insert(SomeOtherObject obj) {
    this.insert(obj.magicStringMethod());
}

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

นี่เป็นสถานที่ที่เหมาะสมในการโอเวอร์โหลดเมธอดหรือไม่? ถ้าไม่ฉันควรโอเวอร์โหลดเมธอดเมื่อใด


สมมติว่า Java, คุณเคยใช้ Generics เพื่อแก้ปัญหานี้หรือไม่? ฉันเดาว่าสิ่งที่ฉันถามจริง ๆ คือสายอักขระเป็นตัวแทนของอะไร หากจริง ๆ แล้วมันเป็นตัวแทนของวัตถุโดเมนจริง ๆ แล้วก็มีวิธีที่เป็นไปได้อีกวิธีหนึ่งในการแก้ปัญหานี้
Martijn Verburg

@MartijnVerburg ฉันยังไม่มาถึงตอนนี้ เนื่องจากฉันเป็นแค่ฝึกงานฉันยังไม่ค่อยคุ้นเคยกับสิ่งต่าง ๆ เช่นรูปแบบการออกแบบและฉันยังไม่มีนิสัยการฝึกการออกแบบที่ดี ในการตอบคำถามที่สองของคุณมันเป็นตัวแทนของวัตถุโดเมนจริง magicStringMethod()ในตัวอย่างของฉันในกรณีของฉันจะได้รับการStringแสดงSomeOtherObjectซึ่งเป็นวัตถุโดเมน
blahman

ฉันขอแนะนำให้คุณหลีกเลี่ยงการบรรทุกเกินพิกัดเมื่อคุณทำได้ มันทำให้เกิดความสับสนในบางครั้ง เป็นการดีที่สุดที่จะแน่ใจเกี่ยวกับวิธีการที่เรียกในขณะออกแบบ ดูที่นี่: programmers.stackexchange.com/questions/132369/…
NoChance

คำตอบ:


7

คุณโอเวอร์โหลดเมื่อคุณต้องการสนับสนุนประเภทต่าง ๆ :

public overload void MyMethod(int value)
{ 
}

public overload void MyMethod(bool value)
{
}

public overload void MyMethod(string value)
{
}

หรือเพื่อสนับสนุนส่วนต่อประสานแบบก้าวหน้าโดยใช้รายการพารามิเตอร์อื่น:

public overload void MyOtherMethod()
{
    this.MyOtherMethod(DefaultValue);
}

public overload void MyOtherMethod(int value)
{
    this.MyOtherMethod(value, DefaultOtherValue);
}

public overload void MyOtherMethod(int value, bool otherValue)
{
    ...
}

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


7

ฉันจะบอกว่าการบรรทุกเกินพิกัดมีความเหมาะสมเมื่อทั้งสองวิธีมีความหมายเทียบเท่ากัน หากต้องการขโมยจากตัวอย่างของ dukeofgaming:

สิ่งเหล่านี้มากเกินไปอย่างเหมาะสม:

public int sum(int a, int b){
    return a+b;
}

public double sum(double a, double b){
    return a+b;
}

สิ่งเหล่านี้ไม่ใช่:

public int sum(int a, int b){
    return a+b;
}

public double sum(double a, double b){
    return a-b;
}

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

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


1
ขอบคุณสำหรับคำตอบ. ฉันถามคนที่อาวุโสกว่า แต่เนื่องจากรหัสอยู่ในสถานะที่ค่อนข้างน่าสนใจการแก้ปัญหาของพวกเขาคือสิ่งที่ฉันไม่แน่ใจ แต่ยังมีการใช้งาน
blahman

5

เป็นหลักที่จะมีพารามิเตอร์ของวิธีการกำหนดวิธีการที่จะประพฤติ

ตัวอย่างรวดเร็วจะเป็น:

class Calc{
    //...
    public int sum(int a, int b){
        return a+b;
    }
    public double sum(double a, double b){
        return a+b;
    }
    //...
}

หากคุณผ่านเมธอดผลรวม (a, b) คู่ของจำนวนเต็มจะรู้ว่าควรเรียกใช้งานครั้งแรกเนื่องจากเมธอด call เกิดขึ้นพร้อมกับลายเซ็นเมธอด (เช่น double sum (double, double) จะไม่ทำงานหากคุณให้ ผลรวมวิธีสองจำนวนเต็ม)

ลายเซ็นของการโทรจะต้องตรงกับการใช้งานที่มีอยู่ดังนั้นการพยายามโทรหาผลรวม (สตริง, สตริง) จะไม่ทำงานจนกว่าคุณจะมีสิ่งนี้:

class Calc{
    //...
    public int sum(int a, int b){
        return a+b;
    }
    public double sum(double a, double b){
        return a+b;
    }
    public string sum(String a, String b){
        return "" + (Double.parseDouble(a) + Double.parseDouble(b));
    }
    //...
}

tl; dr: เพื่อให้คลาสจัดการวิธีการที่ถูกต้องตามพารามิเตอร์ใด ๆ ที่คุณให้วิธีการที่มีชื่อเดียวกัน

เมื่อรับช่วงจะเรียกว่าการเอาชนะ

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

ลองนึกภาพว่าคุณมีclass Robotและวิธีการที่เรียกว่าfireAtTarget(Target target)... ซึ่งจะโทรหาfireWeaponA(target); fireWeaponB(target); fireWeaponC(target);กัน นอกจากนี้คุณยังต้องการมีคอลเลกชันที่ชื่อว่า robot_army ซึ่งคุณสามารถเพิ่มวัตถุของคลาสหุ่นยนต์ได้เท่านั้น หุ่นยนต์ยิงปืนกลตามค่าเริ่มต้นในfireWeaponX()วิธีการ

แล้วคุณต้องการที่จะมีclass LazorRobotและclass MissileRobotคุณจะใช้หุ่นยนต์ทั้งหมดอีกครั้ง ?, ไม่เพียงมี LazorRobot และ MissileRobot สืบทอดหุ่นยนต์และเกินแต่ละfireWeaponX()วิธีการในการใช้ lazorz หรือขีปนาวุธและคุณจะมีพฤติกรรมเดียวกันด้วยอาวุธที่แตกต่างกันได้โดยไม่ต้อง reimplement ส่วนที่เหลือของวิธีการ

tl; dr: เพื่อให้พฤติกรรมของเมธอดนั้นขึ้นอยู่กับคลาสโดยไม่ทำลายอินเตอร์เฟส (robot_army ยอมรับเฉพาะ Robot แต่ยอมรับโดยการขยายคลาสใด ๆ ที่สืบทอด Robot)


ตัวอย่างแรกของคุณเป็นแทนที่ เมื่อคุณรักษาการอินเทอร์เฟซของวิธีการยังเปลี่ยนลักษณะการทำงานในการสืบทอด ความหมายคือคุณไม่ต้องการเสนอวิธีอื่น ตัวอย่างที่สองของคุณมีการโหลดมากเกินไปอย่างถูกต้อง หากความตั้งใจของคุณคือการเน้นว่าเมื่อใดที่จะแทนที่ vs เมื่อใดที่จะมีการใช้งานมากเกินไปคุณอาจต้องการทำให้คำตอบนี้ชัดเจนยิ่งขึ้นมิฉะนั้นwhen inheritingส่วนทั้งหมดอาจไม่จำเป็น :)
S.Robins

Derp คาเฟอีนทำให้ฉันในครั้งนี้ = P เหลือส่วนที่สองเป็นส่วนแรกและส่วนที่สองให้ความสนใจทั่วไป ขอบคุณสำหรับการแก้ไข
dukeofgaming

หากฉันเข้าใจคำตอบของคุณอย่างถูกต้องฉันควรโหลดมากเกินไปเฉพาะเมื่อดูที่พารามิเตอร์จะทำให้ฉันสามารถสรุปความแตกต่างของพฤติกรรมระหว่างพูดsum(String, String)และsum(int, int)?
blahman

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

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