เชื่อมชั้นต่าง ๆ เข้าด้วยกันโดยไม่ใช้เฟรมเวิร์ก DI
ประกอบอ็อบเจ็กต์ที่จุดเริ่มต้นของแอปด้วยรูปแบบรากการประกอบ
เชื่อมชั้นต่าง ๆ เข้าด้วยกันโดยไม่ใช้เฟรมเวิร์ก DI เป็นบทเรียน Swift Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Swift Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน
จุดประกอบระบบ
จุดประกอบระบบคือตำแหน่งเดียวที่สร้างอ็อบเจ็กต์ทั้งหมดและเชื่อมโยงเข้าด้วยกัน ในแอป SwiftUI โดยทั่วไปจะอยู่ที่ @main
@main
struct MyApp: App {
// All wiring happens here
private let container = AppContainer()
var body: some Scene {
WindowGroup { container.makeRootView() }
}
}รูปแบบ AppContainer
โครงสร้างหรือคลาส AppContainer จะสร้างกราฟอ็อบเจ็กต์และซ่อนความซับซ้อนของการเชื่อมโยงจากโครงสร้างแอป
final class AppContainer {
let db = SQLiteDatabase()
lazy var userRepo: UserRepository = RemoteUserRepository(db: db)
lazy var fetchUser = FetchUserUseCase(repo: userRepo)
lazy var profileVM = ProfileViewModel(useCase: fetchUser)
func makeRootView() -> some View { ProfileView(viewModel: profileVM) }
}คุณสมบัติแบบขี้เกียจเพื่อจัดลำดับ
ใช้คุณสมบัติ lazy เพื่อให้แน่ใจว่าการพึ่งพาจะถูกสร้างตามลำดับที่ถูกต้องโดยไม่ต้องจัดลำดับด้วยตนเอง
final class Container {
lazy var network: NetworkClient = URLSessionClient()
lazy var api: APIService = APIService(client: network) // created after network
lazy var repo: ItemRepository = RemoteItemRepository(api: api)
}เมธอดโรงงาน
เพิ่มเมธอดโรงงานให้คอนเทนเนอร์เพื่อสร้างอ็อบเจ็กต์ระดับมุมมองที่มีการพึ่งพาเฉพาะของแต่ละหน้าจอ
extension AppContainer {
func makeOrderFlow() -> OrderFlowView {
let repo = RemoteOrderRepository(api: api)
let useCase = PlaceOrderUseCase(repo: repo)
let vm = OrderViewModel(useCase: useCase)
return OrderFlowView(viewModel: vm)
}
}ค่าสภาพแวดล้อมสำหรับการฉีดการพึ่งพา
ใน SwiftUI ให้ใช้ EnvironmentKey แบบกำหนดเองเพื่อฉีดการพึ่งพาเข้าไปลึกในต้นไม้มุมมองโดยไม่ต้องส่งต่อผ่านคุณสมบัติทุกชั้น
private struct FetchUserKey: EnvironmentKey {
static let defaultValue: FetchingUser = FetchUserUseCase(repo: NullUserRepo())
}
extension EnvironmentValues {
var fetchUser: FetchingUser {
get { self[FetchUserKey.self] } set { self[FetchUserKey.self] = newValue }
}
}การใช้คีย์สภาพแวดล้อม
ฉีดการพึ่งพาด้วย .environment(\.fetchUser, container.fetchUser) และอ่านค่าด้วย @Environment(\.fetchUser) ในมุมมอง
ContentView()
.environment(\.fetchUser, container.fetchUser)
// In a deep view:
@Environment(\.fetchUser) private var fetchUserคอนเทนเนอร์แบบกำหนดขอบเขต
สร้างคอนเทนเนอร์ลูกสำหรับขอบเขตของฟีเจอร์ โดยสืบทอดการพึ่งพาที่ใช้ร่วมกันและเป็นเจ้าของการพึ่งพาเฉพาะของฟีเจอร์นั้น
final class CheckoutContainer {
let cart: CartRepository
let payment: PaymentService
lazy var placeOrder = PlaceOrderUseCase(cart: cart, payment: payment)
init(parent: AppContainer) {
cart = parent.cartRepo
payment = parent.paymentService
}
}การทดสอบจุดประกอบระบบ
ทดสอบเมธอดโรงงานของคอนเทนเนอร์โดยตรวจสอบว่าอ็อบเจ็กต์ที่ส่งคืนมีชนิดและการกำหนดค่าที่ถูกต้อง
func testContainerBuildsProfileView() {
let container = AppContainer(db: InMemoryDatabase())
let view = container.makeRootView()
XCTAssertTrue(type(of: view) == ProfileView.self)
}การหลีกเลี่ยงการพึ่งพาแบบวนรอบ
หาก A พึ่งพา B และ B พึ่งพา A ให้เพิ่มตัวกลางหรือจัดโครงสร้างความรับผิดชอบใหม่เพื่อทำลายวงจร
// Circular:
// UserService → NotificationService → UserService
// Fix: UserService → NotificationInput (protocol) ← NotificationServiceเมื่อใดควรเลือกใช้กรอบงาน DI
การเชื่อมโยงด้วยตนเองรองรับแอปขนาดกลางได้เป็นอย่างดี ควรพิจารณากรอบงาน เช่น นีดเดิลและสวินเจกต์ เฉพาะเมื่อคอนเทนเนอร์มีขนาดใหญ่เกินกว่าจะดูแลรักษาได้
// Manual: fine for <20 top-level dependencies
// Framework: helps with large teams, 50+ services, code-generation for factoriesตัวอย่างสรุป
ตัวอย่างการเชื่อมโยงทั้งหมดในแอปขนาดเล็ก: คอนเทนเนอร์สร้างการพึ่งพาทั้งหมด และโรงงานสร้างมุมมองราก
final class AppContainer {
private let db = CoreDataStack.shared
lazy var userRepo: UserRepository = LocalUserRepository(db: db)
lazy var fetchUser = FetchUserUseCase(repo: userRepo)
func makeContentView() -> ContentView {
ContentView(viewModel: ProfileViewModel(useCase: fetchUser))
}
}ตรวจสอบความเข้าใจอย่างรวดเร็ว
จุดประกอบระบบในแอปสวิฟต์คืออะไร
สรุปบทเรียน
เชื่อมโยงแอปของคุณที่จุดประกอบระบบ ซึ่งโดยปกติคือ @main ใช้ AppContainer ที่มีคุณสมบัติ lazy เพื่อสร้างสิ่งต่าง ๆ ตามลำดับที่ถูกต้อง เพิ่มเมธอดโรงงานสำหรับกราฟการพึ่งพาของแต่ละหน้าจอ ฉีดการพึ่งพาที่อยู่ลึกลงไปด้วย EnvironmentKey แบบกำหนดเอง และทำลายการพึ่งพาแบบวนรอบด้วยโปรโตคอลตัวกลาง
คำถามที่พบบ่อย
บทเรียน “เชื่อมชั้นต่าง ๆ เข้าด้วยกันโดยไม่ใช้เฟรมเวิร์ก DI” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “เชื่อมชั้นต่าง ๆ เข้าด้วยกันโดยไม่ใช้เฟรมเวิร์ก DI” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Swift Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “เชื่อมชั้นต่าง ๆ เข้าด้วยกันโดยไม่ใช้เฟรมเวิร์ก DI”
ประกอบอ็อบเจ็กต์ที่จุดเริ่มต้นของแอปด้วยรูปแบบรากการประกอบ คุณปฏิบัติ Swift Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Swift Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Swift Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “เชื่อมชั้นต่าง ๆ เข้าด้วยกันโดยไม่ใช้เฟรมเวิร์ก DI” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Swift Academy นี้ได้ไหม
ได้ บทเรียน Swift Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- ชั้นต่าง ๆ: โดเมน ข้อมูล และการนำเสนอ
- รูปแบบกรณีใช้งานและคลังข้อมูล
- การกลับทิศทางการพึ่งพาด้วยนามธรรมของโพรโทคอล
- เชื่อมชั้นต่าง ๆ เข้าด้วยกันโดยไม่ใช้เฟรมเวิร์ก DI