ไม่มีกลยุทธ์การแยกส่วนเพียงอย่างเดียวที่เหมาะกับทุกโปรเจ็กต์ เนื่องจาก Gradle มีความยืดหยุ่น จึงมีข้อจำกัดเพียงเล็กน้อยเกี่ยวกับวิธีจัดระเบียบโปรเจ็กต์ หน้านี้จะให้ภาพรวมของกฎทั่วไปและรูปแบบที่พบบ่อยบางอย่างที่คุณสามารถใช้เมื่อพัฒนาแอป Android แบบหลายโมดูล
หลักการเชื่อมโยงสูงและการเชื่อมโยงต่ำ
วิธีหนึ่งในการระบุลักษณะของโค้ดเบสแบบแยกส่วนคือการใช้พร็อพเพอร์ตี้การเชื่อมโยง และความเหนียวแน่น การคัปปลิ้งจะวัดระดับที่โมดูล ขึ้นอยู่กับโมดูลอื่น ความเชื่อมโยงในบริบทนี้จะวัดว่าองค์ประกอบของโมดูลเดียวมีความเกี่ยวข้องในเชิงฟังก์ชันอย่างไร โดยทั่วไปแล้ว คุณควรพยายามให้ การเชื่อมโยงต่ำและความเหนียวแน่นสูง
- การเชื่อมโยงต่ำหมายความว่าโมดูลควรเป็นอิสระจากกันมากที่สุด เพื่อให้การเปลี่ยนแปลงในโมดูลหนึ่งส่งผลกระทบต่อโมดูลอื่นๆ น้อยที่สุดหรือไม่มีเลย โมดูลไม่ควรทราบการทำงานภายในของโมดูลอื่นๆ
- การยึดเหนี่ยวสูงหมายความว่าโมดูลควรประกอบด้วยชุดโค้ดที่ ทำหน้าที่เป็นระบบ โดยควรมีหน้าที่รับผิดชอบที่ชัดเจนและอยู่ภายในขอบเขตของความรู้ในโดเมนที่เฉพาะเจาะจง ลองพิจารณาแอปพลิเคชัน eBook ตัวอย่าง การรวมโค้ดที่เกี่ยวข้องกับหนังสือและการชำระเงินไว้ในโมดูลเดียวกันอาจไม่เหมาะสม เนื่องจากเป็นโดเมนฟังก์ชันการทำงานที่แตกต่างกัน
ประเภทของโมดูล
วิธีจัดระเบียบโมดูลจะขึ้นอยู่กับสถาปัตยกรรมของแอปเป็นหลัก ด้านล่างนี้ คือโมดูลประเภทที่พบบ่อยซึ่งคุณอาจนำมาใช้ในแอปขณะที่ทำตามสถาปัตยกรรมแอปที่แนะนำ
โมดูลข้อมูล
โดยปกติแล้วโมดูลข้อมูลจะมีที่เก็บ แหล่งข้อมูล และคลาสโมเดล ความรับผิดชอบหลัก 3 ประการของโมดูลข้อมูลมีดังนี้
- ห่อหุ้มข้อมูลและตรรกะทางธุรกิจทั้งหมดของโดเมนหนึ่งๆ: โมดูลข้อมูลแต่ละโมดูลควรมีหน้าที่จัดการข้อมูลที่แสดงถึงโดเมนหนึ่งๆ โดยจะจัดการข้อมูลได้หลายประเภทตราบใดที่ข้อมูลนั้นเกี่ยวข้อง
- เปิดเผยที่เก็บเป็น API ภายนอก: API สาธารณะของโมดูลข้อมูลควรเป็นที่เก็บเนื่องจากมีหน้าที่รับผิดชอบในการเปิดเผยข้อมูลต่อส่วนอื่นๆ ของแอป
- ซ่อนรายละเอียดการติดตั้งใช้งานและแหล่งข้อมูลทั้งหมดจากภายนอก
ที่เก็บควรเข้าถึงแหล่งข้อมูลได้จากโมดูลเดียวกันเท่านั้น
โดยจะยังคงซ่อนไว้จากภายนอก คุณบังคับใช้ได้โดยใช้คีย์เวิร์ดการมองเห็น
privateหรือinternalของ Kotlin
โมดูลฟีเจอร์
ฟีเจอร์คือส่วนที่แยกออกมาของฟังก์ชันการทำงานของแอป ซึ่งมักจะสอดคล้องกับหน้าจอหรือชุดหน้าจอที่เกี่ยวข้องอย่างใกล้ชิด เช่น ขั้นตอนการลงชื่อสมัครใช้หรือชำระเงิน หากแอปมีการนำทางด้วยแถบด้านล่าง ปลายทางแต่ละแห่ง น่าจะเป็นฟีเจอร์
ฟีเจอร์จะเชื่อมโยงกับหน้าจอหรือปลายทางในแอป ดังนั้น
จึงมีแนวโน้มที่จะมี UI ที่เชื่อมโยงและViewModelเพื่อจัดการตรรกะ
และสถานะ ฟีเจอร์เดียวไม่จำเป็นต้องจำกัดไว้ที่มุมมองเดียวหรือ
ปลายทางการนำทางเดียว โมดูลฟีเจอร์ขึ้นอยู่กับโมดูลข้อมูล
โมดูลแอป
โมดูลแอปเป็นจุดแรกเข้าของแอปพลิเคชัน โดยจะขึ้นอยู่กับโมดูลฟีเจอร์ และมักจะให้การนำทางระดับรูท โมดูลแอปเดียวสามารถคอมไพล์ เป็นไบนารีต่างๆ ได้หลายรายการด้วยตัวแปรบิลด์
หากแอปของคุณกำหนดเป้าหมายอุปกรณ์หลายประเภท เช่น Android Auto, Wear หรือ TV ให้กำหนดโมดูลแอปสำหรับแต่ละประเภท ซึ่งจะช่วยแยกการอ้างอิงที่เฉพาะเจาะจงแพลตฟอร์ม
โมดูลทั่วไป
โมดูลทั่วไปหรือที่เรียกว่าโมดูลหลักจะมีโค้ดที่โมดูลอื่นๆ ใช้บ่อย ซึ่งจะช่วยลดความซ้ำซ้อนและไม่ได้แสดงเลเยอร์ใดเลเยอร์หนึ่งใน สถาปัตยกรรมของแอป ตัวอย่างโมดูลที่พบบ่อยมีดังนี้
- โมดูล UI: หากคุณใช้องค์ประกอบ UI ที่กำหนดเองหรือการสร้างแบรนด์ที่ซับซ้อนในแอป คุณควรพิจารณาห่อหุ้มคอลเล็กชันวิดเจ็ตไว้ในโมดูล เพื่อให้ฟีเจอร์ทั้งหมดนำกลับมาใช้ใหม่ได้ ซึ่งจะช่วยให้ UI สอดคล้องกันในฟีเจอร์ต่างๆ เช่น หากการกำหนดธีมของคุณรวมศูนย์ คุณก็หลีกเลี่ยง การปรับโครงสร้างที่เจ็บปวดได้เมื่อมีการเปลี่ยนแบรนด์
- โมดูล Analytics: การติดตามมักกำหนดโดยข้อกำหนดทางธุรกิจโดย ไม่ค่อยคำนึงถึงสถาปัตยกรรมซอฟต์แวร์ โดยมักใช้เครื่องมือติดตาม Analytics ในคอมโพเนนต์ที่ไม่เกี่ยวข้องหลายอย่าง หากเป็นกรณีของคุณ คุณอาจ ต้องมีโมดูลข้อมูลวิเคราะห์เฉพาะ
- โมดูลเครือข่าย: เมื่อโมดูลจำนวนมากต้องใช้การเชื่อมต่อเครือข่าย คุณอาจ พิจารณาใช้โมดูลที่ทําหน้าที่เป็นไคลเอ็นต์ http โดยเฉพาะ ซึ่งมีประโยชน์อย่างยิ่งเมื่อไคลเอ็นต์ของคุณต้องการการกำหนดค่าที่กำหนดเอง
- โมดูลยูทิลิตี: ยูทิลิตีหรือที่เรียกว่าตัวช่วยมักเป็นโค้ดขนาดเล็ก ที่นำกลับมาใช้ใหม่ในแอปพลิเคชัน ตัวอย่างของยูทิลิตี ได้แก่ ตัวช่วยในการทดสอบ ฟังก์ชันการจัดรูปแบบสกุลเงิน โปรแกรมตรวจสอบอีเมล หรือ ตัวดำเนินการที่กำหนดเอง
โมดูลทดสอบ
โมดูลทดสอบคือโมดูล Android ที่ใช้เพื่อการทดสอบเท่านั้น โมดูลมีโค้ดทดสอบ ทรัพยากรทดสอบ และการขึ้นต่อกันของการทดสอบที่จำเป็นสำหรับการเรียกใช้การทดสอบเท่านั้น และไม่จำเป็นในระหว่างรันไทม์ของแอปพลิเคชัน โมดูลทดสอบสร้างขึ้นเพื่อแยกโค้ดเฉพาะการทดสอบออกจากแอปพลิเคชันหลัก ซึ่งจะช่วยให้จัดการและดูแลโค้ดโมดูลได้ง่ายขึ้น
กรณีการใช้งานสำหรับโมดูลทดสอบ
ตัวอย่างต่อไปนี้แสดงสถานการณ์ที่การติดตั้งใช้งานโมดูลทดสอบ อาจเป็นประโยชน์อย่างยิ่ง
โค้ดทดสอบที่แชร์: หากมีหลายโมดูลในโปรเจ็กต์และโค้ดทดสอบบางส่วนใช้ได้กับโมดูลมากกว่า 1 โมดูล คุณสามารถสร้างโมดูลทดสอบเพื่อแชร์โค้ดได้ ซึ่งจะช่วยลดการทำซ้ำและทำให้โค้ดทดสอบ ดูแลรักษาง่ายขึ้น โค้ดทดสอบที่แชร์อาจรวมถึงคลาสหรือฟังก์ชันยูทิลิตี เช่น การยืนยันหรือตัวจับคู่ที่กำหนดเอง รวมถึงข้อมูลทดสอบ เช่น การตอบกลับ JSON ที่จำลอง
การกำหนดค่าบิลด์ที่สะอาดขึ้น: โมดูลทดสอบช่วยให้คุณมีการกำหนดค่าบิลด์ที่สะอาดขึ้น เนื่องจากมีไฟล์
build.gradleของตัวเองได้ คุณไม่จำเป็นต้องใส่การกำหนดค่าที่เกี่ยวข้องกับการทดสอบเท่านั้นไว้ในไฟล์build.gradleของโมดูลแอปการทดสอบการผสานรวม: โมดูลทดสอบสามารถใช้เพื่อจัดเก็บการทดสอบการผสานรวมที่ใช้เพื่อทดสอบการโต้ตอบระหว่างส่วนต่างๆ ของแอป รวมถึงอินเทอร์เฟซผู้ใช้ ตรรกะทางธุรกิจ คำขอเครือข่าย และการค้นหาฐานข้อมูล
แอปพลิเคชันขนาดใหญ่: โมดูลการทดสอบมีประโยชน์อย่างยิ่งสำหรับ แอปพลิเคชันขนาดใหญ่ที่มีฐานโค้ดที่ซับซ้อนและมีหลายโมดูล ในกรณีดังกล่าว โมดูลทดสอบจะช่วยปรับปรุงการจัดระเบียบโค้ดและความสามารถในการบำรุงรักษาได้
การสื่อสารระหว่างโมดูล
โดยปกติแล้วโมดูลมักไม่ได้แยกกันอย่างสิ้นเชิง และมักต้องอาศัยโมดูลอื่นๆ และสื่อสารกับโมดูลเหล่านั้น คุณควรลดการเชื่อมโยงให้ต่ำแม้ว่าโมดูลจะทำงานร่วมกันและแลกเปลี่ยนข้อมูลบ่อยก็ตาม บางครั้งการสื่อสารโดยตรงระหว่าง 2 โมดูลอาจไม่เป็นที่ต้องการ เช่น ในกรณีที่มีข้อจำกัดด้านสถาปัตยกรรม หรืออาจเป็นไปไม่ได้ เช่น ในกรณีที่มีการอ้างอิงแบบวงกลม
หากต้องการแก้ปัญหานี้ คุณสามารถมีโมดูลที่สามที่ทำหน้าที่เป็นสื่อกลาง ระหว่างโมดูลอื่นๆ 2 โมดูล โมดูลตัวกลางสามารถรับฟังข้อความจากทั้ง 2 โมดูลและส่งต่อได้ตามต้องการ ในแอปตัวอย่างของเรา หน้าจอการชำระเงิน ต้องทราบว่าควรซื้อหนังสือเล่มใด แม้ว่าเหตุการณ์จะเกิดขึ้นใน หน้าจอแยกต่างหากซึ่งเป็นส่วนหนึ่งของฟีเจอร์อื่นก็ตาม ในกรณีนี้ ตัวกลางคือโมดูลที่เป็นเจ้าของกราฟการนำทาง (โดยปกติคือโมดูลแอป) ในตัวอย่างนี้ เราใช้การนำทางเพื่อส่งข้อมูลจากฟีเจอร์หน้าแรกไปยังฟีเจอร์ ชำระเงินโดยใช้คอมโพเนนต์ Navigation
navController.navigate("checkout/$bookId")
ปลายทางการชำระเงินจะได้รับรหัสหนังสือเป็นอาร์กิวเมนต์ ซึ่งจะใช้เพื่อ
ดึงข้อมูลเกี่ยวกับหนังสือ คุณใช้ตัวแฮนเดิลสถานะที่บันทึกไว้เพื่อ
เรียกอาร์กิวเมนต์การนำทางภายใน ViewModel ของฟีเจอร์ปลายทางได้
class CheckoutViewModel(savedStateHandle: SavedStateHandle, …) : ViewModel() {
val uiState: StateFlow<CheckoutUiState> =
savedStateHandle.getStateFlow<String>("bookId", "").map { bookId ->
// produce UI state calling bookRepository.getBook(bookId)
}
…
}
คุณไม่ควรส่งออบเจ็กต์เป็นอาร์กิวเมนต์การนำทาง แต่ให้ใช้รหัสที่เรียบง่าย ซึ่งฟีเจอร์สามารถใช้เพื่อเข้าถึงและโหลดทรัพยากรที่ต้องการจากชั้นข้อมูล วิธีนี้จะช่วยให้การเชื่อมโยงต่ำและไม่ละเมิดหลักการแหล่งข้อมูลที่ถูกต้องเพียงแห่งเดียว
ในตัวอย่างด้านล่าง ทั้ง 2 โมดูลฟีเจอร์ขึ้นอยู่กับโมดูลข้อมูลเดียวกัน ซึ่งจะช่วยลดปริมาณข้อมูลที่โมดูลตัวกลางต้องส่งต่อ และลดการเชื่อมโยงระหว่างโมดูล โมดูลควรแลกเปลี่ยนรหัสดั้งเดิมและโหลดทรัพยากรจากโมดูลข้อมูลที่ใช้ร่วมกันแทนการส่งออบเจ็กต์
การผกผันการขึ้นต่อกัน
การผกผันการพึ่งพาคือการจัดระเบียบโค้ดเพื่อให้การแยกส่วนเป็น อิสระจากการใช้งานจริง
- การแยกส่วน: สัญญาที่กำหนดวิธีที่คอมโพเนนต์หรือโมดูลในแอปพลิเคชันของคุณ โต้ตอบกัน โมดูลการแยกส่วนจะกำหนด API ของ ระบบและมีอินเทอร์เฟซและโมเดล
- การติดตั้งใช้งานจริง: โมดูลที่ขึ้นอยู่กับโมดูลการแยกข้อมูล และติดตั้งใช้งานลักษณะการทำงานของการแยกข้อมูล
โมดูลที่อิงตามลักษณะการทำงานที่กำหนดไว้ในโมดูลการแยกส่วนควรขึ้นอยู่กับการแยกส่วนเองเท่านั้น ไม่ใช่การติดตั้งใช้งานที่เฉพาะเจาะจง
ตัวอย่าง
ลองนึกถึงโมดูลฟีเจอร์ที่ต้องใช้ฐานข้อมูลในการทำงาน โมดูลฟีเจอร์ไม่เกี่ยวข้องกับวิธีที่ใช้ฐานข้อมูล ไม่ว่าจะเป็นฐานข้อมูล Room ในเครื่องหรืออินสแตนซ์ Firestore ระยะไกล โดยมีหน้าที่เพียงจัดเก็บและอ่านข้อมูลแอปพลิเคชัน
เพื่อให้บรรลุเป้าหมายนี้ โมดูลฟีเจอร์จึงขึ้นอยู่กับโมดูลการแยกข้อมูลมากกว่าการใช้งานฐานข้อมูลที่เฉพาะเจาะจง การแยกข้อมูลนี้กำหนด API ฐานข้อมูลของแอป กล่าวคือ จะกำหนดกฎสำหรับวิธีโต้ตอบกับฐานข้อมูล ซึ่งช่วยให้โมดูลฟีเจอร์ใช้ฐานข้อมูลใดก็ได้โดยไม่ต้อง ทราบรายละเอียดการใช้งานพื้นฐาน
โมดูลการติดตั้งใช้งานที่เฉพาะเจาะจงจะให้การติดตั้งใช้งานจริงของ API ที่กําหนดไว้ในโมดูลการแยกข้อมูล ในการดำเนินการดังกล่าว โมดูลการใช้งาน ยังขึ้นอยู่กับโมดูลการแยกข้อมูลด้วย
การแทรกการอ้างอิง
ตอนนี้คุณอาจสงสัยว่าโมดูลฟีเจอร์เชื่อมต่อกับโมดูลการใช้งานได้อย่างไร คำตอบคือการแทรกการอ้างอิง ฟีเจอร์ โมดูลไม่ได้สร้างอินสแตนซ์ฐานข้อมูลที่จำเป็นโดยตรง แต่จะ ระบุทรัพยากร Dependency ที่ต้องการแทน จากนั้นจะมีการจัดหาทรัพยากร Dependency เหล่านี้ ภายนอก ซึ่งมักจะอยู่ในโมดูลแอป
releaseImplementation(project(":database:impl:firestore"))
debugImplementation(project(":database:impl:room"))
androidTestImplementation(project(":database:impl:mock"))
ประโยชน์
การแยก API และการใช้งาน API มีประโยชน์ดังนี้
- การสับเปลี่ยน: การแยก API และโมดูลการติดตั้งใช้งานอย่างชัดเจนช่วยให้คุณพัฒนาการติดตั้งใช้งานหลายรายการสำหรับ API เดียวกัน และสลับไปมาระหว่างการติดตั้งใช้งานเหล่านั้นได้โดยไม่ต้องเปลี่ยนโค้ดที่ใช้ API ซึ่งอาจเป็นประโยชน์อย่างยิ่งในสถานการณ์ที่คุณต้องการมอบความสามารถหรือลักษณะการทำงานที่แตกต่างกันในบริบทต่างๆ เช่น การติดตั้งใช้งานจำลองสำหรับการทดสอบเทียบกับการติดตั้งใช้งานจริงสำหรับการใช้งานจริง
- การแยกส่วน: การแยกส่วนหมายความว่าโมดูลที่ใช้การแยกส่วนจะไม่ ขึ้นอยู่กับเทคโนโลยีใดๆ หากเลือกที่จะเปลี่ยนฐานข้อมูลจาก Room เป็น Firestore ในภายหลัง ก็จะทำได้ง่ายขึ้นเนื่องจากจะมีการเปลี่ยนแปลงเฉพาะในโมดูลที่เจาะจงซึ่งทำหน้าที่ดังกล่าว (โมดูลการติดตั้งใช้งาน) และจะไม่ส่งผลต่อโมดูลอื่นๆ ที่ใช้ API ของฐานข้อมูล
- ความสามารถในการทดสอบ: การแยก API ออกจากการติดตั้งใช้งานจะช่วยให้การทดสอบง่ายขึ้นมาก คุณเขียนกรณีทดสอบกับสัญญา API ได้ คุณยังใช้การติดตั้งใช้งานที่แตกต่างกันเพื่อทดสอบสถานการณ์ต่างๆ และกรณีขอบ รวมถึงการติดตั้งใช้งานจำลองได้ด้วย
- ประสิทธิภาพการสร้างที่ดีขึ้น: เมื่อแยก API และการ ใช้งานออกเป็นโมดูลต่างๆ การเปลี่ยนแปลงในโมดูล การใช้งานจะไม่บังคับให้ระบบบิลด์คอมไพล์โมดูลอีกครั้งโดยขึ้นอยู่กับโมดูล API ซึ่งจะช่วยให้สร้างได้เร็วขึ้นและเพิ่มประสิทธิภาพการทำงาน โดยเฉพาะในโปรเจ็กต์ขนาดใหญ่ที่อาจใช้เวลาสร้างนาน
กรณีที่ควรแยก
การแยก API ออกจากการใช้งานในกรณีต่อไปนี้จะเป็นประโยชน์
- ความสามารถที่หลากหลาย: หากคุณสามารถใช้ส่วนต่างๆ ของระบบได้หลายวิธี API ที่ชัดเจนจะช่วยให้สามารถสลับการใช้งานต่างๆ ได้ เช่น คุณอาจมีระบบการแสดงผลที่ใช้ OpenGL หรือ Vulkan หรือระบบการเรียกเก็บเงินที่ใช้ได้กับ Play หรือ API การเรียกเก็บเงินภายในองค์กร
- แอปพลิเคชันหลายรายการ: หากคุณกำลังพัฒนาแอปพลิเคชันหลายรายการที่มีความสามารถที่ใช้ร่วมกันสำหรับแพลตฟอร์มต่างๆ คุณสามารถกำหนด API ทั่วไปและพัฒนาการใช้งานที่เฉพาะเจาะจงต่อแพลตฟอร์มได้
- ทีมอิสระ: การแยกส่วนช่วยให้นักพัฒนาแอปหรือทีมต่างๆ สามารถ ทำงานในส่วนต่างๆ ของโค้ดเบสได้พร้อมกัน นักพัฒนาแอปควรให้ความสำคัญ กับการทำความเข้าใจสัญญา API และการใช้งานอย่างถูกต้อง โดยไม่ต้องกังวลเกี่ยวกับรายละเอียดการใช้งานของโมดูลอื่นๆ
- โค้ดเบสขนาดใหญ่: เมื่อโค้ดเบสมีขนาดใหญ่หรือซับซ้อน การแยก API ออกจากการติดตั้งใช้งานจะช่วยให้จัดการโค้ดได้ง่ายขึ้น ซึ่งช่วยให้คุณแบ่ง โค้ดเบสออกเป็นหน่วยที่ละเอียดยิ่งขึ้น เข้าใจได้ง่ายขึ้น และบำรุงรักษาได้
วิธีติดตั้งใช้งาน
หากต้องการใช้การผกผันการพึ่งพา ให้ทำตามขั้นตอนต่อไปนี้
- สร้างโมดูลการแยก: โมดูลนี้ควรมี API (อินเทอร์เฟซ และโมเดล) ที่กำหนดลักษณะการทำงานของฟีเจอร์
- สร้างโมดูลการติดตั้งใช้งาน: โมดูลการติดตั้งใช้งานควรใช้โมดูล API และใช้ลักษณะการทำงานของการแยกข้อมูล
รูปที่ 10 โมดูลการใช้งานขึ้นอยู่กับโมดูลการแยกส่วน - ทำให้โมดูลระดับสูงขึ้นอยู่กับโมดูลการแยกส่วน: ทำให้โมดูลขึ้นอยู่กับโมดูลการแยกส่วนแทนที่จะขึ้นอยู่กับการติดตั้งใช้งานที่เฉพาะเจาะจงโดยตรง
โมดูลระดับสูงไม่จำเป็นต้องทราบรายละเอียดการใช้งาน
เพียงแค่ต้องมีสัญญา (API)
รูปที่ 11 โมดูลระดับสูงขึ้นอยู่กับการแยกส่วน ไม่ใช่การใช้งาน - ระบุโมดูลการติดตั้งใช้งาน: สุดท้าย คุณต้องระบุการติดตั้งใช้งานจริงสำหรับ Dependency การติดตั้งใช้งานเฉพาะจะขึ้นอยู่กับการตั้งค่าโปรเจ็กต์ แต่โดยปกติแล้วโมดูลแอปจะเป็นตำแหน่งที่เหมาะสมในการดำเนินการนี้
หากต้องการระบุการติดตั้งใช้งาน ให้ระบุเป็นการขึ้นต่อกันสำหรับตัวแปรบิลด์หรือชุดแหล่งที่มาสำหรับการทดสอบที่เลือก
รูปที่ 12 โมดูลแอปจะมีการติดตั้งใช้งานจริง
แนวทางปฏิบัติแนะนำโดยทั่วไป
ดังที่กล่าวไว้ในตอนต้นว่าไม่มีวิธีที่ถูกต้องที่สุดในการพัฒนาแอปแบบหลายโมดูล เช่นเดียวกับที่มีสถาปัตยกรรมซอฟต์แวร์มากมาย ก็มีวิธีแยกแอปออกเป็นโมดูลได้หลายวิธีเช่นกัน อย่างไรก็ตาม คำแนะนำทั่วไปต่อไปนี้จะช่วยให้คุณอ่านโค้ดได้ง่ายขึ้น บำรุงรักษาได้ง่ายขึ้น และทดสอบได้ง่ายขึ้น
รักษาการกำหนดค่าให้สอดคล้องกัน
ทุกโมดูลจะทำให้เกิดค่าใช้จ่ายในการกำหนดค่า หากจำนวนโมดูลของคุณ ถึงเกณฑ์ที่กำหนด การจัดการการกำหนดค่าที่สอดคล้องกันจะกลายเป็น ความท้าทาย เช่น โมดูลควรใช้การอ้างอิงเวอร์ชันเดียวกัน หากคุณต้องอัปเดตโมดูลจำนวนมากเพียงเพื่ออัปเดตเวอร์ชันของ การอ้างอิง ก็ไม่ใช่แค่ความพยายามเท่านั้น แต่ยังอาจเกิด ข้อผิดพลาดได้ด้วย หากต้องการแก้ปัญหานี้ คุณสามารถใช้เครื่องมืออย่างใดอย่างหนึ่งของ Gradle เพื่อ รวมการกำหนดค่าไว้ที่เดียวได้
- แคตตาล็อกเวอร์ชันคือรายการที่ปลอดภัยของทรัพยากร Dependency ที่ Gradle สร้างขึ้นระหว่างการซิงค์ เป็นศูนย์กลางในการประกาศการอ้างอิงทั้งหมด และพร้อมใช้งานสำหรับทุกโมดูลในโปรเจ็กต์
- ใช้ปลั๊กอินตามธรรมเนียมเพื่อแชร์ตรรกะการสร้างระหว่างโมดูล
เปิดเผยข้อมูลให้น้อยที่สุด
อินเทอร์เฟซสาธารณะของโมดูลควรมีขนาดเล็กที่สุดและแสดงเฉพาะ
สิ่งจำเป็นเท่านั้น และไม่ควรเปิดเผยรายละเอียดการใช้งานภายนอก จำกัดขอบเขต
ทุกอย่างให้แคบที่สุดเท่าที่จะเป็นไปได้ ใช้ขอบเขตการมองเห็น private หรือ internal
ของ Kotlin เพื่อให้โมดูลการประกาศเป็นแบบส่วนตัว เมื่อประกาศ
การอ้างอิงในโมดูล ให้ใช้ implementation แทน api ส่วนตัวเลือกหลัง
จะแสดงทรัพยากร Dependency แบบทรานซิทีฟต่อผู้ใช้โมดูล การใช้
การติดตั้งใช้งานอาจช่วยปรับปรุงเวลาบิลด์ได้เนื่องจากจะลดจำนวนโมดูล
ที่ต้องสร้างใหม่
ชอบใช้โมดูล Kotlin และ Java
Android Studio รองรับโมดูลที่จำเป็น 3 ประเภท ได้แก่
- โมดูลแอปคือจุดแรกเข้าของแอปพลิเคชัน โดยอาจมี
ซอร์สโค้ด ทรัพยากร ชิ้นงาน และ
AndroidManifest.xmlเอาต์พุตของ โมดูลแอปคือ Android App Bundle (AAB) หรือแพ็กเกจแอปพลิเคชัน Android (APK) - โมดูลไลบรารีมีเนื้อหาเหมือนกับโมดูลแอป โมดูล Android อื่นๆ ใช้เป็นทรัพยากร Dependency เอาต์พุตของโมดูลไลบรารี คือ Android Archive (AAR) ซึ่งมีโครงสร้างเหมือนกับโมดูลแอป แต่จะ คอมไพล์เป็นไฟล์ Android Archive (AAR) ซึ่งโมดูลอื่นๆ สามารถใช้เป็นทรัพยากร Dependency ได้ในภายหลัง โมดูลไลบรารีช่วยให้คุณ แคปซูลและนำตรรกะและทรัพยากรเดียวกันกลับมาใช้ใหม่ในโมดูลแอปหลายโมดูลได้
- ไลบรารี Kotlin และ Java ไม่มีทรัพยากร Android, ชิ้นงาน หรือ ไฟล์ Manifest
เนื่องจากโมดูล Android มีค่าใช้จ่ายเพิ่มเติม คุณจึงควรใช้โมดูล Kotlin หรือ Java ให้มากที่สุด