Separation of Concerns
with Interfaces

homearticlesbooksshowcasequiz
By PradubKeng
  เกริ่นนำ
หลังจากที่เราได้คุยกันถึงภาษาจาว่าและเทคโนโลยีที่เกี่ยวข้องมาพอสมควร วันนี้เรามาเปลี่ยนบรรยากาศคุยกันในเรื่องพื้น ๆ ที่เป็นหัวใจสำคัญอย่างหนึ่งของการออกแบบระบบในภาษาจาว่ากันบ้าง หลายคนในช่วงเวลาว่าง อาจจะเคยลองนั่งอ่านซ๊อดโค๊ดของโปรแกรมพวก open source ใหญ่ ๆ เช่น Apache Tomcat, Xalan หรือ JBoss ส่ิงหนึ่งที่มักจะพบเห็นได้ชัดจากโปรแกรมเหล่านี้คือมีการใช้คอนเซ็ปของ Interface มาช่วยในการออกแบบค่อนข้างมาก โดยบางทีจะเห็นว่า Interface เพียงหนึ่งตัว จะมีคลาสมากมายมารุมทำการ implement ตัว Interface ตัวนั้น วิธีการเช่นนี้มีจุุดประสงค์และข้อดีอย่างไรในเชิงของการออกแบบ วันนี้เรามาคุยกันซักเล็กน้อยกับเรื่องนี้

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

สมมุติว่าเรามีโทรศัพท์มือถืออยู่เครื่องหนึ่ง สิ่งที่เราต้องเรียนรู้เพื่อใช้ในการโทรศัพท์ออกจากโทรศัพท์มือถือของเรามีเพียงสามอย่างคือ
1. ฟังก์ชันกดเบอร์โทรศัพท์ insert() โดยผ่านทางปุ่มกดเบอร์ 0-9
2. ฟังก์ชันสำหรับทำการติดต่อ connect() โดยผ่านทางปุ่ม talk และ
3. ฟังก์ชันสำหรับหยุดการติดต่อ end() โดยผ่านทางปุ่ม end

ถ้าไม่พูดถึงความสวยงามหรือฟังก์ชันอื่น ๆ ของโทรศัพท์ ไม่ว่าโทรศัพท์มือถือเครื่องไหน ถ้ามีสามฟังก์ชันนี้ เราก็สามารถใช้โทรติดต่อสื่อสารได้เหมือนกัน โทรศัพท์มือถือของเราถ้าเปรียบเทียบกับ Interface ก็อาจจะเป็น*

public interface MobilePhone {
   public void insert();
   public void connect();
   public void end();
}
* วิธีการเขียน Interface ในเชิง programming จะไม่ถูกกล่าวถึงในบทความนี้ ผู้ที่สนใจสามารถศึกษารายละเอียดเพิ่มเติมได้จาก Resources

เราทำการ define ตัว Interface ขึ้นมาอันหนึ่งชื่อ MobilePhone ซึ่งบรรจุฟังก์ชันที่จำเป็นไว้สามอย่างคือ insert(), connect() และ end() คลาสใดก็ตามที่ต้องการ implement ตัว Interface นี้ จะต้องทำการ implement ฟังก์ชันสามอย่างนี้ด้วยวิธีใดวิธีหนึ่ง
หลังจากที่เรามีโทรศัพท์ เราก็ต้องมีคนที่ใช้โทรศัพท์ คลาสที่ใช้แทนผู้ใช้โทรศัพท์ก็อาจจะเป็น

public class User {
  public void makeACall(MobilePhone mobile) {
    mobile.insert();
    mobile.connect();
    mobile.end();
  }
}
คลาส User มีเพียงหนึ่งฟังก์ชันคือ makeACall(...) ซึ่งรับ MobilePhone Interface เป็น argument ข้อสังเกตอย่างหนึ่งสำหรับฟังก์ชันนี้คือ ฟังก์ชันนี้จะไม่สนใจว่าคลาสที่ใส่เข้ามาเป็นตัว argument จะเป็นคลาสอะไร แต่คลาสที่จะใส่เข้ามานั้นจะต้องทำการ implement ตัว MobilePhone Interface

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

class NokiaPhone implements MobilePhone {
  public void insert() {
    System.out.println("Nokia - Insert number: Tod Tod Tod ~~~");
  }

  public void connect() {
    System.out.println("Nokia - Connect and Talk: %^&$#@$#");
  }

  public void end() {
    System.out.println("Nokia - End conversation: Oh Yeh!!!");
  }
}
คลาส NokiaPhone ถือว่าเป็นคลาสชนิด MobilePhone เพราะได้ทำการ implement ตัว MobilePhone Interface ด้วยการใส่ส่วนที่เป็น implementation เข้าไปยังฟังก์ชัน insert(), connect() และ end() ทีนี้เรามาดูคลาสที่ใช้แทนโทรศัพท์มือถือยี่ห้อ SamSung กันบ้าง
class SamSungPhone implements MobilePhone {
  public void insert() {
    System.out.println("SamSung - Insert number: Wee Wee Wee ~~~");
  }

  public void connect() {
    System.out.println("SamSung - Connect and Talk: zZZzZz");
  }

  public void end() {
    System.out.println("SamSung - End conversation: Cheer Cheer!!!");
  }
}
คลาส SamSung ก็ถือว่าเป็นคลาสชนิด MobilePhone อีกเช่นกันเพราะตัวมันได้ทำการ implement ฟังก์ชันทั้งหมดที่ถูก define อยู่ใน MobilePhone Interface เหมือนกับที่คลาส NokiaPhone ทำเพียงแต่เราจะสังเกตเห็นว่าโค๊ดที่อยู่ในฟังก์ชันต่าง ๆ ของคลาส SamSung จะไม่เหมือนกับโค๊ดที่อยู่ในคลาส NokiaPhone ซึ่งพูดง่าย ๆ ก็คือคลาสทั้งสองเป็นคลาสชนิด MobilePhone แต่มีส่วนที่เป็น implementation ที่แตกต่างกัน ส่วนที่แตกต่างกันนี้ผู้ใช้จะมองไม่เห็นและไม่จำเป็นจะต้องให้ความสนใจกับมัน ผู้ใช้สามารถเรียกใช้โทรศัพท์ทั้งสองยี่ห้อผ่านทางฟังก์ชันต่าง ๆ ที่เหมือนกันซึ่งถูกบรรจุอยู่ใน MobilePhone Interface อย่างไรก็ตามผลที่ได้จะแตกต่างกันดังตัวอย่างข้างล่าง
...
  User user = new User();
  // both NokiaPhone and SamSungPhone are "MobilePhone"
  MobilePhone nokiaPhone = new NokiaPhone(); 
  MobilePhone samsungPhone = new SamSungPhone();
  
  user.makeACall(nokiaPhone);
  user.makeACall(samsungPhone);
...
ถ้าลองรัน ผลที่ได้ก็จะออกมาเป็นดังนี้ (MobilePhoneTest.java)
>javac MobilePhoneTest.java
>java MobilePhoneTest
Nokia - Insert number: Tod Tod Tod ~~~
Nokia - Connect and Talk: %^&$#@$#
Nokia - End conversation: Oh Yeh!!!
SamSung - Insert number: Wee Wee Wee ~~~
SamSung - Connect and Talk: zZZzZz
SamSung - End conversation: Cheer Cheer!!!
เราจะสังเกตเห็นว่าในคลาส User ข้างต้นจะไม่มีการเกี่ยวข้องกับคลาส NokiaPhone หรือ SamSungPhone เลย สิ่งที่คลาส User รับรู้จะมีเพียง MobilePhone Interface เท่านั้น ในการเขียนส่วนที่เป็น Implementation ให้กับ MobilePhone Interface ตัวคลาส NokiaPhone และคลาส SamSungPhone ก็ไม่มีการเกี่ยวข้องกับคลาส User อีกเช่นกัน คลาส User และคลาสที่เป็นส่วน Implementation ของ MobilePhone Interface (NokiaPhone และ SamSungPhone) เหล่านี้เกี่ยวข้องกันโดยอาศัย MobilePhone Interface เป็นตัวเชื่อม โดยแต่ละคนจะให้ความสนใจใน Interface นี้ในแง่มุมที่แตกต่างกัน (คลาส User สนใจเพียงการใช้โทรศัทพ์มือถือ คลาส NokiaPhone และ SamSungPhone สนใจเพียงการสร้างโทรศัพท์เท่านั้น)

ตัวอย่างของโทรศัพท์มือถือข้างต้นถูกนำมาใช้ในโลกของจาว่าในลักษณะของ APIs (Application Programming Interfaces) ต่าง ๆ ที่ทาง SUN กำหนดขึ้นมาในรูปแบบของ Specification ยกตัวอย่างเช่น Servlet, EJB (Enterprise JavaBeans), JMS (Java Message Service), JAXP (Java APIs for XML Parsing), JMX (Java Management Extension) เป็นต้น โดยทาง SUN จะกำหนดตัว APIs ในรูปแบบของ Interface ซึ่งทางผู้ผลิตซอฟแวร์ที่จะสร้างผลิตภัณฑ์ที่สนันสนุนตัว APIs เหล่านี้จะต้องเป็นคนเขียนส่วนที่เป็น Implementation ของ APIs ขึ้นมาเอง ข้อดีอย่างหนึ่งในการกำหนดรูปแบบของ APIs เข้าไปกับ Specification ก็คือ เราสามารถเขียน Appliation ต่าง ๆ ขึ้นมาโดยอ้างอิงจาก APIs เหล่านี้ โดยหลังจากนั้นเราจะสามารถนำโค๊ดนี้ไปรันบนผลิตภัณฑ์ของบริษัทใดก็ได้ ตราบใดที่่ผลิตภัณฑ์นั้นถูกผลิตขึ้นโดยอาศัย APIs รูปแบบเดียวกันกับที่เราใช้เป็นตัวอ้างอิง ตัวอย่างที่เราพบเห็นง่าย ๆ ก็คือ การเขียน Servlet ไปรันบน Servlet Container เช่น Apache Tomcat, WebLogic, JBoss หรือการเขียน JMS Application ไปรันบน Fiorano JMS Server หรือ JBoss MQ เป็นต้น

Resources
สำหรับผู้อ่านที่สนใจเกี่ยวกับคอบเซ็ปของ Interface สามารถศึกษารายละเอียดเพิ่มเติมได้จาก
Artima.com เจ้าของเวป (Bill Venners) เป็นคนเขียนบทความเองทั้งหมด เนื้อหาดีมากเหมาะสำหรับการปูพื้นฐาน
java.sun.com มีเนื้อหาเกี่ยวกับ Interface อยู่เล็กน้อย :-X


Copyright © 2000-2001 www.jarticles.com