JavaRush /จาวาบล็อก /Random-TH /อินเทอร์เฟซสำหรับผู้ที่ “สนใจมาก แต่ไม่เข้าใจอะไรเลย”
Lilly
ระดับ
Москва

อินเทอร์เฟซสำหรับผู้ที่ “สนใจมาก แต่ไม่เข้าใจอะไรเลย”

เผยแพร่ในกลุ่ม
สวัสดีทุกคน! โพสต์นี้เขียนขึ้นสำหรับผู้ที่ได้เห็นและแก้ไขปัญหาต่าง ๆ บนอินเทอร์เฟซที่ระดับ 12 (Java Core ระดับ 2) แล้ว แต่ยังสงสัยว่า: “เหตุใดจึงจำเป็นหากคุณยังต้องการใช้วิธีการใน นั้น ?” ประกาศในอินเทอร์เฟส??? คือ ไม่ลดจำนวนโค๊ด!!!" . ฉันไม่สงสัยเลยว่าหัวข้อนี้จะกล่าวถึงในการบรรยายครั้งต่อๆ ไป และความหมายที่ลึกซึ้งยิ่งขึ้นและตัวเลือกสำหรับการใช้อินเทอร์เฟซจะถูกเปิดเผย แต่ถ้าคุณไม่สบายใจเลยก็ยินดีต้อนรับ ตอนแรกฉันอยากจะเขียนความคิดเห็น แต่ฉันรู้ว่ามันจะเป็นความคิดเห็นที่ยาวมาก ดังนั้นเราจึงอยู่ที่นี่ นี่เป็นบทความแรกของฉัน ฉันยินดีรับคำวิจารณ์ที่สมเหตุสมผล หากฉันผิดตรงไหนโปรดแก้ไขฉันด้วย อย่างที่เราทราบ เมื่อ สร้างคลาสของเราเอง เราสามารถสืบทอดจากคลาส abstract หรือ non-abstract เพียง คลาส เดียว เท่านั้น แต่ในขณะเดียวกัน ในชั้นเรียนของเราเราสามารถใช้อินเทอร์เฟซจำนวน มากได้ เมื่อรับมรดกคุณจะต้องได้รับคำแนะนำจากสามัญสำนึกและยังคงเขียนเอนทิตีที่เกี่ยวข้อง ลงในลูกหลานของคุณ (เราจะไม่สืบทอดแมวจากรถปราบดินและในทางกลับกัน) ด้วยอินเทอร์เฟซ ตรรกะจะแตกต่างกันเล็กน้อย: สิ่งที่สำคัญ ที่นี่ ไม่ใช่ "เอนทิตี" เอง แต่เป็น"ทักษะ" ของพวกเขาหรือสิ่งที่สามารถทำได้ด้วยสิ่งเหล่านั้น ตัวอย่างเช่น ทั้งแมวและรถปราบดินสามารถเคลื่อนที่ในอวกาศได้ โดยหลักการแล้ว พวกเขาสามารถใช้อินเทอร์เฟซ CanMove หรือ Moveable ได้ หรือคนกับแมว พวกเขาทั้งสองสามารถดื่มได้ แต่ทำในรูปแบบที่แตกต่างกัน: คนดื่มชาหรือกาแฟจากถ้วย และแมวตักน้ำหรือนมจากชาม แต่โดยทั่วไปแล้ว พวกเขาทั้งคู่ดื่ม ดังนั้นแต่ละคนจึงสามารถใช้งานอินเทอร์เฟซ CanDrink ของตนเองได้ สิ่งนี้มีประโยชน์อะไรสำหรับเรา? ลองจินตนาการว่าคุณกำลังสร้างเกม สมมติว่าคุณมีสถานที่: แม่น้ำที่มีตลิ่งทั้งสองด้าน ตามด้วยป่าและภูเขา สิ่งมีชีวิตหลากหลายชนิดมาพักผ่อนบนชายฝั่ง จู่ๆ น้ำท่วมก็เข้ามาใกล้ ใครก็ตามที่บินได้ก็บินหนีไป ผู้ที่ไม่สามารถบินได้ แต่วิ่งได้ วิ่งได้ มีคนรู้วิธีว่ายน้ำ ดังนั้น โดยหลักการแล้วพวกเขาไม่สนใจน้ำท่วมของคุณ (หรือจะว่ายขึ้นฝั่งก็ได้) แม้ว่าบางคนอาจพยายามหลบหนีก่อน (ถ้ารู้) ที่เหลือเศร้าแค่ไหนก็ต้องตาย เรามาลองใช้สิ่งนี้กัน (อย่ากลัวกับโค้ดจำนวนมากในคราวเดียว =) ส่วนใหญ่เป็นความคิดเห็น) เราต้องการคลาสอะไร? เริ่มจาก คลาส นามธรรมของตัวละครหรือยูนิตต่างๆ กัน (ขออภัย ฉันไม่เก่งคำศัพท์เกี่ยวกับเกม โปรดแก้ไขหากฉันผิด) ให้นี่คือคลาสUnit เราจะถือว่าทุกหน่วยสามารถเคลื่อนที่ไปในอวกาศและส่งเสียงได้อย่างแน่นอน คลาสนามธรรมหน่วย :
// Абстрактный класс для всех юнитов
public static abstract class Unit {
    // Базовый метод движения.
    // По умолчанию (если не переопределено) будет у всех наследников.
    public void move (int x, int y) {
        System.out.println("Я ( " + getClassName() + " ) просто брожу по полю на " +
                           x + " метров вправо и " + y + " метров вперед");
    }

    // Абстрактный метод, который ДОЛЖЕН ПЕРЕОПРЕДЕЛИТЬ у себя
    // КАЖДЫЙ КЛАСС, унаследованный от Unit.
    public abstract void makeSound();

    // Вспомогательный метод получения имени класса
    // без всей лишней информации.
    public String getClassName() {
        return this.getClass().getSimpleName();
    }
}
หน่วยของเราต้องการอินเทอร์เฟซ ใด (“ทักษะ”)? พวกเขาสามารถวิ่ง ( CanRun ) ว่ายน้ำ ( CanSwim ) หรือบิน ( CanFly ) บางคนอาจมีทักษะหลายอย่างพร้อมกัน ในขณะที่คนที่โชคร้ายบางคนอาจไม่มีเลย
// Интерфейсы. КАЖДЫЙ КЛАСС, "наследующий" Howой-то интерфейс,
// ДОЛЖЕН РЕАЛИЗОВАТЬ его у себя.
interface CanRun {
    void run(String action);
}
interface CanSwim {
    void swim();
}
interface CanFly {
    void fly();
}
ต่อไปเราจะสร้างคลาสที่สืบทอดมาจากคลาส Unit เชิงนามธรรม เส้นทางจะเป็นคลาส Human :
// Человек НАСЛЕДУЕТ абстрактный класс Unit,
// а также РЕАЛИЗУЕТ интерфейсы CanRun, CanSwim
public static class Human extends Unit implements CanRun, CanSwim {
    // Переопределяем метод public void makeSound()
    // родительского абстрактного класса Unit
    @Override
    public void makeSound() {
        System.out.print("Йу-хуу! ");
    }

    // РЕАЛИЗУЕМ метод public void run(String action) интерфейса CanRun
    @Override
    public void run(String action) {
        System.out.println("Я ( " + this.getClassName() + " ) " + action +
                           " отсюда на двух ногах ");
    }

    // РЕАЛИЗУЕМ метод public void swim() интерфейса CanSwim
    @Override
    public void swim() {
        System.out.println("Я ( " + this.getClassName() + " ) " +
                           "с двумя руками и двумя ногами " +
                           "хорошо плаваю");
    }
}
คลาสนก . ใช่ ฉันเข้าใจว่าไม่มีนกธรรมดาๆ แต่เพื่อความเรียบง่าย ปล่อยให้มันเป็นเพียงนก อย่าทำให้มันเป็นนามธรรม:
// Птица НАСЛЕДУЕТ абстрактный класс Unit,
// и РЕАЛИЗУЕТ интерфейс CanFly
public static class Bird extends Unit implements CanFly {
    // Переопределяем абстрактный метод public void makeSound()
    // родительского абстрактного класса Unit
    @Override
    public void makeSound() {
        System.out.print("Курлык! ");
    }

    // РЕАЛИЗУЕМ метод public void fly() интерфейса CanFly
    @Override
    public void fly() {
        System.out.println("Я ( " + this.getClassName() + " ) улетел отсюда");
    }
}
ตอนนี้เรามาสร้าง คลาส นามธรรม อีก คลาส Animals ซึ่งจะสืบทอดมาจากUnit :
// Абстрактный класс Животных, НАСЛЕДУЕТ абстрактный класс Unit
public static abstract class Animal extends Unit {
    // тут могут быть Howие-то данные и/or методы
}
และทายาทของเขาแล้ว ลูกแกะ ( แกะ ):
// Баран НАСЛЕДУЕТ класс Animal,
// и РЕАЛИЗУЕТ интерфейс CanRun
public static class Sheep extends Animal implements CanRun {
    // Переопределяем абстрактный метод public void makeSound()
    // родительского абстрактного класса Unit
    @Override
    public void makeSound() {
        System.out.print("Беееее! ");
    }

    // РЕАЛИЗУЕМ метод public void run(String action) интерфейса CanRun
    @Override
    public void run(String action) {
        System.out.println("Я ( "+ this.getClassName() + " ) " + action +
                           " отсюда на четырёх копытах");
    }
}
และสลอธ ( สลอธ ):
// Ленивец НАСЛЕДУЕТ класс Animal
// и внутри себя ПЕРЕОПРЕДЕЛЯЕТ метод
// void move(int x, int y) абстрактного класса Unit
public static class Sloth extends Animal {
    // Переопределяем абстрактный метод public void makeSound()
    // родительского абстрактного класса Unit
    @Override
    public void makeSound() {
        System.out.print("Зевает... ");
    }

	// Переопределяем метод public void move(int x, int y)
    // родительского абстрактного класса Unit
    @Override
    public void move(int x, int y) {
        System.out.println("Я ( "+ getClassName() + " ) очень медленный, поэтому " +
                           "переместился на " + (int)(x/12) + " вправо " +
                           "и на " + (int)(y/12) + " вперед");
    }
}
เมธอด move(int x, int y) ของ คลาสabstract Unit ไม่ใช่ abstractดังนั้นเราจึงไม่จำเป็นต้องแทนที่มัน แต่เราได้ทำให้ Sloth ช้าลงเล็กน้อย ตอนนี้เรามาดูการดำเนินการกันดีกว่า
public static void main(String[] args) {
    // создаём список юнитов
    // и добавляем туда представителей разных классов
    List<Unit> units = new ArrayList<unit>();
    units.add(new Human());
    units.add(new Bird());
    units.add(new Sheep());
    units.add(new Sloth());

    // проходим в цикле по всем юнитам и вызываем метод move(int x, int y).
    // У всех, кроме ленивца, будет вызван метод базового класса.
    // У Ленивца будет вызван его переопределенный метод
    for (Unit unit : units) {
		// в самом начале ничего не происходит, юниты просто двигаются.
        unit.move((int)(Math.random()*50), (int)(Math.random()*50));
    }

    System.out.println("\n...Наводнение приближается....");
    int distanceOfFlood = (int)(Math.random()*1000); // Число от 0 до 1000
    System.out.println("...Наводнение на " + distanceOfFlood + " от берега...\n");

    // проходим в цикле по всем юнитам
    for (Unit unit : units) {
        unit.makeSound(); // у каждого юнита свой, переопределенный, звук
        // смотрим, кто что "умеет":
		// если юнит умеет летать
        if(unit instanceof CanFly)
            ((CanFly) unit).fly(); // проблем нет, улетает
        // если юнит не умеет летать, но умеет бегать
        else if(unit instanceof CanRun) {
			// смотрим на Howое расстояние разлилась вода
            if(distanceOfFlood < 400) {
				// если меньше 400 (условно метров)
                ((CanRun) unit).run("убежал"); // юнит убежал
            }
            else {
				// если больше 400, юнит безуспешно пытается убежать
                ((CanRun) unit).run("пытался убежать");
				// если юнит может не только бегать, но и плавать
                if (unit instanceof CanSwim) {
                    ((CanSwim) unit).swim(); // плывёт
                }
				// иначе умирает (он хотя бы пытался)
                else unitIsDead(unit);
            }
        }
		// если юнит не летает, не бегает, но может плавать
        else if (unit instanceof CanSwim)
            ((CanSwim) unit).swim(); // плывёт
        else
			// если юнит не умеет совсем ничего - грустненько :(
            unitIsDead(unit); // умирает

        System.out.println();
    }
}

public static void unitIsDead(Unit unit) {
    System.out.println("Извините, я ( " + unit.getClassName() + " ) умер");
}
ตัวอักษรตัวเลข 12, 50, 400 และ 1,000 ถูกตัดออกไป ระบุค่าอื่นได้ แต่ฉันหวังว่าตรรกะจะชัดเจน ดังที่เราเห็นแล้ว การมีคลาสพาเรนต์ที่เป็นนามธรรมพร้อมด้วยเมธอดทั่วไป เราไม่จำเป็นต้องคิดว่าคลาสนี้หรือยูนิตนั้นเป็นคลาสใด แต่เพียงแค่เรียกเมธอดเหล่านี้ ( makeSound() และmove ( ) ) หลังจากผ่านครั้งแรกในลูป เมื่อ เรียกใช้เมธอดmove() ในทุกยูนิต สิ่งต่อไปนี้จะปรากฏบนหน้าจอ: อินเทอร์เฟซสำหรับผู้ที่ "สนใจมาก แต่ไม่เข้าใจอะไรเลย" - 1 แน่นอนว่าออบเจ็กต์ทั้งหมดยกเว้นสลอธจะแสดงข้อความมาตรฐานจากเมธอดmove()ของ abstract parent class Unitและความเฉื่อยชามีการเคลื่อนไหว (วิธีการ)ถูกกำหนดใหม่ อย่างไรก็ตาม คลาสนามธรรมจะไม่ช่วยให้เราค้นหาว่าหน่วยใด "สามารถทำอะไรได้บ้าง" นี่คือจุดที่อินเทอร์เฟซ เข้ามามี บทบาท เมื่อใช้instanceofเราจะค้นหาว่าหน่วยนี้สามารถดำเนินการบางอย่างได้หรือไม่ ( รองรับอินเทอร์เฟซที่เราต้องการหรือไม่ ) และหากเป็นเช่นนั้น เราใช้การหล่อประเภทที่เราคุ้นเคยอยู่แล้ว เช่น การใช้หน่วย ((CanFly)) fly()เราแปลง อ็อบเจกต์ประเภท Unit ของเรา เป็น ประเภท อิน เทอร์เฟซ CanFlyและเรียกใช้เมธอด fly() และไม่สำคัญว่าวัตถุของเราจะมีคลาสอะไร สิ่งสำคัญเพียงอย่างเดียวคือเขาระบุความสามารถในการบินใน "เรซูเม่" ของเขา เราบอกเขาว่า: “คุณรู้ได้ยังไง บินไปเลย! เราไม่สนใจว่าคุณจะทำอย่างไร ” นั่นคือสำหรับเราในฐานะนักพัฒนา นั่นหมายความว่าคลาสที่ใช้ อินเทอร์เฟซ CanFly สามารถเปลี่ยนการใช้งานวิธี fly()ได้ตลอดเวลา และในทางใดทางหนึ่งคลาสใหม่ที่ใช้อินเทอร์เฟซนั้นสามารถปรากฏขึ้นได้ หรือในทางกลับกัน นักพัฒนาสามารถทำได้ ลบคลาสเก่าบางส่วนออก แต่ตราบใดที่วิธีนี้ยังทำหน้าที่ตามที่ระบุไว้และวัตถุนั้นบินไป เราก็ไม่สนใจ โค้ดของเราที่ทำงานกับออบเจ็กต์ที่ใช้อินเทอร์เฟซนี้จะยังคงเหมือนเดิม โดยเราไม่ต้องเปลี่ยนแปลงอะไรเลย หลังจากรอบที่สอง เมื่อทุกคนพยายามหลบหนี ขึ้นอยู่กับขนาดน้ำท่วม เราจะเห็นบนหน้าจออย่างใดอย่างหนึ่ง อินเทอร์เฟซสำหรับผู้ที่ "สนใจมาก แต่ไม่เข้าใจอะไรเลย" - 2 หรืออินเทอร์เฟซสำหรับผู้ที่ "สนใจมาก แต่ไม่เข้าใจอะไรเลย" - 3 หากไม่มีอินเทอร์เฟซ เราจะต้องตรวจสอบแต่ละวัตถุว่าสอดคล้องกับคลาสใดคลาสหนึ่ง (และเราจะได้ เพื่อตรวจสอบทุกอย่าง) และคำนึงถึงทักษะของแต่ละคลาสโดยเฉพาะ นั่นคือมีลูกแกะอยู่ข้างหน้าเราแล้วและดูเหมือนว่าจะวิ่งได้ จะเกิดอะไรขึ้นถ้าคุณมีตัวละคร (คลาส) ประเภทต่างๆ หลายสิบหรือหลายร้อยตัว? แล้วถ้าไม่ใช่คุณที่เป็นคนเขียนมัน แต่เป็นโปรแกรมเมอร์อีกคน คุณเลยไม่รู้ว่าใครสามารถทำอะไรได้บ้าง? มันจะยากกว่านี้มาก... และเพิ่มเติมเล็กน้อยหลังจากการตีพิมพ์: ในชีวิตจริง คุณไม่ใช่คนเดียวที่ทำงานในโครงการนี้ สมมติว่าคุณทำแต่ตรรกะเท่านั้น วัตถุทั้งหมดที่คุณโต้ตอบด้วยถูกเขียนโดยโปรแกรมเมอร์คนอื่น คุณอาจไม่รู้ด้วยซ้ำว่าโค้ดของคุณใช้งานได้กับคลาสทั้งหมด สิ่งที่คุณต้องการจากพวกเขาก็คือพวกเขาทำในสิ่งที่คุณต้องการให้ทำ อย่างไรก็ตาม พวกเขาทั้งหมดสามารถทำได้ด้วยวิธีที่ต่างกันโดยสิ้นเชิง แต่สมมุติว่าในโค้ดของคุณ คุณสร้างวิธีการที่ใช้ได้กับอ็อบเจ็กต์ของคลาสที่รองรับอินเทอร์เฟซบางอย่างเท่านั้น
void doSomething(CanFly f)
นั่นคือคุณตั้งค่าอินเทอร์เฟซโดยใช้พารามิเตอร์วิธีการ ไม่ใช่คลาสที่เป็นรูปธรรม แต่เป็นอินเทอร์เฟซ และโปรแกรมเมอร์คนอื่นๆ ทั้งหมด เมื่อเรียกเมธอด void doSomething(CanFly) ของคุณ จะต้องส่งผ่านเป็นอาร์กิวเมนต์ ไม่ว่าจะเป็นอ็อบเจ็กต์ที่ชัดเจนของคลาสที่ใช้ CanFly หรืออ็อบเจ็กต์บางส่วนของคลาสบางคลาสที่สามารถนำมาใช้ได้:
Object obj = new Bird();
doSomething(obj); // ошибка компиляции Object cannot be converted to CanFly
doSomething((CanFly) obj); // нет ошибки, потому что obj у нас класса Bird и реализует CanFly

Bird b = new Bird();
doSomething(b); // нет ошибки

Human h = new Human() ;
doSomething(h); // ошибка компиляции
doSomething((CanFly) h); // ошибка времени выполнения ClassCastException
นี่คือวิธีที่อินเทอร์เฟซมีประโยชน์ และนี่ไม่ใช่ความสามารถและวิธีการใช้งานทั้งหมด เราอาจจะได้เรียนรู้เพิ่มเติมในภายหลังในหลักสูตร =) ขอบคุณที่อ่านจนจบ =)
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION