JavaRush /Blog Java /Random-VI /Giao diện dành cho những người “rất quan tâm nhưng không ...
Lilly
Mức độ
Москва

Giao diện dành cho những người “rất quan tâm nhưng không hiểu gì”

Xuất bản trong nhóm
Chào mọi người! Đúng hơn, bài đăng này được viết dành cho những người đã xem và thậm chí đã giải quyết một số vấn đề về Giao diện ở cấp 12 (cấp 2 của Java Core), nhưng vẫn thắc mắc: “Vậy tại sao chúng lại cần thiết nếu bạn vẫn cần triển khai các phương thức trong đó ?” được khai báo trong giao diện??? Tức là họ không giảm số lượng mã!!!" . Tôi tin chắc rằng chủ đề này sẽ được đề cập trong các bài giảng tiếp theo, đồng thời những ý nghĩa sâu sắc hơn cũng như các lựa chọn sử dụng giao diện sẽ được tiết lộ, nhưng nếu bạn hoàn toàn không yên tâm thì không sao cả. Ban đầu tôi muốn viết bình luận, nhưng tôi nhận ra rằng đó sẽ là một bình luận rất dài, vì vậy chúng ta ở đây. Đây là bài viết đầu tiên của tôi, tôi hoan nghênh những lời chỉ trích hợp lý. Nếu tôi sai ở đâu đó, hãy sửa tôi. Vì vậy, như chúng ta đã biết, khi tạo lớp của riêng mình, chúng ta chỉ có thể kế thừa từ một lớp trừu tượng hoặc không trừu tượng . Nhưng đồng thời, trong lớp của chúng ta, chúng ta có thể triển khai một số lượng giao diện khá lớn . Khi kế thừa, bạn cần được hướng dẫn bằng lẽ thường và vẫn ghi chép các thực thể liên quan vào con cháu của mình (chúng ta sẽ không thừa kế một con mèo từ máy ủi và ngược lại). Với giao diện , logic hơi khác một chút: điều quan trọng ở đây không phải là bản thân các “thực thể”, mà là “kỹ năng” của chúng hoặc những gì có thể làm với chúng . Ví dụ, cả một con mèo và một chiếc máy ủi đều có thể di chuyển trong không gian. Tức là về nguyên tắc, họ có thể triển khai giao diện CanMove hoặc Moveable. Hoặc một người đàn ông và một con mèo. Cả hai đều có thể uống, nhưng họ làm theo những cách khác nhau: một người uống trà hoặc cà phê từ cốc, và một con mèo uống một ít nước hoặc sữa từ bát. Nhưng nói chung, cả hai đều uống rượu, vì vậy mỗi người trong số họ có thể tự triển khai giao diện CanDrink. Điều này mang lại lợi ích gì cho chúng ta? Hãy tưởng tượng rằng bạn đang làm một trò chơi. Giả sử bạn có một vị trí: một con sông, có bờ ở hai bên, sau đó là rừng và núi. Nhiều loại sinh vật sống nghỉ ngơi trên bờ. Đột nhiên một cơn lũ ập đến. Bất cứ ai có thể bay đều bay đi. Những người không thể bay, nhưng có thể chạy, hãy chạy. Ai đó biết bơi, vì vậy về nguyên tắc họ không quan tâm đến lũ lụt của bạn (tốt, hoặc họ có thể bơi vào bờ), mặc dù trước tiên một số người trong số họ có thể cố gắng trốn thoát (nếu họ biết cách). Những người còn lại, dù buồn đến mấy, cũng sẽ chết. Hãy thử thực hiện điều này. (Đừng sợ hãi bởi số lượng lớn mã cùng một lúc =) Hầu hết trong số đó là nhận xét) Chúng ta có thể cần những lớp nào? Hãy bắt đầu với một lớp trừu tượng gồm các ký tự hoặc đơn vị khác nhau (xin lỗi, tôi không rành về thuật ngữ chơi game, hãy sửa tôi nếu tôi sai). Hãy để đây là lớp Đơn vị . Chúng tôi sẽ cho rằng hoàn toàn tất cả các đơn vị có thể di chuyển trong không gian và tạo ra âm thanh. Lớp trừu tượng
// Абстрактный класс для всех юнитов
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();
    }
}
Đơn vị của chúng tôi cần những giao diện (“kỹ năng”) nào ? Chúng có thể chạy ( CanRun ), bơi ( CanSwim ) hoặc bay ( CanFly ). Một số người có thể có nhiều kỹ năng cùng một lúc, trong khi một số người không may mắn có thể không có.
// Интерфейсы. КАЖДЫЙ КЛАСС, "наследующий" Howой-то интерфейс,
// ДОЛЖЕН РЕАЛИЗОВАТЬ его у себя.
interface CanRun {
    void run(String action);
}
interface CanSwim {
    void swim();
}
interface CanFly {
    void fly();
}
Tiếp theo chúng ta tạo các lớp kế thừa từ lớp Unit trừu tượng. Đường dẫn sẽ là lớp 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() + " ) " +
                           "с двумя руками и двумя ногами " +
                           "хорошо плаваю");
    }
}
Lớp chim . Vâng, tôi hiểu rằng không có con chim nào đơn giản, nhưng để đơn giản, hãy chỉ là một con chim, đừng làm nó trừu tượng:
// Птица НАСЛЕДУЕТ абстрактный класс 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() + " ) улетел отсюда");
    }
}
Bây giờ hãy tạo một lớp trừu tượng khác Animals , lớp này sẽ được kế thừa từ Unit :
// Абстрактный класс Животных, НАСЛЕДУЕТ абстрактный класс Unit
public static abstract class Animal extends Unit {
    // тут могут быть Howие-то данные и/or методы
}
Và đã là người thừa kế của anh ấy Chiên Con ( Cừu ):
// Баран НАСЛЕДУЕТ класс 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 +
                           " отсюда на четырёх копытах");
    }
}
và Lười ( Sloth ):
// Ленивец НАСЛЕДУЕТ класс 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) + " вперед");
    }
}
Phương thức move(int x, int y) của lớp Unit trừu tượng không phải là abstract , vì vậy chúng ta không cần phải ghi đè lên nó, nhưng chúng ta đã làm mọi thứ chậm lại một chút đối với Sloth. Bây giờ chúng ta hãy chuyển sang hành động.
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() + " ) умер");
}
Các chữ số 12, 50, 400 và 1000 được lấy ngay; những chữ khác có thể được chỉ định, nhưng tôi hy vọng logic rõ ràng. Vì vậy, như chúng ta có thể thấy, có một lớp cha trừu tượng với các phương thức chung, chúng ta không cần phải suy nghĩ xem đơn vị này hoặc đơn vị đó là lớp cụ thể nào, mà chỉ cần gọi các phương thức này ( makeSound() và move ( ) ) . Sau lần chuyển đầu tiên trong vòng lặp, khi phương thức move() được gọi trên tất cả các đơn vị , thông tin sau sẽ được hiển thị trên màn hình: Giao diện dành cho những người “rất thích nhưng chẳng hiểu gì” - 1 Rõ ràng, tất cả các đối tượng ngoại trừ con lười đều hiển thị một thông báo tiêu chuẩn từ phương thức move() của cha mẹ trừu tượng Đơn vị lớp và con lười có một phương thức (phương thức) đã được xác định lại . Tuy nhiên, một lớp trừu tượng sẽ không giúp chúng ta tìm ra một đơn vị cụ thể “có thể làm gì”. Đây là nơi các giao diện phát huy tác dụng . Bằng cách sử dụng instanceof , chúng tôi tìm hiểu xem đơn vị này có thể thực hiện một số hành động nhất định hay không ( liệu nó có hỗ trợ giao diện mà chúng tôi cần hay không ) và nếu vậy, chúng tôi sử dụng kiểu truyền đã quen thuộc với chúng tôi, ví dụ: sử dụng đơn vị ((CanFly)). fly(), chúng tôi chuyển đối tượng loại Đơn vị của mình sang loại giao diện CanFly và gọi phương thức fly() của nó . Và không quan trọng đối tượng của chúng ta thuộc hạng nào, điều quan trọng duy nhất là anh ta đã chỉ ra khả năng bay trong “sơ yếu lý lịch” của mình. Chúng tôi nói với anh ấy: "Bạn biết cách, hãy bay đi! Chúng tôi không quan tâm bạn làm như thế nào . " Nghĩa là, đối với chúng tôi, với tư cách là nhà phát triển, điều này có nghĩa là các lớp triển khai giao diện CanFly có thể thay đổi cách triển khai phương thức fly() bất kỳ lúc nào và theo bất kỳ cách nào , các lớp mới triển khai nó có thể xuất hiện hoặc ngược lại, các nhà phát triển có thể loại bỏ một số lớp cũ. Nhưng miễn là phương pháp này thực hiện các chức năng đã nêu và vật thể bay, chúng tôi không quan tâm. Mã của chúng tôi hoạt động với các đối tượng triển khai giao diện này sẽ vẫn giữ nguyên; chúng tôi sẽ không phải thay đổi bất cứ điều gì. Sau chu kỳ thứ hai, khi mọi người đang cố gắng trốn thoát, tùy thuộc vào quy mô của lũ lụt, chúng ta sẽ thấy trên màn hình hoặc Giao diện dành cho những người “rất thích nhưng chẳng hiểu gì” - 2 Không Giao diện dành cho những người “rất thích nhưng chẳng hiểu gì” - 3 có giao diện, chúng ta sẽ phải kiểm tra từng đối tượng xem có tuân thủ một lớp nhất định không (và chúng ta sẽ có để kiểm tra mọi thứ) và ghi nhớ kỹ năng của từng lớp cụ thể. Tức là bây giờ có một con cừu non đang ở trước mặt chúng ta và nó dường như biết chạy. Điều gì sẽ xảy ra nếu bạn có hàng chục hoặc hàng trăm loại (lớp) ký tự khác nhau như vậy? Và điều gì sẽ xảy ra nếu người viết chúng không phải là bạn mà là một lập trình viên khác, nên bạn không biết ai có thể làm được điều gì? Nó sẽ khó khăn hơn nhiều... Và một bổ sung nhỏ sau khi xuất bản: Trong cuộc sống thực, bạn không phải là người duy nhất làm việc trong một dự án. Giả sử bạn chỉ làm logic. Tất cả các đối tượng bạn tương tác đều được viết bởi các lập trình viên khác. Bạn thậm chí có thể không biết tất cả các lớp mà mã của bạn hoạt động. Tất cả những gì bạn cần ở họ là họ làm những gì bạn cần họ làm. Tuy nhiên, tất cả họ đều có thể làm điều này theo những cách hoàn toàn khác nhau. Tuy nhiên, giả sử, trong mã của bạn, bạn tạo một phương thức chỉ hoạt động với các đối tượng của các lớp hỗ trợ một giao diện nhất định
void doSomething(CanFly f)
nghĩa là bạn đặt giao diện bằng tham số phương thức. Không phải là một lớp cụ thể, mà là một giao diện. Và tất cả các lập trình viên khác, khi gọi phương thức void doSomething(CanFly) này của bạn, phải chuyển làm đối số hoặc một đối tượng rõ ràng của một lớp triển khai CanFly hoặc một số đối tượng của một số lớp có thể được truyền tới nó:
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
Đây là cách giao diện có thể hữu ích. Và đây không phải là tất cả khả năng và phương pháp ứng dụng của họ. Có lẽ chúng ta sẽ tìm hiểu thêm sau trong khóa học =) Cảm ơn bạn đã đọc đến cuối =)
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION