0Pricing
Android Academy · บทเรียน

การจัดการการพึ่งพาระหว่างโมดูล

รักษากราฟให้สะอาดและไม่มีวงจร

การจัดการการพึ่งพาระหว่างโมดูล เป็นบทเรียน Android Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Android Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Android Academy มีบทเรียนทั้งหมด 4 บทเรียน

กราฟการขึ้นต่อ

ทุกโมดูลจะประกาศว่าโมดูลอื่นใดที่ตนขึ้นต่อ โมดูลทั้งหมดนี้รวมกันเป็น กราฟการขึ้นต่อ กราฟที่ดีควรเป็น DAG (กราฟมีทิศทางที่ไม่มีวงจร): การขึ้นต่อจะชี้ไปในทิศทางเดียวและไม่ก่อให้เกิดลูป

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

ประกาศการขึ้นต่อของโมดูล

คุณเพิ่มการขึ้นต่อไปยังโมดูลอื่นด้วย project(":path:to:module") ภายในบล็อก dependencies เส้นทางนี้จะสะท้อนโครงสร้างโฟลเดอร์และตรงกับสิ่งที่คุณประกาศไว้ใน settings.gradle.kts

// feature/profile/build.gradle.kts
dependencies {
    implementation(project(":core:data"))
    implementation(project(":core:designsystem"))
    implementation(project(":core:model"))
}

implementation กับ api

การกำหนดค่าที่คุณเลือกจะควบคุมว่าอะไรจะ รั่วไหลไปยังผู้ใช้โมดูล:

  • implementation: การขึ้นต่อเป็น ส่วนตัว โมดูลที่ขึ้นต่อกับคุณจะมองไม่เห็นสิ่งนี้ นี่เป็นตัวเลือกเริ่มต้น
  • api: การขึ้นต่อจะถูก เปิดเผยต่อ (ส่งต่อแบบถ่ายทอด) ใช้เฉพาะเมื่อชนิดข้อมูลสาธารณะของคุณมาจากการขึ้นต่อนั้น

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

// core/data/build.gradle.kts
dependencies {
    // Repository signatures return :core:model types,
    // so consumers need to SEE it -> api
    api(project(":core:model"))

    // Network is an internal detail -> implementation
    implementation(project(":core:network"))
}

เหตุผลที่ implementation ช่วยให้สร้างได้เร็วขึ้น

เมื่อใช้ implementation Gradle จะทราบว่าการเปลี่ยนแปลงในการขึ้นต่อที่ซ่อนอยู่ไม่สามารถส่งผลต่อ ABI สาธารณะของโมดูลได้ ดังนั้นผู้ใช้โมดูลจึงไม่ต้องคอมไพล์ใหม่ แต่เมื่อใช้ api การเปลี่ยนแปลงจะส่งผลต่อเนื่องไปยังผู้ใช้โมดูลที่ขึ้นต่อแบบถ่ายทอดทุกตัว

หลักง่าย ๆ คือ ให้ใส่การขึ้นต่อใน api เฉพาะเมื่อการขึ้นต่อนั้นปรากฏในชนิดข้อมูล สาธารณะ ของโมดูล (ชนิดข้อมูลที่ส่งคืนหรือพารามิเตอร์สาธารณะ) หากไม่ใช่ ให้ใช้ implementation

// Public -> needs api
fun observeUser(): Flow<User>   // Flow and User leak out

// Internal -> implementation is enough
private val client: OkHttpClient // never exposed

รวมศูนย์รุ่น: แค็ตตาล็อกรุ่น

เมื่อมีหลายโมดูล คุณคงไม่ต้องการระบุรุ่นของไลบรารีซ้ำไปทั่ว Gradle แค็ตตาล็อกรุ่น (gradle/libs.versions.toml) จะกำหนดรุ่นและชื่อแทนไว้เพียงครั้งเดียว ทุกโมดูลจะอ้างอิงชื่อแทนเดียวกัน

# gradle/libs.versions.toml
[versions]
compose-bom = "2024.09.00"
retrofit = "2.11.0"

[libraries]
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }

ใช้แค็ตตาล็อกในโมดูล

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

// core/network/build.gradle.kts
dependencies {
    implementation(platform(libs.compose.bom))
    implementation(libs.retrofit)
}

บาปมหันต์: วงจร

วงจร เกิดขึ้นเมื่อโมดูล A ขึ้นต่อกับ B และ B ขึ้นต่อกลับมายัง A (โดยตรงหรือผ่านสายโซ่) Gradle ปฏิเสธการสร้างการขึ้นต่อแบบวงกลม นอกจากนี้ยังบ่งชี้ว่ามีปัญหาด้านการออกแบบ เพราะขอบเขตระหว่างสองโมดูลไม่ถูกต้อง

// :feature:cart  -> implementation(project(":feature:checkout"))
// :feature:checkout -> implementation(project(":feature:cart"))
//
// Gradle error:
// Circular dependency between the following tasks:
// :feature:cart:compile -> :feature:checkout:compile -> :feature:cart:compile

แก้ไขวงจร

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

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

// Before: cart <-> checkout (cycle)
// After:  cart -> :core:order  <-  checkout

// core/order/OrderContract.kt
data class Order(val items: List<CartItem>, val total: Double)

// feature/cart    -> implementation(project(":core:order"))
// feature/checkout-> implementation(project(":core:order"))

การผกผัน: ขึ้นต่อกับนามธรรม

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

// core/analytics defines the contract
interface AnalyticsLogger {
    fun log(event: String)
}

// :app provides the real implementation and injects it down
@Module
@InstallIn(SingletonComponent::class)
object AnalyticsModule {
    @Provides
    fun logger(impl: FirebaseAnalyticsLogger): AnalyticsLogger = impl
}

แสดงภาพและป้องกันกราฟ

คุณสามารถสั่งให้ Gradle พิมพ์หรือแสดงผลกราฟของโมดูล และยังเพิ่มการทดสอบที่ ทำให้การสร้างล้มเหลว หากพบการขึ้นต่อที่ไม่อนุญาต (เช่น โมดูลแกนกลางขึ้นต่อกับฟีเจอร์) เครื่องมืออย่างปลั๊กอิน module-graph สามารถสร้างแผนภาพให้อัตโนมัติ

# Print the project structure
./gradlew projects

# Inspect why :feature:home pulls in a library
./gradlew :feature:home:dependencies --configuration debugRuntimeClasspath

ตัวอย่างที่สะอาดและไม่มีวงจร

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

// :app
//   -> :feature:home   -> :core:data -> :core:network -> :core:model
//   -> :feature:profile -> :core:data -> :core:database -> :core:model
//   -> :core:designsystem
//
// Every path ends at :core:model. No cycles. Builds in parallel.

ตรวจสอบอย่างรวดเร็ว

โมดูล :core:network ของคุณใช้ OkHttpClient ภายในเท่านั้น โดยไม่เคยปรากฏในลายเซ็นฟังก์ชันสาธารณะใดเลย คุณควรใช้การกำหนดค่า Gradle แบบใดเพื่อประกาศการขึ้นต่อกับ OkHttp

สรุป: การจัดการการขึ้นต่อระหว่างโมดูล

คุณได้เรียนรู้วิธีรักษากราฟของโมดูลให้มีสุขภาพดี:

  • ประกาศการขึ้นต่อของโมดูลด้วย project(":path")
  • ใช้ implementation เป็นค่าเริ่มต้น และใช้ api เฉพาะกับชนิดข้อมูลที่อยู่ในส่วนติดต่อสาธารณะ
  • รวมศูนย์รุ่นไว้ใน แค็ตตาล็อกรุ่น (libs.versions.toml)
  • อย่าสร้าง วงจร เด็ดขาด เพราะ Gradle จะปฏิเสธวงจรเหล่านี้ ให้แก้ด้วยการแยกโค้ดที่ใช้ร่วมกันลงด้านล่าง หรือผกผันการขึ้นต่อด้วยส่วนติดต่อ

ถัดไป คุณจะเชื่อมฟีเจอร์ต่าง ๆ เข้าหากันผ่านการนำทางโดยไม่ทำให้ฟีเจอร์ขึ้นต่อกัน

คำถามที่พบบ่อย

บทเรียน “การจัดการการพึ่งพาระหว่างโมดูล” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การจัดการการพึ่งพาระหว่างโมดูล” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Android Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Android Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การจัดการการพึ่งพาระหว่างโมดูล”

รักษากราฟให้สะอาดและไม่มีวงจร คุณปฏิบัติ Android Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Android Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Android Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “การจัดการการพึ่งพาระหว่างโมดูล” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Android Academy นี้ได้ไหม

ได้ บทเรียน Android Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. เหตุใดจึงควรแยกเป็นโมดูล
  2. โมดูลฟีเจอร์และโมดูลแกนกลาง
  3. การจัดการการพึ่งพาระหว่างโมดูล
  4. การนำทางข้ามโมดูล
← กลับไปที่ Android Academy