รูปแบบการแยกส่วนที่พบบ่อย

ไม่มีกลยุทธ์การแยกส่วนเพียงอย่างเดียวที่เหมาะกับทุกโปรเจ็กต์ เนื่องจาก Gradle มีความยืดหยุ่น จึงมีข้อจำกัดเพียงเล็กน้อยเกี่ยวกับวิธีจัดระเบียบโปรเจ็กต์ หน้านี้จะให้ภาพรวมของกฎทั่วไปและรูปแบบที่พบบ่อยบางอย่างที่คุณสามารถใช้เมื่อพัฒนาแอป Android แบบหลายโมดูล

หลักการเชื่อมโยงสูงและการเชื่อมโยงต่ำ

วิธีหนึ่งในการระบุลักษณะของโค้ดเบสแบบแยกส่วนคือการใช้พร็อพเพอร์ตี้การเชื่อมโยง และความเหนียวแน่น การคัปปลิ้งจะวัดระดับที่โมดูล ขึ้นอยู่กับโมดูลอื่น ความเชื่อมโยงในบริบทนี้จะวัดว่าองค์ประกอบของโมดูลเดียวมีความเกี่ยวข้องในเชิงฟังก์ชันอย่างไร โดยทั่วไปแล้ว คุณควรพยายามให้ การเชื่อมโยงต่ำและความเหนียวแน่นสูง

  • การเชื่อมโยงต่ำหมายความว่าโมดูลควรเป็นอิสระจากกันมากที่สุด เพื่อให้การเปลี่ยนแปลงในโมดูลหนึ่งส่งผลกระทบต่อโมดูลอื่นๆ น้อยที่สุดหรือไม่มีเลย โมดูลไม่ควรทราบการทำงานภายในของโมดูลอื่นๆ
  • การยึดเหนี่ยวสูงหมายความว่าโมดูลควรประกอบด้วยชุดโค้ดที่ ทำหน้าที่เป็นระบบ โดยควรมีหน้าที่รับผิดชอบที่ชัดเจนและอยู่ภายในขอบเขตของความรู้ในโดเมนที่เฉพาะเจาะจง ลองพิจารณาแอปพลิเคชัน eBook ตัวอย่าง การรวมโค้ดที่เกี่ยวข้องกับหนังสือและการชำระเงินไว้ในโมดูลเดียวกันอาจไม่เหมาะสม เนื่องจากเป็นโดเมนฟังก์ชันการทำงานที่แตกต่างกัน

ประเภทของโมดูล

วิธีจัดระเบียบโมดูลจะขึ้นอยู่กับสถาปัตยกรรมของแอปเป็นหลัก ด้านล่างนี้ คือโมดูลประเภทที่พบบ่อยซึ่งคุณอาจนำมาใช้ในแอปขณะที่ทำตามสถาปัตยกรรมแอปที่แนะนำ

โมดูลข้อมูล

โดยปกติแล้วโมดูลข้อมูลจะมีที่เก็บ แหล่งข้อมูล และคลาสโมเดล ความรับผิดชอบหลัก 3 ประการของโมดูลข้อมูลมีดังนี้

  1. ห่อหุ้มข้อมูลและตรรกะทางธุรกิจทั้งหมดของโดเมนหนึ่งๆ: โมดูลข้อมูลแต่ละโมดูลควรมีหน้าที่จัดการข้อมูลที่แสดงถึงโดเมนหนึ่งๆ โดยจะจัดการข้อมูลได้หลายประเภทตราบใดที่ข้อมูลนั้นเกี่ยวข้อง
  2. เปิดเผยที่เก็บเป็น API ภายนอก: API สาธารณะของโมดูลข้อมูลควรเป็นที่เก็บเนื่องจากมีหน้าที่รับผิดชอบในการเปิดเผยข้อมูลต่อส่วนอื่นๆ ของแอป
  3. ซ่อนรายละเอียดการติดตั้งใช้งานและแหล่งข้อมูลทั้งหมดจากภายนอก ที่เก็บควรเข้าถึงแหล่งข้อมูลได้จากโมดูลเดียวกันเท่านั้น โดยจะยังคงซ่อนไว้จากภายนอก คุณบังคับใช้ได้โดยใช้คีย์เวิร์ดการมองเห็น private หรือ internal ของ Kotlin
รูปที่ 1 โมดูลข้อมูลตัวอย่างและเนื้อหา

โมดูลฟีเจอร์

ฟีเจอร์คือส่วนที่แยกออกมาของฟังก์ชันการทำงานของแอป ซึ่งมักจะสอดคล้องกับหน้าจอหรือชุดหน้าจอที่เกี่ยวข้องอย่างใกล้ชิด เช่น ขั้นตอนการลงชื่อสมัครใช้หรือชำระเงิน หากแอปมีการนำทางด้วยแถบด้านล่าง ปลายทางแต่ละแห่ง น่าจะเป็นฟีเจอร์

รูปที่ 2 แต่ละแท็บของแอปพลิเคชันนี้สามารถกำหนดเป็นฟีเจอร์ได้

ฟีเจอร์จะเชื่อมโยงกับหน้าจอหรือปลายทางในแอป ดังนั้น จึงมีแนวโน้มที่จะมี UI ที่เชื่อมโยงและViewModelเพื่อจัดการตรรกะ และสถานะ ฟีเจอร์เดียวไม่จำเป็นต้องจำกัดไว้ที่มุมมองเดียวหรือ ปลายทางการนำทางเดียว โมดูลฟีเจอร์ขึ้นอยู่กับโมดูลข้อมูล

รูปที่ 3 ตัวอย่างโมดูลฟีเจอร์และเนื้อหาของโมดูล

โมดูลแอป

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

รูปที่ 4 กราฟการขึ้นต่อกันของโมดูล Product Flavor *Demo* และ *Full*

หากแอปของคุณกำหนดเป้าหมายอุปกรณ์หลายประเภท เช่น Android Auto, Wear หรือ TV ให้กำหนดโมดูลแอปสำหรับแต่ละประเภท ซึ่งจะช่วยแยกการอ้างอิงที่เฉพาะเจาะจงแพลตฟอร์ม

รูปที่ 5 กราฟการขึ้นต่อกันของแอป Android Auto

โมดูลทั่วไป

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

  • โมดูล UI: หากคุณใช้องค์ประกอบ UI ที่กำหนดเองหรือการสร้างแบรนด์ที่ซับซ้อนในแอป คุณควรพิจารณาห่อหุ้มคอลเล็กชันวิดเจ็ตไว้ในโมดูล เพื่อให้ฟีเจอร์ทั้งหมดนำกลับมาใช้ใหม่ได้ ซึ่งจะช่วยให้ UI สอดคล้องกันในฟีเจอร์ต่างๆ เช่น หากการกำหนดธีมของคุณรวมศูนย์ คุณก็หลีกเลี่ยง การปรับโครงสร้างที่เจ็บปวดได้เมื่อมีการเปลี่ยนแบรนด์
  • โมดูล Analytics: การติดตามมักกำหนดโดยข้อกำหนดทางธุรกิจโดย ไม่ค่อยคำนึงถึงสถาปัตยกรรมซอฟต์แวร์ โดยมักใช้เครื่องมือติดตาม Analytics ในคอมโพเนนต์ที่ไม่เกี่ยวข้องหลายอย่าง หากเป็นกรณีของคุณ คุณอาจ ต้องมีโมดูลข้อมูลวิเคราะห์เฉพาะ
  • โมดูลเครือข่าย: เมื่อโมดูลจำนวนมากต้องใช้การเชื่อมต่อเครือข่าย คุณอาจ พิจารณาใช้โมดูลที่ทําหน้าที่เป็นไคลเอ็นต์ http โดยเฉพาะ ซึ่งมีประโยชน์อย่างยิ่งเมื่อไคลเอ็นต์ของคุณต้องการการกำหนดค่าที่กำหนดเอง
  • โมดูลยูทิลิตี: ยูทิลิตีหรือที่เรียกว่าตัวช่วยมักเป็นโค้ดขนาดเล็ก ที่นำกลับมาใช้ใหม่ในแอปพลิเคชัน ตัวอย่างของยูทิลิตี ได้แก่ ตัวช่วยในการทดสอบ ฟังก์ชันการจัดรูปแบบสกุลเงิน โปรแกรมตรวจสอบอีเมล หรือ ตัวดำเนินการที่กำหนดเอง

โมดูลทดสอบ

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

กรณีการใช้งานสำหรับโมดูลทดสอบ

ตัวอย่างต่อไปนี้แสดงสถานการณ์ที่การติดตั้งใช้งานโมดูลทดสอบ อาจเป็นประโยชน์อย่างยิ่ง

  • โค้ดทดสอบที่แชร์: หากมีหลายโมดูลในโปรเจ็กต์และโค้ดทดสอบบางส่วนใช้ได้กับโมดูลมากกว่า 1 โมดูล คุณสามารถสร้างโมดูลทดสอบเพื่อแชร์โค้ดได้ ซึ่งจะช่วยลดการทำซ้ำและทำให้โค้ดทดสอบ ดูแลรักษาง่ายขึ้น โค้ดทดสอบที่แชร์อาจรวมถึงคลาสหรือฟังก์ชันยูทิลิตี เช่น การยืนยันหรือตัวจับคู่ที่กำหนดเอง รวมถึงข้อมูลทดสอบ เช่น การตอบกลับ JSON ที่จำลอง

  • การกำหนดค่าบิลด์ที่สะอาดขึ้น: โมดูลทดสอบช่วยให้คุณมีการกำหนดค่าบิลด์ที่สะอาดขึ้น เนื่องจากมีไฟล์ build.gradle ของตัวเองได้ คุณไม่จำเป็นต้องใส่การกำหนดค่าที่เกี่ยวข้องกับการทดสอบเท่านั้นไว้ในไฟล์ build.gradle ของโมดูลแอป

  • การทดสอบการผสานรวม: โมดูลทดสอบสามารถใช้เพื่อจัดเก็บการทดสอบการผสานรวมที่ใช้เพื่อทดสอบการโต้ตอบระหว่างส่วนต่างๆ ของแอป รวมถึงอินเทอร์เฟซผู้ใช้ ตรรกะทางธุรกิจ คำขอเครือข่าย และการค้นหาฐานข้อมูล

  • แอปพลิเคชันขนาดใหญ่: โมดูลการทดสอบมีประโยชน์อย่างยิ่งสำหรับ แอปพลิเคชันขนาดใหญ่ที่มีฐานโค้ดที่ซับซ้อนและมีหลายโมดูล ในกรณีดังกล่าว โมดูลทดสอบจะช่วยปรับปรุงการจัดระเบียบโค้ดและความสามารถในการบำรุงรักษาได้

รูปที่ 6 ใช้โมดูลทดสอบเพื่อแยกโมดูลที่อาจต้องพึ่งพาซึ่งกันและกัน

การสื่อสารระหว่างโมดูล

โดยปกติแล้วโมดูลมักไม่ได้แยกกันอย่างสิ้นเชิง และมักต้องอาศัยโมดูลอื่นๆ และสื่อสารกับโมดูลเหล่านั้น คุณควรลดการเชื่อมโยงให้ต่ำแม้ว่าโมดูลจะทำงานร่วมกันและแลกเปลี่ยนข้อมูลบ่อยก็ตาม บางครั้งการสื่อสารโดยตรงระหว่าง 2 โมดูลอาจไม่เป็นที่ต้องการ เช่น ในกรณีที่มีข้อจำกัดด้านสถาปัตยกรรม หรืออาจเป็นไปไม่ได้ เช่น ในกรณีที่มีการอ้างอิงแบบวงกลม

รูปที่ 7 การสื่อสารแบบ 2 ทางโดยตรงระหว่างโมดูลเป็นไปไม่ได้เนื่องจากมีการอ้างอิงแบบวงกลม คุณต้องมีโมดูลสื่อกลางเพื่อประสานงานการไหลของข้อมูลระหว่างโมดูลอิสระอื่นๆ 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 โมดูลฟีเจอร์ขึ้นอยู่กับโมดูลข้อมูลเดียวกัน ซึ่งจะช่วยลดปริมาณข้อมูลที่โมดูลตัวกลางต้องส่งต่อ และลดการเชื่อมโยงระหว่างโมดูล โมดูลควรแลกเปลี่ยนรหัสดั้งเดิมและโหลดทรัพยากรจากโมดูลข้อมูลที่ใช้ร่วมกันแทนการส่งออบเจ็กต์

รูปที่ 8 โมดูลฟีเจอร์ 2 โมดูลที่ใช้โมดูลข้อมูลที่แชร์

การผกผันการขึ้นต่อกัน

การผกผันการพึ่งพาคือการจัดระเบียบโค้ดเพื่อให้การแยกส่วนเป็น อิสระจากการใช้งานจริง

  • การแยกส่วน: สัญญาที่กำหนดวิธีที่คอมโพเนนต์หรือโมดูลในแอปพลิเคชันของคุณ โต้ตอบกัน โมดูลการแยกส่วนจะกำหนด API ของ ระบบและมีอินเทอร์เฟซและโมเดล
  • การติดตั้งใช้งานจริง: โมดูลที่ขึ้นอยู่กับโมดูลการแยกข้อมูล และติดตั้งใช้งานลักษณะการทำงานของการแยกข้อมูล

โมดูลที่อิงตามลักษณะการทำงานที่กำหนดไว้ในโมดูลการแยกส่วนควรขึ้นอยู่กับการแยกส่วนเองเท่านั้น ไม่ใช่การติดตั้งใช้งานที่เฉพาะเจาะจง

รูปที่ 9 แทนที่จะใช้โมดูลระดับสูงที่ขึ้นอยู่กับโมดูลระดับต่ำโดยตรง โมดูลระดับสูงและโมดูลการติดตั้งใช้งานจะขึ้นอยู่กับโมดูลการแยกส่วน

ตัวอย่าง

ลองนึกถึงโมดูลฟีเจอร์ที่ต้องใช้ฐานข้อมูลในการทำงาน โมดูลฟีเจอร์ไม่เกี่ยวข้องกับวิธีที่ใช้ฐานข้อมูล ไม่ว่าจะเป็นฐานข้อมูล 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 ออกจากการติดตั้งใช้งานจะช่วยให้จัดการโค้ดได้ง่ายขึ้น ซึ่งช่วยให้คุณแบ่ง โค้ดเบสออกเป็นหน่วยที่ละเอียดยิ่งขึ้น เข้าใจได้ง่ายขึ้น และบำรุงรักษาได้

วิธีติดตั้งใช้งาน

หากต้องการใช้การผกผันการพึ่งพา ให้ทำตามขั้นตอนต่อไปนี้

  1. สร้างโมดูลการแยก: โมดูลนี้ควรมี API (อินเทอร์เฟซ และโมเดล) ที่กำหนดลักษณะการทำงานของฟีเจอร์
  2. สร้างโมดูลการติดตั้งใช้งาน: โมดูลการติดตั้งใช้งานควรใช้โมดูล API และใช้ลักษณะการทำงานของการแยกข้อมูล
    แทนที่จะให้โมดูลระดับสูงขึ้นอยู่กับโมดูลระดับต่ำโดยตรง โมดูลระดับสูงและโมดูลการติดตั้งใช้งานจะขึ้นอยู่กับโมดูลการแยกส่วน
    รูปที่ 10 โมดูลการใช้งานขึ้นอยู่กับโมดูลการแยกส่วน
  3. ทำให้โมดูลระดับสูงขึ้นอยู่กับโมดูลการแยกส่วน: ทำให้โมดูลขึ้นอยู่กับโมดูลการแยกส่วนแทนที่จะขึ้นอยู่กับการติดตั้งใช้งานที่เฉพาะเจาะจงโดยตรง โมดูลระดับสูงไม่จำเป็นต้องทราบรายละเอียดการใช้งาน เพียงแค่ต้องมีสัญญา (API)
    โมดูลระดับสูงจะขึ้นอยู่กับการแยกส่วน ไม่ใช่การใช้งาน
    รูปที่ 11 โมดูลระดับสูงขึ้นอยู่กับการแยกส่วน ไม่ใช่การใช้งาน
  4. ระบุโมดูลการติดตั้งใช้งาน: สุดท้าย คุณต้องระบุการติดตั้งใช้งานจริงสำหรับ 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 ให้มากที่สุด