CodeGym /จาวาบล็อก /สุ่ม /ทำไมเราต้องการส่วนต่อประสานใน Java
John Squirrels
ระดับ
San Francisco

ทำไมเราต้องการส่วนต่อประสานใน Java

เผยแพร่ในกลุ่ม
สวัสดี! วันนี้เราจะพูดถึงแนวคิดที่สำคัญใน Java: อินเทอร์เฟซ คำนี้น่าจะคุ้นหูกันดี ตัวอย่างเช่น โปรแกรมคอมพิวเตอร์และเกมส่วนใหญ่มีอินเทอร์เฟซ ในความหมายกว้างๆ อินเทอร์เฟซคือ 'รีโมตคอนโทรล' ประเภทหนึ่งที่เชื่อมต่อสองฝ่ายที่มีปฏิสัมพันธ์ ตัวอย่างง่ายๆ ของอินเทอร์เฟซในชีวิตประจำวันคือรีโมทคอนโทรลของทีวี โดยจะเชื่อมต่อวัตถุสองชิ้น — คนกับทีวี — และทำงานต่างๆ กัน: เพิ่มหรือลดระดับเสียง สลับช่อง และเปิดหรือปิดทีวี ฝ่ายหนึ่ง (บุคคลนั้น) ต้องเข้าถึงอินเทอร์เฟซ (กดปุ่มบนรีโมทคอนโทรล) เพื่อให้ฝ่ายที่สองดำเนินการ ตัวอย่างเช่น เพื่อให้ทีวีเปลี่ยนเป็นช่องถัดไป ยิ่งไปกว่านั้น ผู้ใช้ไม่ ไม่จำเป็นต้องรู้ว่ามีการจัดระเบียบทีวีอย่างไรหรือกระบวนการเปลี่ยนช่องใช้งานภายในอย่างไร สิ่งเดียวที่ผู้ใช้สามารถเข้าถึงได้คืออินเทอร์เฟซ วัตถุประสงค์หลักเพื่อให้ได้ผลลัพธ์ที่ต้องการ สิ่งนี้เกี่ยวข้องกับการเขียนโปรแกรมและ Java หรือไม่ ทุกอย่าง :) การสร้างส่วนต่อประสานนั้นคล้ายกับการสร้างคลาสปกติ แต่ใช้คำแทนclassเราระบุส่วนต่อประสานคำ มาดูอินเทอร์เฟซ Java ที่ง่ายที่สุด ดูว่ามันทำงานอย่างไร และเหตุใดเราจึงต้องใช้:

public interface CanSwim {

     public void swim();
}
เราได้สร้างอินเทอร์เฟซ CanSwim มันคล้ายกับรีโมตคอนโทรลของเรา แต่มี 'ปุ่ม' เพียงปุ่มเดียว: วิธีการว่ายน้ำ () แต่เราจะใช้งานรีโมทคอนโทรลนี้ได้อย่างไร? ในการทำเช่นนี้ เราจำเป็นต้องใช้วิธีการ เช่น ปุ่มควบคุมระยะไกลของเรา ในการใช้อินเทอร์เฟซ บางคลาสในโปรแกรมของเราต้องใช้เมธอดของมัน มาสร้างชั้นเรียนที่มีวัตถุ 'ว่ายน้ำได้' กันเถอะ ตัวอย่างเช่น คลาส Duckเหมาะกับ:

public class Duck implements CanSwim {

    public void swim() {
        System.out.println("Duck, swim!");
    }

    public static void main(String[] args) {

        Duck duck = new Duck();
        duck.swim();
    }
}
"เราเห็นอะไรที่นี่ คลาสDuckนั้น 'เชื่อมโยง' กับอินเตอร์เฟสCanSwim โดยใช้ คีย์เวิร์ด Implementคุณอาจจำได้ว่าเราใช้กลไกที่คล้ายกันในการเชื่อมโยงสองคลาสผ่านการสืบทอด แต่ในกรณีนั้น เราใช้คำขยาย สำหรับ เราสามารถแปลคำว่า ' public class Duck Implement CanSwim ' ได้อย่างแท้จริงว่า: ' The public Duck class Impulse the CanSwim interface ' ซึ่งหมายความว่าคลาสที่เกี่ยวข้องกับอินเตอร์เฟสจะต้องใช้เมธอดทั้งหมดของมัน หมายเหตุ: Duckคลาสของเราก็เหมือนกับ อินเตอร์เฟสCanSwimมีswim()เมธอด และมีลอจิก นี่เป็นข้อกำหนดบังคับ ถ้าเราแค่เขียนpublic class Duck implements CanSwimโดยไม่ต้องสร้างswim()เมธอดในDuckคลาส คอมไพเลอร์จะให้ข้อผิดพลาดแก่เรา: Duck is not abstract and does not override abstract method swim() in CanSwim Why? ทำไมสิ่งนี้ถึงเกิดขึ้น? หากเราอธิบายข้อผิดพลาดโดยใช้ตัวอย่างทีวี ก็จะเหมือนกับการมอบรีโมตคอนโทรลทีวีที่มีปุ่ม 'เปลี่ยนช่อง' ให้กับใครบางคนซึ่งไม่สามารถเปลี่ยนช่องได้ คุณสามารถกดปุ่มได้มากเท่าที่คุณต้องการ แต่จะไม่ทำงาน รีโมตคอนโทรลไม่เปลี่ยนช่องด้วยตัวเอง แต่จะส่งสัญญาณไปยังทีวีเท่านั้น ซึ่งใช้กระบวนการที่ซับซ้อนในการเปลี่ยนช่อง เป็ดของเราก็เช่นกัน มันต้องรู้วิธีว่ายน้ำจึงจะเรียกว่าใช้CanSwimอินเทอร์เฟซได้ หากไม่ทราบวิธีการCanSwimอินเทอร์เฟซไม่เชื่อมต่อสองฝ่าย — บุคคลและโปรแกรม บุคคลนั้นจะไม่สามารถใช้swim()วิธีการว่ายDuckน้ำภายในโปรแกรมได้ ตอนนี้คุณเข้าใจชัดเจนมากขึ้นว่าอินเทอร์เฟซมีไว้เพื่ออะไร อินเทอร์เฟซอธิบายพฤติกรรมที่คลาสที่ใช้อินเทอร์เฟซต้องมี 'พฤติกรรม' เป็นชุดของวิธีการ หากเราต้องการสร้างผู้ส่งสารหลายๆ คน วิธีที่ง่ายที่สุดคือสร้างMessengerอินเทอร์เฟซ ผู้ส่งสารทุกคนต้องการอะไร? ในระดับพื้นฐานจะต้องสามารถรับและส่งข้อความได้

public interface Messenger{

     public void sendMessage();

     public void getMessage();
}
ตอนนี้เราสามารถสร้างคลาส Messenger ของเราที่ใช้อินเทอร์เฟซที่เกี่ยวข้องได้ ตัวคอมไพเลอร์จะ 'บังคับ' ให้เรานำไปใช้ในชั้นเรียนของเรา โทรเลข:

public class Telegram implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a Telegram message!");
    }

     public void getMessage() {
         System.out.println("Receiving a Telegram message!");
     }
}
วอทส์แอพ:

public class WhatsApp implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a WhatsApp message!");
    }

     public void getMessage() {
         System.out.println("Reading a WhatsApp message!");
     }
}
ไวเบอร์:

public class Viber implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a Viber message!");
    }

     public void getMessage() {
         System.out.println("Receiving a Viber message!");
     }
}
สิ่งนี้ให้ข้อดีอะไรบ้าง? สิ่งที่สำคัญที่สุดคือข้อต่อแบบหลวม ลองจินตนาการว่าเรากำลังออกแบบโปรแกรมที่จะรวบรวมข้อมูลลูกค้า ชั้นClientเรียนต้องการฟิลด์เพื่อระบุว่าผู้ส่งสารใดที่ลูกค้าใช้อยู่ หากไม่มีอินเทอร์เฟซ สิ่งนี้จะดูแปลก:

public class Client {

    private WhatsApp whatsApp;
    private Telegram telegram;
    private Viber viber;
}
เราสร้างฟิลด์สามฟิลด์ แต่ไคลเอ็นต์สามารถมีแมสเซนเจอร์ได้เพียงหนึ่งฟิลด์ เราแค่ไม่รู้ว่าอันไหน ดังนั้นเราจึงต้องเพิ่มทุกความเป็นไปได้ให้กับชั้นเรียนเพื่อให้สามารถสื่อสารกับลูกค้าได้ ปรากฎว่าหนึ่งหรือสองคนจะเป็นnullโปรแกรมที่ไม่ต้องการโดยสิ้นเชิง ควรใช้อินเทอร์เฟซของเราแทน:

public class Client {

    private Messenger messenger;
}
นี่คือตัวอย่างของข้อต่อหลวม! แทนที่จะระบุคลาสผู้ส่งสารเฉพาะในClientชั้นเรียน เราเพียงระบุว่าลูกค้ามีผู้รับสาร อันไหนจะถูกกำหนดในขณะที่โปรแกรมทำงาน แต่ทำไมเราถึงต้องการอินเทอร์เฟซสำหรับสิ่งนี้ ทำไมพวกเขาถึงถูกเพิ่มเข้าไปในภาษา? นั่นเป็นคำถามที่ดี — และเป็นคำถามที่ถูกต้อง! เราไม่สามารถบรรลุผลลัพธ์เดียวกันโดยใช้การสืบทอดแบบธรรมดาได้หรือ? ชั้นMessengerเรียนในฐานะผู้ปกครอง และViber, Telegram, และWhatsAppในฐานะเด็ก แน่นอนว่าเป็นไปได้ แต่มีอุปสรรค์อย่างหนึ่ง ดังที่คุณทราบแล้ว Java ไม่มีการสืบทอดหลายรายการ แต่มีการสนับสนุนหลายอินเทอร์เฟซ คลาสสามารถใช้อินเทอร์เฟซได้มากเท่าที่คุณต้องการ ลองนึกภาพว่าเรามีSmartphoneชั้นเรียนที่มีหนึ่งAppช่องซึ่งแสดงถึงแอพที่ติดตั้งบนสมาร์ทโฟน

public class Smartphone {

    private App app;
}
แน่นอนว่าแอปและโปรแกรมส่งข้อความนั้นคล้ายกัน แต่ก็ยังมีความแตกต่างกันอยู่ อาจมี Messenger เวอร์ชันมือถือและเดสก์ท็อป แต่แอพแสดงถึงแอพมือถือโดยเฉพาะ นี่คือข้อตกลง ถ้าเราใช้การสืบทอด เราจะไม่สามารถเพิ่มวัตถุTelegramในSmartphoneชั้นเรียนได้ ท้ายที่สุดTelegramคลาสไม่สามารถสืบทอดพร้อมกันAppและMessenger! และเราได้ทำให้มันสืบทอดMessengerและเพิ่มเข้าไปในClientคลาสแล้ว แต่Telegramชั้นเรียนสามารถใช้ทั้งสองอินเทอร์เฟซได้อย่างง่ายดาย! ดังนั้น เราสามารถให้Clientคลาสเป็นTelegramออบเจกต์เป็น a Messengerและเราสามารถให้คลาสSmartphoneเป็นออบเจกต์Appได้ นี่คือวิธีที่คุณทำ:

public class Telegram implements Application, Messenger {

    // ...methods
}

public class Client {

    private Messenger messenger;

    public Client() {
        this.messenger = new Telegram();
    }
}


public class Smartphone {

    private Application application;

    public Smartphone() {
        this.application = new Telegram();
    }
}
ตอนนี้เรากำลังใช้Telegramชั้นเรียนตามที่เราต้องการ ในบางแห่งจะทำหน้าที่เป็นไฟล์App. ในที่อื่น ๆ จะทำหน้าที่เป็นMessenger. คุณคงสังเกตเห็นแล้วว่าเมธอดของอินเทอร์เฟซนั้น 'ว่างเปล่า' เสมอ นั่นคือไม่มีการใช้งาน เหตุผลนี้ง่ายมาก: อินเทอร์เฟซอธิบายพฤติกรรม แต่ไม่ได้นำไปใช้ 'วัตถุทั้งหมดที่ใช้CanSwimอินเทอร์เฟซต้องว่ายน้ำได้' นั่นคือทั้งหมดที่อินเทอร์เฟซบอกเรา วิธีเฉพาะที่ปลา เป็ด และม้าว่ายน้ำเป็นคำถามสำหรับFish, Duck, และHorseคลาสไม่ใช่อินเทอร์เฟซ เช่นเดียวกับการเปลี่ยนช่องเป็นงานสำหรับทีวี รีโมทจะให้ปุ่มนี้แก่คุณ อย่างไรก็ตาม การเพิ่มที่น่าสนใจปรากฏใน Java 8 — วิธีการเริ่มต้น ตัวอย่างเช่น อินเทอร์เฟซของคุณมี 10 วิธี 9 รายการมีการใช้งานที่แตกต่างกันในคลาสต่างๆ แต่มีการใช้งานเหมือนกันสำหรับทุกคน ก่อนหน้านี้ ก่อน Java 8 เมธอดอินเตอร์เฟสไม่มีการนำไปใช้งานใดๆ เลย: คอมไพเลอร์แสดงข้อผิดพลาดทันที ตอนนี้คุณสามารถทำสิ่งนี้:

public interface CanSwim {

   public default void swim() {
       System.out.println("Swim!");
   }

   public void eat();

   public void run();
}
ด้วยการใช้defaultคำหลัก เราได้สร้างวิธีการเชื่อมต่อด้วยการใช้งานเริ่มต้น เราจำเป็นต้องจัดให้มีการใช้ งานของเราเองสำหรับอีกสองวิธี — eat()และrun()— ในคลาสทั้งหมดที่ใช้ CanSwimเราไม่จำเป็นต้องทำเช่นนี้กับเมธอดswim(): การนำไปใช้จะเหมือนกันในทุกคลาส อย่างไรก็ตาม คุณได้พบอินเทอร์เฟซในงานที่ผ่านมาแล้ว แม้ว่าคุณจะไม่ได้สังเกตก็ตาม :) นี่คือตัวอย่างที่ชัดเจน: เหตุใดอินเทอร์เฟซจึงจำเป็นใน Java - 2คุณเคยทำงานกับ อินเทอร์เฟซ ListและSet! แม่นยำยิ่งขึ้น คุณได้ทำงานกับการใช้งานของพวกเขา — ArrayList, LinkedList, HashSet, ฯลฯ แผนภาพเดียวกันแสดงให้เห็นอย่างชัดเจนว่าคลาสหนึ่งใช้อินเทอร์เฟซหลายรายการพร้อมกัน ตัวอย่างเช่นLinkedListใช้ListandDeque(คิวปลายคู่) อินเตอร์เฟส คุณคุ้นเคยกับMapอินเทอร์เฟซหรือคุ้นเคยกับHashMapการใช้งาน อย่างไรก็ตาม ไดอะแกรมนี้แสดงคุณลักษณะ: อินเทอร์เฟซสามารถสืบทอดอินเทอร์เฟซอื่นได้ อิน เท อSortedMapร์เฟซสืบทอดMapในขณะที่Dequeสืบทอด Queueนี่เป็นสิ่งจำเป็นหากคุณต้องการแสดงความสัมพันธ์ระหว่างอินเทอร์เฟซ โดยที่อินเทอร์เฟซหนึ่งเป็นเวอร์ชันขยายของอีกอินเทอร์เฟซหนึ่ง ลองพิจารณาตัวอย่างด้วยQueueอินเทอร์เฟซ เรายังไม่ได้ตรวจสอบQueuesแต่มันค่อนข้างเรียบง่ายและทำงานเหมือนคิวหรือแถวทั่วไปที่ร้านค้า คุณสามารถเพิ่มรายการที่ส่วนท้ายของคิวเท่านั้น และรับได้ตั้งแต่เริ่มต้นเท่านั้น ในบางจุด นักพัฒนาต้องการคิวเวอร์ชันปรับปรุงเพื่อเพิ่มและรับรายการที่ปลายทั้งสองด้าน ดังนั้นพวกเขาจึงสร้างDequeส่วนต่อประสานซึ่งเป็นคิวสองด้าน มันมีวิธีการทั้งหมดของคิวธรรมดา ท้ายที่สุดมันเป็นพาเรนต์ของคิวแบบ double-end แต่ก็ยังเพิ่มวิธีการใหม่
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION