ความแตกต่างกับการเอาชนะการบรรทุกเกินพิกัด


347

ในแง่ของ Java เมื่อมีคนถามว่า:

ความแตกต่างคืออะไร?

จะบรรทุกเกินพิกัดหรือเอาชนะจะเป็นคำตอบที่ยอมรับได้หรือไม่?

ฉันคิดว่ามันมีอะไรมากกว่านั้น

หากคุณมีคลาสฐานนามธรรมที่กำหนดวิธีการที่ไม่มีการใช้งานและคุณกำหนดวิธีการนั้นในคลาสย่อยนั้นจะยังคงทับซ้อนกัน?

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


คำตอบด้านล่างอธิบายเกี่ยวกับ polymorphism ได้ดีมาก แต่ฉันมีข้อคัดค้านอย่างมากที่จะบอกว่าการบรรทุกเกินพิกัดเป็นรูปแบบหนึ่งของความหลากหลายซึ่งฉันได้ลองปรับในคำถามและคำตอบของฉันที่จริงแล้วมุ่งเน้นไปที่การบรรทุกเกินพิกัดคือความหลากหลายหรือไม่ ฉันพยายามที่จะพิสูจน์ @The Digital Gabeg คำตอบปัจจุบันในหัวข้อนี้ ดูรายละเอียด
PraveenKumar Lalasangi

คำตอบ:


894

วิธีที่ชัดเจนที่สุดในการแสดงความแตกต่างคือผ่านชั้นฐานนามธรรม (หรืออินเตอร์เฟซ)

public abstract class Human{
   ...
   public abstract void goPee();
}

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

ดังนั้นเราเลื่อนการใช้งานโดยใช้คลาสนามธรรม

public class Male extends Human{
...
    @Override
    public void goPee(){
        System.out.println("Stand Up");
    }
}

และ

public class Female extends Human{
...
    @Override
    public void goPee(){
        System.out.println("Sit Down");
    }
}

ตอนนี้เราสามารถบอกได้ทั้งห้องที่เต็มไปด้วยมนุษย์เพื่อไปฉี่

public static void main(String[] args){
    ArrayList<Human> group = new ArrayList<Human>();
    group.add(new Male());
    group.add(new Female());
    // ... add more...

    // tell the class to take a pee break
    for (Human person : group) person.goPee();
}

การทำเช่นนี้จะทำให้:

Stand Up
Sit Down
...

37
@yuudachi ฉันมากับตัวอย่างนี้เมื่อสอนชั้นเรียน คลาส "บัญชีธนาคาร" ที่ยอมรับได้ไม่ได้แสดงถึง "นามธรรม" ของคลาสพื้นฐาน ตัวอย่างบัญญัติอื่น ๆ (สัตว์ทำเสียง) นั้นเป็นนามธรรมเกินกว่าจะเข้าใจได้ ฉันกำลังมองหาฐานเดียวที่มีคลาสย่อยที่ชัดเจนเกินไป ที่จริงแล้ว goPee () เป็นเพียงตัวอย่างเดียวที่ฉันคิดว่าไม่ใช่เรื่องเพศหรือโปรเฟสเซอร์ (ถึงแม้ในชั้นเรียนฉันจะพิมพ์ "ลงห้องโถงทางด้านซ้าย" แทนที่จะยืนหรือนั่งลง)
Chris Cudmore

100
ตัวอย่างนี้ยังเน้นถึงความยากลำบากในการใช้ระบบลำดับชั้นเพื่ออธิบายระบบชีวภาพ มนุษย์บางคนเช่นเด็กเล็กฉี่เกือบทุกตำแหน่งและทารกไม่สามารถบอกได้ง่ายๆว่า goPee () มนุษย์บางคนกำลัง intersex ที่ฉลากทางชีวภาพของ "ชาย" หรือ "หญิง" กลายเป็นค่อนข้างชัดเจน - กำหนด; ความหมายทางสังคมนั้นซับซ้อนยิ่งขึ้น เป็นตัวอย่างการสอนมันแสดงให้เห็นว่าสมมติฐานการสร้างแบบจำลองสามารถมีผลลัพธ์เชิงลบเช่นความหมายที่บางคน (เช่นนักเรียนของการเขียนโปรแกรม OO) ที่ไม่หยุดยั้งหรือ intersex ไม่ใช่มนุษย์จริง
Andrew Dalke

7
อย่างน้อยฉันก็นึกถึงมนุษย์ไม่กี่คนที่จะหักล้างคุณ "คุณไม่สามารถสร้างมนุษย์ที่ไม่ใช่วิทยานิพนธ์ทั้งชายและหญิงแม้ว่ามันจะยังคงเป็นจริงในรหัสของคุณ ... สิ่งที่เป็นนามธรรมฉันคิดว่าฉันพูด ? ;)
Frank W. Zammetti

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

20
@AndrewDalke +1 สำหรับบันทึกความซับซ้อนทางชีวภาพ นอกจากนี้goPeeไม่ได้ใช้สนามความโน้มถ่วงเป็นอินพุต การพึ่งพาสถานะโลกนี้ทำให้การทดสอบหน่วยCatheterizedIntersexAstronautทำได้ยากและแสดงว่าคลาสย่อยอาจไม่ใช่วิธีที่ดีที่สุดในการจัดองค์ประกอบของลักษณะ
Mike Samuel

99

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

การแทนที่เป็นฟังก์ชันประเภทหนึ่งที่เกิดขึ้นในคลาสที่สืบทอดมาจากคลาสอื่น ฟังก์ชั่นการแทนที่ "แทนที่" ฟังก์ชั่นที่สืบทอดมาจากคลาสฐาน แต่ทำในลักษณะที่มันถูกเรียกแม้เมื่ออินสแตนซ์ของคลาสนั้นแสร้งทำเป็นประเภทที่แตกต่างกันผ่าน polymorphism อ้างถึงตัวอย่างก่อนหน้านี้คุณสามารถกำหนดคลาสของคุณเองและแทนที่ฟังก์ชั่น toString () เนื่องจากฟังก์ชั่นนี้ได้รับการสืบทอดจาก Object จึงจะยังคงใช้งานได้หากคุณคัดลอกอินสแตนซ์ของคลาสนี้ลงในตัวแปร Object-type โดยปกติถ้าคุณเรียก toString () ในชั้นเรียนของคุณในขณะที่มันแสร้งทำเป็น Object รุ่น toString ซึ่งจะยิงจริง ๆ นั้นเป็นวัตถุที่กำหนดไว้ใน Object เอง อย่างไรก็ตามเนื่องจากฟังก์ชั่นนี้มีการแทนที่คำจำกัดความของ toString () จากคลาสของคุณจะถูกใช้แม้เมื่ออินสแตนซ์ของคลาส '

การโอเวอร์โหลดคือการกระทำของการกำหนดหลายวิธีด้วยชื่อเดียวกัน แต่มีพารามิเตอร์ต่างกัน มันไม่เกี่ยวข้องกับการเอาชนะหรือ polymorphism


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

5
ที่จริงแล้วคุณไม่จำเป็นต้องเรียนเพื่อความแตกต่างเลย
StCredZero

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

9
ไม่ถูกต้อง Ad hoc polymorphismคือสิ่งที่คุณอธิบายไว้ในส่วนการรับภาระมากเกินไปและเป็นกรณีของความหลากหลาย
Jossie Calderon

1
"มันไม่เกี่ยวข้องกับการเอาชนะหรือ polymorphism" คำสั่งนี้ผิด
Shailesh Pratapwar

45

ความแตกต่างหมายถึงมากกว่าหนึ่งรูปแบบวัตถุเดียวกันการดำเนินการที่แตกต่างกันตามความต้องการ

ความแตกต่างสามารถทำได้โดยใช้สองวิธีคือ

  1. วิธีการเอาชนะ
  2. วิธีการมากไป

เมธอดโอเวอร์โหลดหมายถึงการเขียนสองวิธีขึ้นไปในคลาสเดียวกันโดยใช้ชื่อเมธอดเดียวกัน แต่พารามิเตอร์การส่งจะแตกต่างกัน

การแทนที่เมธอดหมายถึงเราใช้ชื่อเมธอดในคลาสที่ต่างกันซึ่งหมายความว่าเมธอดคลาสหลักถูกใช้ในคลาสย่อย

ใน Java เพื่อให้เกิดความหลากหลายรูปแบบตัวแปรอ้างอิงระดับสูงสามารถถือวัตถุชั้นย่อย

เพื่อให้บรรลุความแตกต่างผู้พัฒนาทุกคนจะต้องใช้ชื่อวิธีการเดียวกันในโครงการ


4
+1 สำหรับคำตอบที่ดี คำตอบที่ได้รับการยอมรับจะอธิบายความหลากหลายหลายรูปแบบเท่านั้น คำตอบนี้เสร็จสมบูรณ์
apadana

1
ความหลากหลายเป็นกระบวนทัศน์ (OOP) แต่การเอาชนะและการบรรทุกเกินพิกัดเป็นสิ่งอำนวยความสะดวกด้านภาษา
曾其威

ความแตกต่างยังสามารถทำได้โดยประเภททั่วไป
Minh Nghĩa

43

นี่คือตัวอย่างของ polymorphism ใน pseudo-C # / Java:

class Animal
{
    abstract string MakeNoise ();
}

class Cat : Animal {
    string MakeNoise () {
        return "Meow";
    }
}

class Dog : Animal {
    string MakeNoise () {
        return "Bark";
    }
}

Main () {
   Animal animal = Zoo.GetAnimal ();
   Console.WriteLine (animal.MakeNoise ());
}

ฟังก์ชั่นหลักไม่ทราบประเภทของสัตว์และขึ้นอยู่กับพฤติกรรมการใช้งานเฉพาะของวิธีการ MakeNoise ()

แก้ไข: ดูเหมือนว่า Brian ทุบตีฉันเพื่อชก ตลกเราใช้ตัวอย่างเดียวกัน แต่โค้ดด้านบนจะช่วยชี้แจงแนวคิด


มันเป็นตัวอย่างของ polymorphism แบบรันไทม์ ความแตกต่างของเวลาในการคอมไพล์ยังเป็นไปได้ด้วยวิธีการโอเวอร์โหลดและชนิดทั่วไป
Pete Kirkham

รูปร่าง -> สี่เหลี่ยมด้านขนาน -> สี่เหลี่ยมผืนผ้า -> สี่เหลี่ยมจัตุรัส
mpen

@ yankee2905 ในกรณีนี้ฉันคิดว่าคุณสามารถใช้อินเตอร์เฟสได้เนื่องจากคลาสสามารถใช้หลายอินเตอร์เฟสได้
Sam003

1
@Zhisheng หรือเพิ่มวิธีการฉี่ในระดับผู้ปกครองที่เป็นนามธรรม? ฉันจะใช้ส่วนต่อประสานเพื่อใช้งานอย่างอื่น
joey rohan

43

ทั้งการเอาชนะและการบรรทุกเกินพิกัดใช้เพื่อให้เกิดความหลากหลาย

คุณสามารถมีวิธีการในชั้นเรียนที่ถูกแทนที่ในหนึ่งคลาสย่อยหรือมากกว่า วิธีการทำสิ่งต่าง ๆ ขึ้นอยู่กับคลาสที่ใช้ในการสร้างอินสแตนซ์ของวัตถุ

    abstract class Beverage {
       boolean isAcceptableTemperature();
    }

    class Coffee extends Beverage {
       boolean isAcceptableTemperature() { 
           return temperature > 70;
       }
    }

    class Wine extends Beverage {
       boolean isAcceptableTemperature() { 
           return temperature < 10;
       }
    }

คุณอาจมีวิธีที่ มากไปด้วยชุดของอาร์กิวเมนต์สองชุดขึ้นไป วิธีการทำสิ่งต่าง ๆ ตามประเภทของการโต้แย้งผ่าน

    class Server {
        public void pour (Coffee liquid) {
            new Cup().fillToTopWith(liquid);
        }

        public void pour (Wine liquid) {
            new WineGlass().fillHalfwayWith(liquid);
        }

        public void pour (Lemonade liquid, boolean ice) {
            Glass glass = new Glass();
            if (ice) {
                glass.fillToTopWith(new Ice());
            }
            glass.fillToTopWith(liquid);
        }
    }

ฉันคิดว่ามันถูกโหวตลงเพราะวิธีการบรรทุกเกินพิกัดในอดีตไม่ถือว่าเป็นส่วนหนึ่งของความหลากหลายในกระบวนทัศน์เชิงวัตถุ วิธีการบรรทุกเกินพิกัดและความแตกต่างเป็นสอง ortogonal คุณสมบัติอิสระของภาษาการเขียนโปรแกรม
Sergio Acosta

7
ตามที่ระบุไว้ในคำตอบของฉันที่นี่ฉันไม่เห็นด้วย - คุณสมบัติทั้งสองนี้ไม่ได้เป็นฉาก แต่มีความเกี่ยวข้องอย่างใกล้ชิด ความแตกต่าง! = มรดก คุณมีคะแนนของฉัน
Peter Meyer

2
กล่าวอีกนัยหนึ่งพิมพ์ polymorphism กับ ad-hoc polymorphism ฉันกำลัง upvoting คำตอบนี้แม้ว่าจะไม่สมบูรณ์เท่าที่ควรเพราะมันถูกต้องระบุว่าทั้งการบรรทุกเกินพิกัดและการเอาชนะมีความเกี่ยวข้องกับความหลากหลาย การบอกว่า polymorphism ในภาษา OOP สามารถทำได้โดยการสืบทอดคลาสนั้นผิด - เราควรจำไว้ว่ามีภาษา OOP อื่น ๆ นอกเหนือจาก Java และ C ++ ที่หนึ่งสามารถใช้แนวความคิดเช่นการเยื้อง, ad hoc polymorphism, parametric polymorphism เป็นต้น .
rsenna

2
@rsenna สิ่งนี้อาจไม่สมบูรณ์ แต่ตอบคำถามได้ดีกว่า IMHO ที่เหลือ นอกจากนี้ดีมากที่คุณพูดถึง ad-hoc และตัวแปรหลากหลาย
Valentin Radu

15

คุณถูกต้องว่าการบรรทุกเกินพิกัดไม่ใช่คำตอบ

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


3
มันไม่ควรจะเป็นพฤติกรรมของวัตถุที่เปลี่ยนแปลง แต่การใช้งานของเขา พฤติกรรมเดียวกันการใช้งานที่แตกต่างนั่นคือความแตกต่าง
QBziZ

@QBziZ คุณจำเป็นต้องกำหนดลักษณะการทำงานโดยเฉพาะอย่างยิ่งคำคุณศัพท์เดียวกัน หากพฤติกรรมเหมือนกันเหตุใดการปรับใช้ของพวกเขาจึงแตกต่างกัน ไม่ใช่ว่าบางคนไม่มีความสุขกับการใช้งานบางอย่างดังนั้นจึงต้องใช้วิธีอื่น
Sнаđошƒаӽ

11

การบอกว่าการบรรทุกเกินพิกัดหรือการเอาชนะไม่ได้ให้ภาพเต็ม ความแตกต่างเป็นเพียงความสามารถของวัตถุที่เชี่ยวชาญพฤติกรรมของมันขึ้นอยู่กับประเภทของมัน

ฉันจะไม่เห็นด้วยกับคำตอบบางส่วนที่นี่ในการบรรทุกเกินพิกัดเป็นรูปแบบของความหลากหลาย (parametric polymorphism) ในกรณีที่วิธีการที่มีชื่อเดียวกันสามารถทำงานต่างประเภทพารามิเตอร์ที่แตกต่างกัน ตัวอย่างที่ดีคือการใช้งานมากเกินไป คุณสามารถกำหนด "+" เพื่อยอมรับพารามิเตอร์ประเภทต่าง ๆ - พูดสตริงหรือ int's - และตามประเภทเหล่านั้น "+" จะทำงานแตกต่างกัน

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


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

มันอาจจะง่ายกว่าที่จะอธิบายความหลากหลายในแง่ของการถ่ายทอดทางพันธุกรรมเพียงอย่างเดียว แต่วิธีที่คำถามนี้ถูกถามฉันคิดว่ามันรอบคอบที่จะอธิบายความหลากหลายรูปแบบของตัวแปร
Patrick McElhaney

4
เพื่อความชัดเจนฉันคิดว่าควรระบุรูปแบบที่แตกต่าง - ซึ่งฉันยังไม่ได้ทำอย่างเพียงพอ - เพราะมีคำตอบไม่กี่ข้อที่นำเสนออย่างสมบูรณ์ ฉันไม่เห็นด้วยอย่างเคารพว่าใน "บริบทของโปรแกรมเมอร์ ... 'polymorphism' มักจะหมายถึง 'polymorphism ที่ใช้การสืบทอด' เสมอ
Peter Meyer

2
ฉันคิดว่าการโหลดมากเกินไปนั้นจัดว่าดีกว่าเป็น Ad-hoc_polymorphism en.wikipedia.org/wiki/…
มนู

ฉันมักจะเห็นด้วยกับ 'The Digital Gabeg' ในการติดตาม หากคุณกำลังพูดคุยเกี่ยวกับ OOP ความแตกต่างมักจะหมายถึงความหลากหลายย่อยและถ้าคุณกำลังพูดคุยเกี่ยวกับทฤษฎีประเภทมันหมายถึงความหลากหลายชนิดใด ๆ แต่เหมือนที่คุณพูดด้วย 'บริบทของโปรแกรมเมอร์' มันคลุมเครือเกินไปที่จะดูถูก
มนู

7

ความแตกต่างก็หมายถึง "หลายรูปแบบ"

มันไม่ได้รับการร้องขอการสืบทอดเพื่อให้บรรลุ ... เนื่องจากการใช้อินเตอร์เฟสซึ่งไม่ได้รับมรดกเลยสามารถตอบสนองความต้องการ polymorphic ได้ การใช้งานอินเทอร์เฟซรองรับความต้องการ polymorphic "ดีกว่า" มากกว่าการสืบทอด

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

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


2
คำตอบที่ดีที่สุดแน่นอนยัง ความแตกต่างสามารถนำไปใช้กับการสร้างภาษาทั้งหมดไม่ว่าจะเป็นคำนาม (ชั้นเรียน) หรือคำกริยา (วิธีการ)
Radu Gasler

6

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

รหัสการโทรไม่จำเป็นต้องรู้ว่าเป็นสัตว์ชนิดใด

นั่นคือสิ่งที่ฉันคิดว่าเป็นความแตกต่าง


4

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


4

ทั้ง:

การโหลดมากเกินไปคือเมื่อคุณมีชื่อฟังก์ชันเดียวกันกับที่ใช้พารามิเตอร์ต่างกัน

การเอาชนะคือเมื่อคลาสเด็กแทนที่เมธอดของพาเรนต์ด้วยหนึ่งในตัวของมันเอง (ซึ่งในตัวมันเองไม่ได้มีความหลากหลาย)

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

ใน Java คุณเห็นความแตกต่างมากมายกับห้องสมุดคอลเลกชัน:

int countStuff(List stuff) {
  return stuff.size();
}

List เป็นคลาสพื้นฐานคอมไพเลอร์ไม่มีเงื่อนงำถ้าคุณกำลังนับรายการเชื่อมโยงเวกเตอร์อาร์เรย์หรือการใช้งานรายการที่กำหนดเองตราบใดที่มันทำหน้าที่เหมือนกับ List:

List myStuff = new MyTotallyAwesomeList();
int result = countStuff(myStuff);

หากคุณทำงานหนักมากเกินไปคุณจะมี:

int countStuff(LinkedList stuff) {...}
int countStuff(ArrayList stuff) {...}
int countStuff(MyTotallyAwesomeList stuff) {...}
etc...

และเวอร์ชันที่ถูกต้องของ countStuff () จะถูกเลือกโดยคอมไพเลอร์เพื่อให้ตรงกับพารามิเตอร์


4

แม้ว่าความแตกต่างได้อธิบายไว้แล้วในรายละเอียดที่ดีในโพสต์นี้ แต่ฉันอยากจะเน้นว่าทำไมส่วนหนึ่งของมัน

ทำไมความแตกต่างจึงมีความสำคัญในภาษา OOP

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

สมมติว่าคุณเป็นวิศวกรซอฟต์แวร์ที่ บริษัท ทีวีและขอให้คุณเขียนซอฟต์แวร์สำหรับตัวควบคุมระดับเสียง, ความสว่างและสีเพื่อเพิ่มและลดค่าของพวกเขาในคำสั่งผู้ใช้

คุณเริ่มต้นด้วยการเขียนคลาสสำหรับแต่ละคุณสมบัติเหล่านี้โดยการเพิ่ม

  1. set: - เพื่อตั้งค่าของตัวควบคุม (หากมีรหัสเฉพาะของตัวควบคุม)
  2. รับ: - เพื่อรับค่าของตัวควบคุม (หากมีรหัสเฉพาะตัวควบคุม)
  3. Adjust: - เพื่อตรวจสอบอินพุตและการตั้งค่าคอนโทรลเลอร์ (การตรวจสอบทั่วไป .. เป็นอิสระจากคอนโทรลเลอร์)
  4. การแมปอินพุตของผู้ใช้กับตัวควบคุม: - เพื่อรับอินพุตของผู้ใช้และเรียกใช้ตัวควบคุมตามลำดับ

แอปพลิเคชันเวอร์ชัน 1

import java.util.Scanner;    
class VolumeControllerV1 {
    private int value;
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of VolumeController \t"+this.value);
        this.value = value;
        System.out.println("New value of VolumeController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}
class  BrightnessControllerV1 {
    private int value;
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of BrightnessController \t"+this.value);
        this.value = value;
        System.out.println("New value of BrightnessController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}
class ColourControllerV1    {
    private int value;
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of ColourController \t"+this.value);
        this.value = value;
        System.out.println("New value of ColourController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}

/*
 *       There can be n number of controllers
 * */
public class TvApplicationV1 {
    public static void main(String[] args)  {
        VolumeControllerV1 volumeControllerV1 = new VolumeControllerV1();
        BrightnessControllerV1 brightnessControllerV1 = new BrightnessControllerV1();
        ColourControllerV1 colourControllerV1 = new ColourControllerV1();


        OUTER: while(true) {
            Scanner sc=new Scanner(System.in);
            System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
            System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
            System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
            System.out.println("Press any other Button to shutdown");
            int button = sc.nextInt();
            switch (button) {
                case  1:    {
                    volumeControllerV1.adjust(5);
                    break;
                }
                case 2: {
                    volumeControllerV1.adjust(-5);
                    break;
                }
                case  3:    {
                    brightnessControllerV1.adjust(5);
                    break;
                }
                case 4: {
                    brightnessControllerV1.adjust(-5);
                    break;
                }
                case  5:    {
                    colourControllerV1.adjust(5);
                    break;
                }
                case 6: {
                colourControllerV1.adjust(-5);
                break;
            }
            default:
                System.out.println("Shutting down...........");
                break OUTER;
        }

    }
    }
}

ตอนนี้คุณมีแอปพลิเคชันการทำงานรุ่นแรกของเราพร้อมที่จะนำไปใช้งานแล้ว ถึงเวลาวิเคราะห์งานที่ทำไปแล้ว

ปัญหาในแอปพลิเคชันทีวีเวอร์ชัน 1

  1. รหัสการปรับ (ค่า int) ซ้ำกันในทั้งสามคลาส คุณต้องการลดความซ้ำซ้อนของรหัส (แต่คุณไม่ได้คิดว่ารหัสทั่วไปและย้ายไปที่บางระดับชั้นเพื่อหลีกเลี่ยงรหัสที่ซ้ำกัน)

คุณตัดสินใจที่จะอยู่กับมันตราบใดที่ใบสมัครของคุณทำงานตามที่คาดไว้

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

คุณเริ่มเขียนคลาสใหม่ (ResetFunctionV2) สำหรับฟังก์ชันการทำงานใหม่และแมปรหัสการแมปการป้อนข้อมูลผู้ใช้สำหรับคุณลักษณะใหม่นี้

แอปพลิเคชันเวอร์ชัน 2

import java.util.Scanner;
class VolumeControllerV2    {

    private int defaultValue = 25;
    private int value;

    int getDefaultValue() {
        return defaultValue;
    }
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of VolumeController \t"+this.value);
        this.value = value;
        System.out.println("New value of VolumeController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}
class  BrightnessControllerV2   {

    private int defaultValue = 50;
    private int value;
    int get()    {
        return value;
    }
    int getDefaultValue() {
        return defaultValue;
    }
    void set(int value) {
        System.out.println("Old value of BrightnessController \t"+this.value);
        this.value = value;
        System.out.println("New value of BrightnessController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}
class ColourControllerV2    {

    private int defaultValue = 40;
    private int value;
    int get()    {
        return value;
    }
    int getDefaultValue() {
        return defaultValue;
    }
    void set(int value) {
        System.out.println("Old value of ColourController \t"+this.value);
        this.value = value;
        System.out.println("New value of ColourController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}

class ResetFunctionV2 {

    private VolumeControllerV2 volumeControllerV2 ;
    private BrightnessControllerV2 brightnessControllerV2;
    private ColourControllerV2 colourControllerV2;

    ResetFunctionV2(VolumeControllerV2 volumeControllerV2, BrightnessControllerV2 brightnessControllerV2, ColourControllerV2 colourControllerV2)  {
        this.volumeControllerV2 = volumeControllerV2;
        this.brightnessControllerV2 = brightnessControllerV2;
        this.colourControllerV2 = colourControllerV2;
    }
    void onReset()    {
        volumeControllerV2.set(volumeControllerV2.getDefaultValue());
        brightnessControllerV2.set(brightnessControllerV2.getDefaultValue());
        colourControllerV2.set(colourControllerV2.getDefaultValue());
    }
}
/*
 *       so on
 *       There can be n number of controllers
 *
 * */
public class TvApplicationV2 {
    public static void main(String[] args)  {
        VolumeControllerV2 volumeControllerV2 = new VolumeControllerV2();
        BrightnessControllerV2 brightnessControllerV2 = new BrightnessControllerV2();
        ColourControllerV2 colourControllerV2 = new ColourControllerV2();

        ResetFunctionV2 resetFunctionV2 = new ResetFunctionV2(volumeControllerV2, brightnessControllerV2, colourControllerV2);

        OUTER: while(true) {
            Scanner sc=new Scanner(System.in);
            System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
            System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
            System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
            System.out.println(" Press 7 to reset TV \n Press any other Button to shutdown");
            int button = sc.nextInt();
            switch (button) {
                case  1:    {
                    volumeControllerV2.adjust(5);
                    break;
                }
                case 2: {
                    volumeControllerV2.adjust(-5);
                    break;
                }
                case  3:    {
                    brightnessControllerV2.adjust(5);
                    break;
                }
                case 4: {
                    brightnessControllerV2.adjust(-5);
                    break;
                }
                case  5:    {
                    colourControllerV2.adjust(5);
                    break;
                }
                case 6: {
                    colourControllerV2.adjust(-5);
                    break;
                }
                case 7: {
                    resetFunctionV2.onReset();
                    break;
                }
                default:
                    System.out.println("Shutting down...........");
                    break OUTER;
            }

        }
    }
}

เพื่อให้คุณพร้อมใช้งานกับคุณสมบัติรีเซ็ต แต่ตอนนี้คุณเริ่มตระหนักว่า

ปัญหาในแอปพลิเคชันทีวีเวอร์ชัน 2

  1. หากมีการแนะนำคอนโทรลเลอร์ใหม่ให้กับผลิตภัณฑ์คุณจะต้องเปลี่ยนรหัสคุณสมบัติรีเซ็ต
  2. หากจำนวนตัวควบคุมเพิ่มขึ้นสูงมากคุณจะมีปัญหาในการเก็บข้อมูลอ้างอิงของตัวควบคุม
  3. รีเซ็ตรหัสคุณลักษณะควบคู่ไปกับรหัสของคลาสคอนโทรลเลอร์อย่างแน่นหนา (เพื่อรับและตั้งค่าเริ่มต้น)
  4. รีเซ็ตฟีเจอร์คลาส (ResetFunctionV2) สามารถเข้าถึงวิธีอื่นของ (ปรับ) คลาสคอนโทรลเลอร์ที่ไม่พึงประสงค์

ในเวลาเดียวกันคุณได้ยินจากคุณบอสว่าคุณอาจต้องเพิ่มคุณสมบัติที่คอนโทรลเลอร์แต่ละตัวเริ่มต้นต้องตรวจสอบไดรเวอร์รุ่นล่าสุดจากที่เก็บโฮสต์ไดรเวอร์ของ บริษัท ผ่านอินเทอร์เน็ต

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

คุณเริ่มคิดถึงการใช้การสืบทอดเพื่อให้คุณสามารถใช้ประโยชน์จากความสามารถ polymorphic ของ JAVA และคุณเพิ่มคลาส abstract ใหม่ (ControllerV3) ไปยัง

  1. ประกาศลายเซ็นของ get และ set method
  2. มีการปรับใช้งานวิธีการซึ่งถูกจำลองแบบก่อนหน้านี้ในตัวควบคุมทั้งหมด
  3. ประกาศเมธอด setDefault เพื่อให้คุณลักษณะรีเซ็ตสามารถนำไปใช้ประโยชน์ได้อย่างง่ายดายจาก Polymorphism

ด้วยการปรับปรุงเหล่านี้คุณมีแอปพลิเคชั่นทีวีเวอร์ชัน 3 พร้อมกับคุณ

แอปพลิเคชันเวอร์ชัน 3

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

abstract class ControllerV3 {
    abstract void set(int value);
    abstract int get();
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
    abstract void setDefault();
}
class VolumeControllerV3 extends ControllerV3   {

    private int defaultValue = 25;
    private int value;

    public void setDefault() {
        set(defaultValue);
    }
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of VolumeController \t"+this.value);
        this.value = value;
        System.out.println("New value of VolumeController \t"+this.value);
    }
}
class  BrightnessControllerV3  extends ControllerV3   {

    private int defaultValue = 50;
    private int value;

    public void setDefault() {
        set(defaultValue);
    }
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of BrightnessController \t"+this.value);
        this.value = value;
        System.out.println("New value of BrightnessController \t"+this.value);
    }
}
class ColourControllerV3 extends ControllerV3   {

    private int defaultValue = 40;
    private int value;

    public void setDefault() {
        set(defaultValue);
    }
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of ColourController \t"+this.value);
        this.value = value;
        System.out.println("New value of ColourController \t"+this.value);
    }
}

class ResetFunctionV3 {

    private List<ControllerV3> controllers = null;

    ResetFunctionV3(List<ControllerV3> controllers)  {
        this.controllers = controllers;
    }
    void onReset()    {
        for (ControllerV3 controllerV3 :this.controllers)  {
            controllerV3.setDefault();
        }
    }
}
/*
 *       so on
 *       There can be n number of controllers
 *
 * */
public class TvApplicationV3 {
    public static void main(String[] args)  {
        VolumeControllerV3 volumeControllerV3 = new VolumeControllerV3();
        BrightnessControllerV3 brightnessControllerV3 = new BrightnessControllerV3();
        ColourControllerV3 colourControllerV3 = new ColourControllerV3();

        List<ControllerV3> controllerV3s = new ArrayList<>();
        controllerV3s.add(volumeControllerV3);
        controllerV3s.add(brightnessControllerV3);
        controllerV3s.add(colourControllerV3);

        ResetFunctionV3 resetFunctionV3 = new ResetFunctionV3(controllerV3s);

        OUTER: while(true) {
            Scanner sc=new Scanner(System.in);
            System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
            System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
            System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
            System.out.println(" Press 7 to reset TV \n Press any other Button to shutdown");
            int button = sc.nextInt();
            switch (button) {
                case  1:    {
                    volumeControllerV3.adjust(5);
                    break;
                }
                case 2: {
                    volumeControllerV3.adjust(-5);
                    break;
                }
                case  3:    {
                    brightnessControllerV3.adjust(5);
                    break;
                }
                case 4: {
                    brightnessControllerV3.adjust(-5);
                    break;
                }
                case  5:    {
                    colourControllerV3.adjust(5);
                    break;
                }
                case 6: {
                    colourControllerV3.adjust(-5);
                    break;
                }
                case 7: {
                    resetFunctionV3.onReset();
                    break;
                }
                default:
                    System.out.println("Shutting down...........");
                    break OUTER;
            }

        }
    }
}

แม้ว่าส่วนใหญ่ของปัญหาที่ระบุไว้ในรายการปัญหาของ V2 ได้รับการแก้ไขยกเว้น

ปัญหาในแอปพลิเคชันทีวีเวอร์ชัน 3

  1. รีเซ็ตฟีเจอร์คลาส (ResetFunctionV3) สามารถเข้าถึงวิธีอื่นของ (ปรับ) คลาสคอนโทรลเลอร์ที่ไม่พึงประสงค์

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

ดังนั้นคุณแบ่งสัญญาที่กำหนดไว้ในคลาสนามธรรมและเขียน 2 อินเตอร์เฟสสำหรับ

  1. รีเซ็ตคุณสมบัติ
  2. อัพเดตไดร์เวอร์

และให้ชั้นรูปธรรมที่ 1 ของคุณนำไปใช้งานได้ดังนี้

แอปพลิเคชันเวอร์ชัน 4

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

interface OnReset {
    void setDefault();
}
interface OnStart {
    void checkForDriverUpdate();
}
abstract class ControllerV4 implements OnReset,OnStart {
    abstract void set(int value);
    abstract int get();
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}

class VolumeControllerV4 extends ControllerV4 {

    private int defaultValue = 25;
    private int value;
    @Override
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of VolumeController \t"+this.value);
        this.value = value;
        System.out.println("New value of VolumeController \t"+this.value);
    }
    @Override
    public void setDefault() {
        set(defaultValue);
    }

    @Override
    public void checkForDriverUpdate()    {
        System.out.println("Checking driver update for VolumeController .... Done");
    }
}
class  BrightnessControllerV4 extends ControllerV4 {

    private int defaultValue = 50;
    private int value;
    @Override
    int get()    {
        return value;
    }
    @Override
    void set(int value) {
        System.out.println("Old value of BrightnessController \t"+this.value);
        this.value = value;
        System.out.println("New value of BrightnessController \t"+this.value);
    }

    @Override
    public void setDefault() {
        set(defaultValue);
    }

    @Override
    public void checkForDriverUpdate()    {
        System.out.println("Checking driver update for BrightnessController .... Done");
    }
}
class ColourControllerV4 extends ControllerV4 {

    private int defaultValue = 40;
    private int value;
    @Override
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of ColourController \t"+this.value);
        this.value = value;
        System.out.println("New value of ColourController \t"+this.value);
    }
    @Override
    public void setDefault() {
        set(defaultValue);
    }

    @Override
    public void checkForDriverUpdate()    {
        System.out.println("Checking driver update for ColourController .... Done");
    }
}
class ResetFunctionV4 {

    private List<OnReset> controllers = null;

    ResetFunctionV4(List<OnReset> controllers)  {
        this.controllers = controllers;
    }
    void onReset()    {
        for (OnReset onreset :this.controllers)  {
            onreset.setDefault();
        }
    }
}
class InitializeDeviceV4 {

    private List<OnStart> controllers = null;

    InitializeDeviceV4(List<OnStart> controllers)  {
        this.controllers = controllers;
    }
    void initialize()    {
        for (OnStart onStart :this.controllers)  {
            onStart.checkForDriverUpdate();
        }
    }
}
/*
*       so on
*       There can be n number of controllers
*
* */
public class TvApplicationV4 {
    public static void main(String[] args)  {
        VolumeControllerV4 volumeControllerV4 = new VolumeControllerV4();
        BrightnessControllerV4 brightnessControllerV4 = new BrightnessControllerV4();
        ColourControllerV4 colourControllerV4 = new ColourControllerV4();
        List<ControllerV4> controllerV4s = new ArrayList<>();
        controllerV4s.add(brightnessControllerV4);
        controllerV4s.add(volumeControllerV4);
        controllerV4s.add(colourControllerV4);

        List<OnStart> controllersToInitialize = new ArrayList<>();
        controllersToInitialize.addAll(controllerV4s);
        InitializeDeviceV4 initializeDeviceV4 = new InitializeDeviceV4(controllersToInitialize);
        initializeDeviceV4.initialize();

        List<OnReset> controllersToReset = new ArrayList<>();
        controllersToReset.addAll(controllerV4s);
        ResetFunctionV4 resetFunctionV4 = new ResetFunctionV4(controllersToReset);

        OUTER: while(true) {
            Scanner sc=new Scanner(System.in);
            System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
            System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
            System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
            System.out.println(" Press 7 to reset TV \n Press any other Button to shutdown");
            int button = sc.nextInt();
            switch (button) {
                case  1:    {
                    volumeControllerV4.adjust(5);
                    break;
                }
                case 2: {
                    volumeControllerV4.adjust(-5);
                    break;
                }
                case  3:    {
                    brightnessControllerV4.adjust(5);
                    break;
                }
                case 4: {
                    brightnessControllerV4.adjust(-5);
                    break;
                }
                case  5:    {
                    colourControllerV4.adjust(5);
                    break;
                }
                case 6: {
                    colourControllerV4.adjust(-5);
                    break;
                }
                case 7: {
                    resetFunctionV4.onReset();
                    break;
                }
                default:
                    System.out.println("Shutting down...........");
                    break OUTER;
            }

        }
    }
}

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

  1. เก็บส่วนต่าง ๆ ของแอปพลิเคชันไว้อย่างหลวม ๆ (องค์ประกอบของฟีเจอร์การรีเซ็ตหรือการอัปเดตไดรเวอร์ไม่จำเป็นต้องทราบถึงคลาสคอนโทรลเลอร์จริง (ระดับเสียง, ความสว่างและสี), การใช้คลาส OnReset หรือ OnStart จะยอมรับคุณสมบัติการรีเซ็ตหรือไดรเวอร์ ส่วนประกอบตามลำดับ)
  2. การเพิ่มประสิทธิภาพของแอพพลิเคชั่นจะง่ายขึ้น (การเพิ่มตัวควบคุมใหม่จะไม่ส่งผลกระทบต่อการรีเซ็ตหรือองค์ประกอบคุณสมบัติการอัพเดทไดรเวอร์และตอนนี้มันง่ายสำหรับคุณที่จะเพิ่มใหม่)
  3. เก็บเลเยอร์ของสิ่งที่เป็นนามธรรม (ขณะนี้คุณลักษณะรีเซ็ตสามารถดูได้เฉพาะเมธอด setDefault ของตัวควบคุมและคุณลักษณะรีเซ็ตสามารถดูได้เฉพาะวิธี checkForDriverUpdate ของคอนโทรลเลอร์

หวังว่านี่จะช่วย :-)


3

คำที่มากเกินไปหมายถึงการมีบางสิ่งบางอย่างหลายรุ่นที่มีชื่อเดียวกันโดยปกติวิธีการที่มีรายการพารามิเตอร์ที่แตกต่างกัน

public int DoSomething(int objectId) { ... }
public int DoSomething(string objectName) { ... }

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

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


2

การบรรทุกมากเกินไปคือเมื่อคุณกำหนด 2 วิธีด้วยชื่อเดียวกัน แต่มีพารามิเตอร์ต่างกัน

การแทนที่คือที่ที่คุณเปลี่ยนพฤติกรรมของคลาสพื้นฐานผ่านฟังก์ชันที่มีชื่อเดียวกันในคลาสย่อย

ดังนั้นความแตกต่างมีความเกี่ยวข้องกับการเอาชนะ แต่ไม่มากไปจริง ๆ

อย่างไรก็ตามถ้ามีคนให้คำตอบง่ายๆสำหรับฉัน "เอาชนะ" สำหรับคำถาม "ความแตกต่างคืออะไร" ฉันจะขอคำอธิบายเพิ่มเติม


2

การเอาชนะก็เหมือนกับการซ่อนวิธีสืบทอดโดยการประกาศวิธีการที่มีชื่อและลายเซ็นเหมือนกันกับวิธีการระดับบน (วิธีการ super) ซึ่งจะเพิ่มพฤติกรรม polymorphic ให้กับชั้นเรียน กล่าวอีกนัยหนึ่งการตัดสินใจเลือกวิธีระดับที่จะเรียกในเวลาทำงานไม่ใช่เวลารวบรวม สิ่งนี้นำไปสู่แนวคิดของอินเทอร์เฟซและการใช้งาน


2

ความแตกต่างคืออะไร?

จากการสอนด้วยจาวา

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

โดยการพิจารณาตัวอย่างและคำจำกัดความการเอาชนะควรได้รับการยอมรับคำตอบ

เกี่ยวกับข้อความค้นหาที่สองของคุณ:

หากคุณมีคลาสฐานนามธรรมที่กำหนดวิธีการที่ไม่มีการใช้งานและคุณกำหนดวิธีการนั้นในคลาสย่อยนั้นจะยังคงทับซ้อนกัน?

มันควรจะเรียกว่าเอาชนะ

ลองดูตัวอย่างนี้เพื่อทำความเข้าใจกับการเอาชนะประเภทต่างๆ

  1. คลาสพื้นฐานไม่มีการนำไปใช้และคลาสย่อยต้องแทนที่เมธอดทั้งหมด - (นามธรรม)
  2. คลาสพื้นฐานให้การใช้งานเริ่มต้นและคลาสย่อยสามารถเปลี่ยนพฤติกรรมได้
  3. คลาสย่อยเพิ่มส่วนขยายให้กับการใช้คลาสพื้นฐานโดยการเรียกใช้super.methodName()เป็นคำสั่งแรก
  4. คลาสพื้นฐานจะกำหนดโครงสร้างของอัลกอริทึม (วิธีเทมเพลต) และคลาสย่อยจะแทนที่ส่วนของอัลกอริทึม

ข้อมูลโค้ด:

import java.util.HashMap;

abstract class Game implements Runnable{

    protected boolean runGame = true;
    protected Player player1 = null;
    protected Player player2 = null;
    protected Player currentPlayer = null;

    public Game(){
        player1 = new Player("Player 1");
        player2 = new Player("Player 2");
        currentPlayer = player1;
        initializeGame();
    }

    /* Type 1: Let subclass define own implementation. Base class defines abstract method to force
        sub-classes to define implementation    
    */

    protected abstract void initializeGame();

    /* Type 2: Sub-class can change the behaviour. If not, base class behaviour is applicable */
    protected void logTimeBetweenMoves(Player player){
        System.out.println("Base class: Move Duration: player.PlayerActTime - player.MoveShownTime");
    }

    /* Type 3: Base class provides implementation. Sub-class can enhance base class implementation by calling
        super.methodName() in first line of the child class method and specific implementation later */
    protected void logGameStatistics(){
        System.out.println("Base class: logGameStatistics:");
    }
    /* Type 4: Template method: Structure of base class can't be changed but sub-class can some part of behaviour */
    protected void runGame() throws Exception{
        System.out.println("Base class: Defining the flow for Game:");  
        while ( runGame) {
            /*
            1. Set current player
            2. Get Player Move
            */
            validatePlayerMove(currentPlayer);  
            logTimeBetweenMoves(currentPlayer);
            Thread.sleep(500);
            setNextPlayer();
        }
        logGameStatistics();
    }
    /* sub-part of the template method, which define child class behaviour */
    protected abstract void validatePlayerMove(Player p);

    protected void setRunGame(boolean status){
        this.runGame = status;
    }
    public void setCurrentPlayer(Player p){
        this.currentPlayer = p;
    }
    public void setNextPlayer(){
        if ( currentPlayer == player1) {
            currentPlayer = player2;
        }else{
            currentPlayer = player1;
        }
    }
    public void run(){
        try{
            runGame();
        }catch(Exception err){
            err.printStackTrace();
        }
    }
}

class Player{
    String name;
    Player(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
}

/* Concrete Game implementation  */
class Chess extends Game{
    public Chess(){
        super();
    }
    public void initializeGame(){
        System.out.println("Child class: Initialized Chess game");
    }
    protected void validatePlayerMove(Player p){
        System.out.println("Child class: Validate Chess move:"+p.getName());
    }
    protected void logGameStatistics(){
        super.logGameStatistics();
        System.out.println("Child class: Add Chess specific logGameStatistics:");
    }
}
class TicTacToe extends Game{
    public TicTacToe(){
        super();
    }
    public void initializeGame(){
        System.out.println("Child class: Initialized TicTacToe game");
    }
    protected void validatePlayerMove(Player p){
        System.out.println("Child class: Validate TicTacToe move:"+p.getName());
    }
}

public class Polymorphism{
    public static void main(String args[]){
        try{

            Game game = new Chess();
            Thread t1 = new Thread(game);
            t1.start();
            Thread.sleep(1000);
            game.setRunGame(false);
            Thread.sleep(1000);

            game = new TicTacToe();
            Thread t2 = new Thread(game);
            t2.start();
            Thread.sleep(1000);
            game.setRunGame(false);

        }catch(Exception err){
            err.printStackTrace();
        }       
    }
}

เอาท์พุท:

Child class: Initialized Chess game
Base class: Defining the flow for Game:
Child class: Validate Chess move:Player 1
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Child class: Validate Chess move:Player 2
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Base class: logGameStatistics:
Child class: Add Chess specific logGameStatistics:
Child class: Initialized TicTacToe game
Base class: Defining the flow for Game:
Child class: Validate TicTacToe move:Player 1
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Child class: Validate TicTacToe move:Player 2
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Base class: logGameStatistics:

2

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

  1. การผูกปลาย
  2. มรดก

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

อย่างไรก็ตามใน C ++ ที่ไม่เป็นเช่นนั้น วิธีการโอเวอร์โหลดใด ๆอย่างอิสระไม่ว่าจะเป็นลายเซ็นเหมือนกันหรือไม่ (จำนวนเงินที่แตกต่างกัน, ประเภทที่แตกต่างกัน) ก็ถูกแทนที่ด้วยด้วย นั่นคือวันนี้เมธอดคลาสพื้นฐาน 'ไม่พร้อมใช้งานในคลาสย่อยเมื่อถูกเรียกจากนอกวัตถุคลาสย่อยอย่างชัดเจน

ดังนั้นคำตอบคือเมื่อพูดถึง Java ใช้งานมากไป ในภาษาอื่นอาจแตกต่างจากที่เกิดขึ้นใน c ++


1

ความแตกต่างมีแนวโน้มมากที่สุดเท่าที่มันมีความหมายที่เกี่ยวข้อง ... เพื่อเอาชนะในจาวา

มันเกี่ยวกับพฤติกรรมที่แตกต่างกันของวัตถุ SAME ในสถานการณ์ที่แตกต่างกัน (ในวิธีการเขียนโปรแกรม ... คุณสามารถเรียกใช้ ARGUMENTS ที่แตกต่างกันได้)

ฉันคิดว่าตัวอย่างด้านล่างจะช่วยให้คุณเข้าใจ ... แม้ว่าจะไม่ใช่รหัสจาวา PURE ...

     public void See(Friend)
     {
        System.out.println("Talk");
     }

แต่ถ้าเราเปลี่ยน ARGUMENT ... พฤติกรรมจะเปลี่ยน ...

     public void See(Enemy)
     {
        System.out.println("Run");
     }

บุคคล (ที่นี่ "วัตถุ") เหมือนกัน ...


1

ความแตกต่างคือการใช้งานหลายอย่างของวัตถุหรือคุณสามารถพูดได้หลายรูปแบบของวัตถุ สมมติว่าคุณมีคลาสAnimalsเป็นคลาสพื้นฐานที่เป็นนามธรรมและมีวิธีที่เรียกว่าmovement()ซึ่งกำหนดวิธีการเคลื่อนไหวของสัตว์ ตอนนี้ในความเป็นจริงเรามีสัตว์ชนิดต่าง ๆ และพวกมันเคลื่อนไหวต่างกันด้วยบางส่วนมี 2 ขาส่วนที่มี 4 และบางตัวไม่มีขา ฯลฯ เพื่อกำหนดความแตกต่างmovement()ของสัตว์แต่ละชนิดบนโลกเราจำเป็นต้องใช้ความหลากหลาย อย่างไรก็ตามคุณต้องกำหนดคลาสเพิ่มเติมเช่นคลาสDogs Cats Fishเป็นต้นจากนั้นคุณต้องขยายคลาสเหล่านั้นออกจากคลาสฐานAnimalsและแทนที่เมธอดของคลาสนั้นmovement()ด้วยฟังก์ชันการเคลื่อนไหวใหม่ตามสัตว์แต่ละตัวที่คุณมี คุณยังสามารถใช้Interfacesเพื่อให้บรรลุ คำหลักในที่นี้คือการแทนที่การใช้งานมากเกินไปแตกต่างกันและไม่ถือว่าเป็นความแตกต่าง ด้วยการโหลดมากเกินไปคุณสามารถกำหนดหลายวิธี "ด้วยชื่อเดียวกัน" แต่ด้วยพารามิเตอร์ที่แตกต่างกันในวัตถุหรือคลาสเดียวกัน


0

ความแตกต่างเกี่ยวข้องกับความสามารถของภาษาในการมีวัตถุที่แตกต่างกันได้รับการปฏิบัติอย่างสม่ำเสมอโดยใช้อินเทอร์เฟซเดียว เช่นนี้เกี่ยวข้องกับการเอาชนะดังนั้นอินเตอร์เฟส (หรือคลาสพื้นฐาน) คือ polymorphic, implementor เป็นวัตถุที่แทนที่ (สองหน้าของเหรียญเดียวกัน)

อย่างไรก็ตามความแตกต่างระหว่างคำสองคำจะดีกว่าอธิบายโดยใช้ภาษาอื่น ๆ เช่นภาษา C ++: วัตถุ polymorphic ใน c ++ ทำงานเป็นคู่จาวาถ้าฟังก์ชันฐานเป็นเสมือน แต่ถ้าวิธีการไม่ได้เป็นเสมือนกระโดดรหัสได้รับการแก้ไขแบบคงที่ , และชนิดที่แท้จริงไม่ได้ถูกตรวจสอบ ณ รันไทม์ดังนั้นความหลากหลายรวมถึงความสามารถในการทำงานของวัตถุที่แตกต่างกันขึ้นอยู่กับอินเตอร์เฟสที่ใช้ในการเข้าถึง ให้ฉันทำตัวอย่างใน pseudocode:

class animal {
    public void makeRumor(){
        print("thump");
    }
}
class dog extends animal {
    public void makeRumor(){
        print("woff");
    }
}

animal a = new dog();
dog b = new dog();

a.makeRumor() -> prints thump
b.makeRumor() -> prints woff

(สมมติว่า makeRumor ไม่ใช่เสมือน)

java ไม่ได้เสนอระดับความหลากหลายนี้อย่างแท้จริง (เรียกว่าการแบ่งวัตถุ)

สัตว์ a = หมาตัวใหม่ (); สุนัข b = สุนัขใหม่ ();

a.makeRumor() -> prints thump
b.makeRumor() -> prints woff

ในทั้งสองกรณีมันจะพิมพ์เฉพาะ woff .. เนื่องจาก a และ b อ้างถึง dog class


การอ้างอิงบางส่วน: linuxtopia.org/online_books/programming_books/thinking_in_c++/…
Lorenzo Boccaccia

สัตว์ a = หมาตัวใหม่ (); a ถูกสร้างเป็นสุนัขและจะพิมพ์ "woff" ถ้าคุณต้องการให้พิมพ์กระหน่ำคุณต้อง upcast มัน (สัตว์) a .makeRumor ()
Chris Cudmore

นั่นคือการอ้างอิงที่ทำให้ดูแคลน แต่วัตถุก็ยังเป็นสุนัขอยู่ หากคุณต้องการให้มันเป็นสัตว์คุณต้องถ่ายทอดวัตถุอย่างชัดเจน
Chris Cudmore

คิดออก คำถามถูกติดแท็ก Java คุณตอบ C ++ คุณอาจถูกต้องใน C ++ ฉันถูกต้องแน่นอนใน Java
Chris Cudmore

ควรเกิดขึ้นทุกครั้งที่ตัวสร้างการคัดลอกมีส่วนเกี่ยวข้องที่นี่คือfredosaurus.com/notes-cpp/oop-condestructorsการอ้างอิง กรณีที่สามตรงกัน; เพิกเฉยโอเปอเรเตอร์ใหม่ซึ่งมีเพียงการสร้างความกำกวม
Lorenzo Boccaccia

0
import java.io.IOException;

class Super {

    protected Super getClassName(Super s) throws IOException {
        System.out.println(this.getClass().getSimpleName() + " - I'm parent");
        return null;
    }

}

class SubOne extends Super {

    @Override
    protected Super getClassName(Super s)  {
        System.out.println(this.getClass().getSimpleName() + " - I'm Perfect Overriding");
        return null;
    }

}

class SubTwo extends Super {

    @Override
    protected Super getClassName(Super s) throws NullPointerException {
        System.out.println(this.getClass().getSimpleName() + " - I'm Overriding and Throwing Runtime Exception");
        return null;
    }

}

class SubThree extends Super {

    @Override
    protected SubThree getClassName(Super s) {
        System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and Returning SubClass Type");
        return null;
    }

}

class SubFour extends Super {

    @Override
    protected Super getClassName(Super s) throws IOException {
        System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and Throwing Narrower Exception ");
        return null;
    }

}

class SubFive extends Super {

    @Override
    public Super getClassName(Super s) {
        System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and have broader Access ");
        return null;
    }

}

class SubSix extends Super {

    public Super getClassName(Super s, String ol) {
        System.out.println(this.getClass().getSimpleName()+ " - I'm Perfect Overloading ");
        return null;
    }

}

class SubSeven extends Super {

    public Super getClassName(SubSeven s) {
        System.out.println(this.getClass().getSimpleName()+ " - I'm Perfect Overloading because Method signature (Argument) changed.");
        return null;
    }

}

public class Test{

    public static void main(String[] args) throws Exception {

        System.out.println("Overriding\n");

        Super s1 = new SubOne(); s1.getClassName(null);

        Super s2 = new SubTwo(); s2.getClassName(null);

        Super s3 = new SubThree(); s3.getClassName(null);

        Super s4 = new SubFour(); s4.getClassName(null);

        Super s5 = new SubFive(); s5.getClassName(null);

        System.out.println("Overloading\n");

        SubSix s6 = new SubSix(); s6.getClassName(null, null);

        s6 = new SubSix(); s6.getClassName(null);

        SubSeven s7 = new SubSeven(); s7.getClassName(s7);

        s7 = new SubSeven(); s7.getClassName(new Super());

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