เพิ่มประสิทธิภาพฐานโค้ด Swift ของคุณด้วยการออกแบบคลาสเชิงวัตถุ
ฉันพยายามปรับปรุงคุณภาพโค้ดของฉัน เขียนโค้ดที่สามารถอ่านและพัฒนาได้มากขึ้นมาระยะหนึ่งแล้ว หนึ่งในขั้นตอนที่สำคัญที่สุดในกระบวนการนี้คือการใช้หลักการ SOLID ในโค้ด มาดูกันว่า SOLID นี้คืออะไร?
SOLID เป็นหลักการห้าประการของการออกแบบคลาสเชิงวัตถุ เป็นชุดของกฎและแนวทางปฏิบัติที่ดีที่สุดที่ต้องปฏิบัติตามขณะออกแบบโครงสร้าง
(S) หลักการความรับผิดชอบเดียว
โดยพื้นฐานแล้ว หลักการนี้เน้นว่าแต่ละโมดูลที่พัฒนาแล้วมีหน้าที่รับผิดชอบเพียงข้อเดียวเท่านั้น อ็อบเจ็กต์และ/หรือคลาสมีหน้าที่รับผิดชอบงานเดียวเท่านั้นนับตั้งแต่วินาทีที่เริ่มต้น
ก่อนอื่น มาดูโค้ดที่ไม่เป็นไปตาม SRP และใช้งานได้ภายใต้สภาวะปกติกันก่อน
เรามีคลาส DataHandler
ซึ่งสร้างข้อมูล แยกวิเคราะห์ข้อมูล และสุดท้ายจะบันทึกข้อมูลที่ได้สร้างไว้ หากคุณนำโค้ดนี้ไปลองรัน มันจะคอมไพล์ได้โดยไม่มีปัญหาใดๆ แล้วปัญหาหรือปัญหาที่นี่คืออะไร?
เมื่อเราดูโค้ด คลาส DataHandler
มีหน้าที่หลายอย่าง เช่น การแยกวิเคราะห์ การบันทึก และการสร้างข้อมูล
วิธีแก้ปัญหา ความรับผิดชอบปัจจุบันสามารถย้ายไปยังคลาสอื่นได้:
(O) หลักการเปิด/ปิด
กล่าวง่ายๆ ก็คือ เอนทิตีควรเปิดเพื่อขยาย แต่ปิดเพื่อแก้ไข
คำอธิบายอาจทำให้สับสนในบางครั้ง มาตรวจสอบโค้ดกัน:
ลองจินตนาการว่าเรามี PaymentManager
ให้ผู้จัดการรายนี้สนับสนุนวิธีการชำระเงินด้วยเงินสดและ Visa ในขั้นตอนแรก จนถึงตอนนี้ทุกอย่างดีมาก หลังจากนั้นไม่นาน เราต้องอัปเดตผู้จัดการและเราคาดว่าจะเพิ่มฟีเจอร์ MasterCard เป็นฟีเจอร์ใหม่
มาสร้างฟังก์ชันที่เรียกว่า makeMasterCardPayment
เหมือนฟังก์ชันก่อนหน้านี้กันดีกว่า เยี่ยมมาก รหัสของเราจะยังคงใช้งานได้ต่อไป เราปฏิบัติตามข้อกำหนดแต่เราฝ่าฝืนกฎที่ต้องปิดชั้นเรียนเพื่อทำการแก้ไข สำหรับงานที่ทำงานคล้ายกัน เราไม่ควรเพิ่มสิ่งใหม่ลงในชั้นเรียน
มาดูกันว่าเราจะแก้ไขปัญหานี้ได้อย่างไร:
เรามากำหนดงานหลักใน PaymentManager
ในโครงสร้างนามธรรม (โปรโตคอล) กัน โครงสร้างนี้จะตอบข้อกำหนดที่ PaymentManager
คาดหวัง
นอกจากนี้เรายังสร้างคลาสแยกต่างหากสำหรับวิธีการชำระเงินแต่ละวิธี คลาสเหล่านี้จะอยู่ในโครงสร้างนามธรรมที่ PaymentMamager
คาดหวัง ดังนั้นเราจึงสามารถเพิ่มวิธีการชำระเงินใหม่ได้มากเท่าที่ต้องการโดยไม่ต้องทำการเปลี่ยนแปลงใดๆ กับผู้จัดการ
ดังนั้นเราจึงปล่อยให้คลาส PaymentManager
ของเราเปิดรับส่วนขยายแต่ปิดไม่ให้มีการแก้ไข
(L) หลักการเปลี่ยนตัวลิสคอฟ
วัตถุควรถูกแทนที่ด้วยอินสแตนซ์ของคลาสย่อยโดยไม่เปลี่ยนแปลงพฤติกรรม หลังจากคำอธิบายสั้น ๆ นี้ เรามาพูดถึงโค้ดกันดีกว่า
สมมติว่าเรามีสี่เหลี่ยมผืนผ้าประเภทหนึ่ง สี่เหลี่ยมผืนผ้ามีความกว้างและความสูง และผลคูณของพวกมันเท่ากับพื้นที่
ไม่ว่าเราจะมีคลาสสแควร์ก็ตาม ตามทฤษฎีแล้ว สแควร์ก็คือสี่เหลี่ยมผืนผ้า ดังนั้นเราสามารถสืบทอดคลาสสแควร์จากคลาสสี่เหลี่ยมผืนผ้าได้ จนถึงตอนนี้ทุกอย่างดีมาก
ฟังก์ชัน setSizeAndPrint
ต่อไปนี้ต้องการตัวแปรประเภทสี่เหลี่ยมผืนผ้า และกำหนดความกว้างและความสูงของสี่เหลี่ยมผืนผ้าเป็นค่าเริ่มต้น คุณสามารถเรียกฟังก์ชันนี้สำหรับคลาสสี่เหลี่ยมผืนผ้าได้ เนื่องจาก width = 4
, height = 5
, area = 20
แต่สิ่งเดียวกันนี้ไม่เป็นความจริงสำหรับกำลังสองที่สืบทอดมาจากคลาสสี่เหลี่ยมผืนผ้า เนื่องจากด้านทั้งสองของสี่เหลี่ยมจัตุรัสเท่ากัน เราไม่สามารถกำหนด 4 และ 5 ตามค่าเริ่มต้นและคาดหวังให้มันทำงานเหมือนกับคลาสที่สืบทอดมาได้
ณ จุดนี้ คลาสที่ไม่สามารถทำหน้าที่เป็นคลาสที่สืบทอดมาและต้องการการพัฒนาเฉพาะสถานการณ์จะทำให้ LSP เสียหาย
วิธีแก้ปัญหานี้มีเป้าหมายเพื่อให้แต่ละคลาสทำงานของตัวเองภายในตัวเอง โดยเก็บงานทั่วไประหว่างคลาสไว้ในโครงสร้างนามธรรม (โปรโตคอล)
ดังตัวอย่างข้างต้น งานทั่วไประหว่างคลาส Rectangle
และ Square
คือการคำนวณพื้นที่ของวัตถุ ทั้งคลาสสี่เหลี่ยมและสี่เหลี่ยมสืบทอดโครงสร้างนามธรรมของรูปหลายเหลี่ยมหลังจากงานนี้ถูกกำหนดในโปรโตคอลทั่วไป ดังนั้นแต่ละชั้นเรียนจึงบรรลุภารกิจที่จำเป็นภายในตัวมันเอง และไม่จำเป็นต้องพัฒนาเป็นพิเศษใดๆ คลาสมีพฤติกรรมเหมือนกับโครงสร้างที่สืบทอดมา
(I) หลักการแยกอินเทอร์เฟซ
โดยสรุป ลูกค้าไม่ควรถูกบังคับให้พึ่งพาอินเทอร์เฟซที่พวกเขาไม่ได้ใช้ ไม่ควรบังคับให้โค้ดขึ้นอยู่กับวิธีการที่ไม่ได้ใช้
มาดูโค้ดกันดีกว่าและดูปัญหาในทางปฏิบัติ
เรามาสร้างโครงสร้างเชิงนามธรรมที่เรียกว่า Worker
และโดยทั่วไป เราคาดหวังว่าผู้ที่สืบทอดมาจากคลาส Worker จะสามารถทำงานกินและทำงานได้
ก่อนอื่น ให้มีคลาสชื่อ Human
และคลาสนี้สืบทอดมาจากโครงสร้างนามธรรม Worker ตามทฤษฎีแล้ว เราคาดหวังให้บุคคลหนึ่งได้ทั้งกินและทำงาน จากนั้นเราจำเป็นต้องมีโครงสร้างหุ่นยนต์และเราสืบทอดมาจากโครงสร้างผู้ปฏิบัติงานเพราะหุ่นยนต์สามารถทำงานได้
ปัญหาเริ่มต้นที่นี่เนื่องจากโปรโตคอล Worker มีสองฟังก์ชัน ฟังก์ชันหนึ่งคืองานและอีกฟังก์ชันกิน ไม่มีปัญหาสำหรับฟังก์ชันการทำงานเพราะหุ่นยนต์สามารถทำงานได้ แต่เนื่องจากเราสืบทอดมาจากโครงสร้างผู้ปฏิบัติงาน เราจึงต้องเพิ่มฟังก์ชันการกินเป็น เลยทำให้ต้องส่งต่อความรับผิดชอบที่ไม่จำเป็นให้กับชั้นเรียน นี่คือการหยุดพักของ ISP
เพื่อที่จะแก้ไขปัญหานี้ เราต้องแบ่งความรับผิดชอบของเราซึ่งมีโครงสร้างที่เป็นนามธรรมออกเป็นส่วนพื้นฐาน
เรากำลังสร้างโครงสร้างนามธรรมใหม่ที่เรียกว่า Feedable
สำหรับฟังก์ชัน eat และโครงสร้างนามธรรม Workable
สำหรับฟังก์ชันการทำงาน ดังนั้นเราจึงได้แบ่งหน้าที่ความรับผิดชอบของเรา
ตอนนี้คลาส Human
จะสืบทอดมาจาก Feeble
และ Workable
และคลาส Robot
จาก Workable
เท่านั้น
ดังนั้นเราจึงไม่กำหนดความรับผิดชอบที่ไม่จำเป็นให้กับคลาสใดๆ และเราสร้างโครงสร้างที่เหมาะสมสำหรับ ISP
(D) หลักการผกผันการพึ่งพา
โมดูลระดับสูงตามทฤษฎี DIP ไม่ควรนำเข้าสิ่งใดจากโมดูลระดับต่ำ ทั้งสองอย่างควรขึ้นอยู่กับนามธรรม และนามธรรมไม่ควรขึ้นอยู่กับรายละเอียด รายละเอียดควรขึ้นอยู่กับนามธรรม
ลองดูตัวอย่างด้านล่างพร้อมข้อมูลเชิงทฤษฎี
เรามีโครงสร้าง employee
และโครงสร้างนี้มีฟังก์ชันการทำงาน นอกจากนี้เรายังมีโครงสร้าง Employer
และโครงสร้างนี้ช่วยให้พนักงานที่มีอยู่ทำงานได้
ออบเจ็กต์ employer
ถูกสร้างขึ้นในฟังก์ชัน run และตามค่าเริ่มต้นจะใช้อาร์เรย์ Employee
จนถึงตอนนี้ทุกอย่างเรียบร้อยดีอีกครั้ง โครงการของเราน่าจะได้ผล แต่มีบางอย่างที่เราพลาดไป โครงสร้าง Employer
เชื่อมโยงโดยตรงกับโครงสร้าง Employee
ที่ไม่ใช่นามธรรม นี่คือจุดที่เราต้องการกรมทรัพย์สินทางปัญญา
ด้วยการใช้ความรู้ทางทฤษฎีของกรมทรัพย์สินทางปัญญา เรารู้ว่าโครงสร้างควรขึ้นอยู่กับแบบจำลองเชิงนามธรรม
ดังนั้นเราจึงสร้างโครงสร้าง Workable
แบบนามธรรมและขึ้นอยู่กับคลาส Employee
เป็น Workable
เพื่อให้โครงสร้าง Employee
ยังคงฟังก์ชันดั้งเดิมไว้
ประเด็นก็คือตอนนี้คลาส Employer
คาดว่าจะมีอาร์เรย์ของโครงสร้างนามธรรม Workable
แทนที่จะเป็นอาร์เรย์ Employee
ดังนั้นเราจึงเชื่อมโยงการขึ้นต่อกันของโครงสร้าง Employer
กับโมดูลนามธรรม ซึ่งหมายความว่าโครงสร้าง Employer
มาถึงจุดที่สามารถเรียกใช้โครงสร้างใดๆ ที่ขึ้นอยู่กับโมดูล Workable
ได้
ฉันหวังว่าคุณจะสนุกกับบทความนี้ คุณสามารถค้นหารหัสทั้งหมดได้ใน GitHub ของฉัน:
ลิงค์อ้างอิง
1.https://en.wikipedia.org/wiki/Dependency_inversion_principle
2.https://www.youtube.com/watch?v=rndiYu8If-I&list=PL_csAAO9PQ8ZIh89P2re5fziX9kaI8Yhx&index=1
3.https://en.wikipedia.org/wiki/SOLID
4.http://www.principles-wiki.net/principles:open-closed_principle
5.https://en.wikipedia.org/wiki/Dependency_inversion_principle