Inversi Dependensi dalam Praktik
Hubungkan adapter ke inti dengan inversi dependensi.
Inversi Dependensi dalam Praktik adalah pelajaran PHP Academy gratis di CoddyKit. Ini adalah pelajaran 4 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar PHP Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus PHP Academy mencakup 4 pelajaran total.
Inversi vs Injeksi
Orang sering mencampuradukkan dua gagasan yang berbeda. Injeksi Dependensi adalah teknik: meneruskan kolaborator alih-alih membuatnya. Inversi Dependensi (huruf D dalam SOLID) adalah prinsip: kebijakan tingkat tinggi dan detail tingkat rendah sama-sama bergantung pada abstraksi, dan abstraksi tersebut dimiliki oleh modul tingkat tinggi. Pelajaran ini membahas cara mewujudkan prinsip kedua — merangkai adaptor konkret ke inti yang mendefinisikan antarmuka.
Prinsipnya, Secara Tepat
Prinsip inversi dependensi menyatakan:
- Modul tingkat tinggi tidak boleh bergantung pada modul tingkat rendah. Keduanya bergantung pada abstraksi.
- Abstraksi tidak boleh bergantung pada detail. Detail bergantung pada abstraksi.
Bagian yang halus adalah: antarmuka dimiliki oleh konsumen, bukan pelaksana. Domain Anda mendeklarasikan PaymentGateway; adaptor Stripe menyesuaikan diri dengannya — bukan sebaliknya.
Tentukan Abstraksi di Inti
Letakkan antarmuka di samping kode yang membutuhkannya dan nyatakan dalam istilah domain. Tidak ada tipe Stripe yang merembes ke dalamnya.
<?php
// src/Billing/Application/Port/PaymentGateway.php
interface PaymentGateway
{
public function charge(Money $amount, CardToken $token): ChargeId;
}
final class Money {
public function __construct(public readonly int $cents, public readonly string $currency) {}
}
final class CardToken { public function __construct(public readonly string $value) {} }
final class ChargeId { public function __construct(public readonly string $value) {} }Sesuaikan Adaptor Dengannya
Adaptor infrastruktur mengimplementasikan antarmuka inti dan menerjemahkannya ke SDK vendor. Panah dependensi mengarah dari adaptor (detail) ke abstraksi (kebijakan) — inversi tercapai.
<?php
final class StripePaymentGateway implements PaymentGateway
{
public function __construct(private \Stripe\StripeClient $stripe) {}
public function charge(Money $amount, CardToken $token): ChargeId {
$intent = $this->stripe->paymentIntents->create([
'amount' => $amount->cents,
'currency' => strtolower($amount->currency),
'payment_method' => $token->value,
'confirm' => true,
]);
return new ChargeId($intent->id);
}
}Injeksi Konstruktor sebagai Bawaan
Lakukan injeksi melalui konstruktor dan tetapkan tipe abstraksinya. Dependensi menjadi eksplisit, tidak dapat diubah, dan mustahil terlupakan. Hindari injeksi setter dan properti untuk kolaborator wajib — keduanya memungkinkan objek setengah jadi.
<?php
final class CheckoutService
{
public function __construct(
private PaymentGateway $payments, // abstraction, not StripeClient
private OrderRepository $orders,
) {}
public function pay(OrderId $id, CardToken $token): ChargeId {
$order = $this->orders->get($id);
$charge = $this->payments->charge($order->total(), $token);
$order->markPaid($charge);
$this->orders->save($order);
return $charge;
}
}Akar Komposisi
Semua perangkaian konkret dilakukan tepat di satu tempat — akar komposisi — sedekat mungkin dengan main()/titik masuk. Tidak ada bagian lain yang menginisialisasi infrastruktur secara langsung. Hanya tempat inilah yang mengetahui keberadaan Stripe.
<?php
// public/index.php — composition root
$stripe = new \Stripe\StripeClient(getenv('STRIPE_SECRET'));
$gateway = new StripePaymentGateway($stripe);
$orders = new PdoOrderRepository(new PDO(getenv('DB_DSN')));
$checkout = new CheckoutService($gateway, $orders);
// Everything below depends only on abstractions
$controller = new CheckoutController($checkout);Merangkai dengan Wadah DI
Untuk aplikasi yang tidak sederhana, wadah (PHP-DI, Symfony) mengotomatiskan perangkaian. Langkah utamanya adalah mengikat antarmuka ke implementasi. Perangkaian otomatis menyelesaikan konstruktor berdasarkan tipe; Anda hanya perlu mendeklarasikan peta antarmuka→kelas.
<?php
use function DI\autowire;
use function DI\get;
return [
PaymentGateway::class => autowire(StripePaymentGateway::class),
OrderRepository::class => autowire(PdoOrderRepository::class),
\Stripe\StripeClient::class => fn() => new \Stripe\StripeClient(getenv('STRIPE_SECRET')),
PDO::class => fn() => new PDO(getenv('DB_DSN')),
];Mengganti Adaptor Membuktikan Intinya
Karena inti hanya bergantung pada PaymentGateway, mengganti penyedia atau melakukan pengujian secara luring cukup dilakukan dengan mengubah satu baris perangkaian. Berikut tiruan yang digunakan dalam pengujian unit — CheckoutService tidak berubah dan tidak pernah mengimpor Stripe.
<?php
final class FakeGateway implements PaymentGateway {
public array $charges = [];
public function charge(Money $a, CardToken $t): ChargeId {
$this->charges[] = $a;
return new ChargeId('ch_test_' . count($this->charges));
}
}
$fake = new FakeGateway();
$id = $fake->charge(new Money(2500, 'EUR'), new CardToken('tok_visa'));
echo $id->value, ' charges=', count($fake->charges), PHP_EOL; // ch_test_1 charges=1Hindari Jebakan Pencari Layanan
Menyuntikkan wadah itu sendiri lalu mengambil dependensi di dalam metode merupakan antipola pencari layanan. Cara ini menyembunyikan dependensi, menggagalkan pemeriksaan tipe, dan kembali mengikat kode Anda ke wadah. Suntikkan hal-hal yang Anda butuhkan secara eksplisit.
<?php
// ANTI-PATTERN: hidden dependencies, container leaks everywhere
final class BadCheckout {
public function __construct(private ContainerInterface $c) {}
public function pay($id, $token) {
$gateway = $this->c->get(PaymentGateway::class); // hidden!
// ...
}
}Tempat Wadah Boleh Muncul
Wadah hanya sah berada di satu lapisan: akar komposisi dan perekat kerangka kerja di sekitarnya (misalnya, pabrik pengendali). Kelas domain dan aplikasi Anda harus tetap tidak bergantung pada wadah — keduanya menerima objek biasa melalui konstruktor dan dapat dibuat secara manual. Pengujian yang baik: dapatkah Anda merangkai seluruh aplikasi dalam satu file PHP tanpa wadah? Jika ya, dependensi Anda memang eksplisit.
Perangkaian Malas Tanpa Pencari Layanan
Terkadang dependensi mahal untuk dibuat atau hanya diperlukan dalam kondisi tertentu. Jangan menyuntikkan wadah — suntikkan closure pabrik sebagai gantinya. Dependensi tetap eksplisit dan memiliki tipe, sementara pembuatannya ditunda hingga benar-benar digunakan.
<?php
final class ReportService
{
/** @param Closure():PaymentGateway $gatewayFactory */
public function __construct(private Closure $gatewayFactory) {}
public function refundIfNeeded(bool $needed): void {
if (!$needed) return;
$gateway = ($this->gatewayFactory)(); // built only when required
// $gateway->charge(...) etc.
}
}
// Composition root supplies the factory, not the container
$svc = new ReportService(fn() => new StripePaymentGateway($stripe ?? null));
echo 'lazy dependency wired', PHP_EOL;Pemeriksaan Singkat
Siapa yang seharusnya memiliki antarmuka PaymentGateway?
Ringkasan
Anda telah merangkai adaptor ke inti dengan cara yang benar:
- Inversi ≠ injeksi: injeksi adalah mekanismenya, sedangkan inversi berarti abstraksi dimiliki oleh konsumen.
- Inti mendefinisikan antarmuka; adaptor infrastruktur menyesuaikan diri dengannya.
- Gunakan injeksi konstruktor untuk abstraksi; lakukan semua perangkaian konkret di satu akar komposisi (atau konfigurasi wadah yang mengikat antarmuka→kelas).
- Mengganti adaptor dan menggunakan tiruan dalam pengujian cukup dilakukan dengan mengubah satu baris.
- Hindari antipola pencari layanan — simpan wadah hanya di tepi.
Pertanyaan yang Sering Diajukan
Apakah pelajaran “Inversi Dependensi dalam Praktik” gratis?
Ya — teks lengkap “Inversi Dependensi dalam Praktik” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus PHP Academy, upgrade ke CoddyKit PRO. Kursus PHP Academy mencakup 4 pelajaran total.
Apa yang akan aku pelajari di “Inversi Dependensi dalam Praktik”?
Hubungkan adapter ke inti dengan inversi dependensi. Kamu berlatih PHP Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.
Apakah aku perlu pengalaman untuk memulai PHP Academy?
Tidak diperlukan pengalaman sebelumnya. PHP Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 4 dari 4.
Berapa lama pelajaran “Inversi Dependensi dalam Praktik” memakan waktu?
Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.
Bisakah aku menulis dan menjalankan kode dalam pelajaran PHP Academy ini?
Ya. Setiap pelajaran PHP Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.
Semua pelajaran dalam kursus ini
- Dari Arsitektur Berlapis ke Arsitektur Bersih
- Port dan Adapter Dijelaskan
- Kasus Penggunaan dan Layanan Aplikasi
- Inversi Dependensi dalam Praktik