
มาถึงตอนที่ 4 ของซีรีย์ SOLID กันแล้วนะครับ กับตัว I ซึ่งตัวนี้ย่อมาจาก Interface segregation Principle (ISP) ที่จะทำให้ Code ที่เขียนด้วยภาษา OOP นั้นดูดีมากขึ้นไปอีกขั้น 😉
ISP นั้นกล่าวถึงหลักการที่ว่า
Class ที่มา Implement เพื่อใช้งาน Interface นั้นๆไม่ควรที่จะต้องมา Implement Method ต่างๆให้ครบโดยที่อาจจะไม่ได้ใช้
ซึ่งถ้าตีความง่ายๆที่สุดเลยก็คือ เราควรอย่าไปกลัวกับการที่จะต้องสร้าง Interface ใหม่ เพราะโดนส่วนใหญ่ Developer อาจจะชอบเพิ่ม Method ใน Interface เดิม ซึ่งมันดูปลอดภัยมากกว่านั่นเอง
เราลองมาดูตัวอย่างกัน …
เริ่มแรกเลย
ผมมี Interface ชื่อ IPhone (ไม่ใช่ Appple iPhone นะครับ :D) ซึ่งมีด้วยกันทั้งหมด 6 Methods
interface IPhone {
call();
ring();
installApp();
addContact();
connectWifi();
connectMobile();
}
ที่นีผมก็ลองสร้าง Class ชื่อ SmartPhone ที่จะมา Implement ตัว Interface IPhone ดูครับ
class SmartPhone implements IPhone {
public call(){ /* do something /* }
public ring(){/* do something /* }
public installApp(){/* do something /* }
public addContact(){/* do something /* }
public connectWifi(){/* do something /* }
public connectMobile(){/* do something /* }
}
เนื่องจากว่า SmartPhone นั้นมีความสามารถครบก็ดูโอเคดีครับที่เราจะออกแบบ Interface IPhone แบบนี้
แต่ถ้าผมมีอีก Class ชื่อ FeaturePhone ที่ต้อง Implement IPhone แต่ไม่สามารถ install App หรือ ต่อ wifi ได้ล่ะ ด้วยมันก็จะเป็นแบบนี้
class FeaturePhone implements IPhone {
public call(){}
public ring(){}
public installApp(){
throw new Error('Feature Phone does not support apps..');
}
public addContact(){}
public connectWifi(){
throw new Error('Feature Phone does not support using Wifi..');
}
public connectMobile(){}
}
และถ้าเรามีอีก Class ชื่อ BasicTablet ที่มันทำอะไรไม่ค่อยได้นอกว่า install App กับต่อ wifi มันก็จะเป็นแบบนี้
BasicTablet implements IPhone {
public call(){
throw new Error('Basic Tablet does not support call..');
}
public ring(){
throw new Error('Basic Tablet does not support ring..');
}
public installApp(){}
public addContact(){
throw new Error('Basic Tablet does not support contacts..');
}
public connectWifi(){}
public connectMobile(){
throw new Error('Basic Tablet does not support using mobile..');
}
}
ซึ่งจากตัวอย่างทั้ง 2 Class นั้นก็จะเห็นได้ว่า การออกแบบ Interface แบบนี้ดูแล้วมันค่อนข้างกว้างไป
.
.
เริ่มการ Refactor
แน่นอนว่า Interface IPhone ที่สร้างไว้ตั้งแต่ต้นนั้นมันกว้างไป เทคนิคที่จะใช้ครั้งนี้คือ Extract Interface ซึ่งก็คือทำการแบบ Interface IPhone ออกมาเป็น Interface ย่อยๆตามกลุ่มของ Method ที่เรามี
ซึ่งถ้าเราสังเกตุดูจาก Interface IPhone นั้นจะมี Method อยู่ 3 กลุ่มคือ
- โทร พื้นฐาน
- การใช้งาน App
- การเชื่อมต่อ
ดังนั้นหลักจากที่ทำการ Extract Method แล้วจะได้ผลแบบนี้ครับ
interface IPhone {
call();
ring();
addContact();
}
interface ISmartFeatures {
installApp();
}
interface IMobileConnect {
connectMobile();
connectWifi();
}
ที่นี้ Class SmartPhone ของเราก็ต้องแก้ Code ให้มาเป็นแบบนี้แทน
class SmartPhone implements IPhone, ISmartFeatures, IMobileConnect {
public call(){}
public ring(){}
public installApp(){}
public addContact(){}
public connectWifi(){}
public connectMobile(){}
}
ส่วน Class FeaturePhone ก็จะเป็นแบบนี้ ซึ่งก็ยังต้องมีการ handle Method ที่ไม่ Support อยู่แต่ว่าก็จะน้อยลงกว่าตอนแรก
class FeaturePhone implements IPhone, IMobileConnect { public call(){} public ring(){} public addContact(){} public connectMobile(){} public connectWifi() { throw new Error('Feature Phone does not support using Wifi..'); } }
ส่วน Class BasicTablet นั้นก็จะคล้ายๆกับ FeaturePhone ก็คือ
class BasicTablet implements ISmartFeatures, IMobileConnect { public installApp(){} public connectWifi(){} public addContact(){} public connectMobile(){} public connectMobile(){ throw new Error('Basic Tablet does not support using mobile..'); } }
สรุป
เพื่อนๆก็จะเห็นว่าการทำ Interface Segregation นั้นจะทำให้เราสามารถออกแบบ Interface ได้ดียิ่งขึ้นและไม่เป็นการทำให้ Client ของ Interface นั้นต้องมาเขียน Code เพิ่มโดยที่ไม่ได้ Handle อะไรเลย รวมถึงทำให้ Class และ Interface ของเราดูแลได้ง่ายขึ้นและเจาะจงการการทำงานแต่ละเรื่องมากขึ้น