มาถึงตอนที่ 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 ของเราดูแลได้ง่ายขึ้นและเจาะจงการการทำงานแต่ละเรื่องมากขึ้น