Menjadi "baru" atau tidak menjadi ...

Halo lagi. Untuk mengantisipasi dimulainya kursus dasar dan lanjutan tentang pengembangan Android, kami telah menyiapkan untuk Anda terjemahan lain yang menarik.




Ketergantungan injeksi mengharuskan kita untuk memisahkan operator baru dan logika aplikasi . Pemisahan ini mendorong Anda untuk menggunakan pabrik dalam kode Anda yang bertanggung jawab untuk menghubungkan aplikasi Anda . Namun, daripada menulis pabrik, kami lebih suka menggunakan injeksi ketergantungan otomatis , seperti GUICE, untuk mengambil alih ikatan. Tetapi bisakah injeksi ketergantungan benar-benar menyelamatkan kita dari semua operator baru ?
Mari kita lihat dua hal ekstrem. Katakanlah Anda memiliki kelas MusicPlayer yang seharusnya mendapatkan AudioDevice. Di sini kami ingin menggunakan injeksi ketergantungan dan meminta AudioDevice di konstruktor MusicPlayer. Ini akan memungkinkan kita untuk menambahkan AudioDevice yang ramah uji, yang dapat kita gunakan untuk mengklaim bahwa suara yang benar keluar dari MusicPlayer kita. Jika kami menggunakan operator baru untuk membuat turunan dari BuiltInSpeakerAudioDevice, maka kami akan mengalami beberapa kesulitan pengujian. Jadi mari kita sebut objek seperti AudioDevice atau MusicPlayer "Suntik". Suntikan adalah objek yang akan Anda minta di konstruktor dan berharap bahwa kerangka kerja untuk menerapkan dependensi akan memberi Anda mereka.

Sekarang ke ekstrim lainnya. Misalkan Anda memiliki int int primitive, tetapi Anda ingin mengemasnya secara otomatis ke Integer, yang paling sederhana adalah memanggil Integer baru (5), dan itulah akhirnya. Tetapi jika injeksi ketergantungan adalah "baru" baru, mengapa kita memanggil in-line baru? Apakah itu akan merusak pengujian kami? Ternyata kerangka kerja untuk menyuntikkan dependensi tidak dapat memberi Anda Integer yang Anda butuhkan, karena mereka tidak mengerti seperti apa Integer itu. Ini sedikit contoh mainan, jadi mari kita lihat sesuatu yang lebih kompleks.

Misalkan pengguna telah memasukkan alamat email di bidang login, dan Anda perlu menelepon Email baru (ยซa@b.comยป) Bisakah kita membiarkannya seperti ini, atau haruskah kita meminta Email di konstruktor kita? Sekali lagi, kerangka kerja injeksi ketergantungan tidak dapat memberi Anda Email, karena Anda harus terlebih dahulu mendapatkan String tempat email itu berada. Dan ada banyak String yang bisa dipilih. Seperti yang Anda lihat, ada banyak objek yang tidak bisa disediakan oleh kerangka injeksi ketergantungan. Sebut mereka "Baru", karena Anda akan dipaksa untuk memanggil baru untuk mereka secara manual.

Pertama, mari kita tentukan beberapa aturan dasar. Kelas Injectable dapat meminta Injectable lain dalam konstruktornya. (Terkadang saya menyebut Suntikan sebagai Obyek Layanan, tetapi istilah ini kelebihan beban.) Suntikan cenderung memiliki antarmuka, karena ada kemungkinan kami harus menggantinya dengan implementasi yang nyaman untuk pengujian. Namun, Injectable tidak pernah dapat meminta non-Injectable (Newable) dalam konstruktornya. Ini karena kerangka kerja injeksi ketergantungan tidak tahu cara membuat yang baru. Berikut adalah beberapa contoh kelas yang akan saya harapkan dari kerangka kerja injeksi ketergantungan saya: CreditCardProcessor, MusicPlayer, MailSender, OfflineQueue. Demikian pula, Newable dapat diminta oleh Newable lain di konstruktor mereka, tetapi tidak Injectable (kadang-kadang saya sebut Newable sebagai Objek Nilai, tetapi sekali lagi,istilah ini kelebihan beban). Beberapa contoh Newable: Email, MailMessage, Pengguna, CreditCard, Song. Jika Anda mengikuti perbedaan ini, kode Anda akan mudah diuji dan bekerja dengannya. Jika Anda melanggar aturan ini, kode Anda akan sulit untuk diuji.

Mari kita lihat contoh MusicPlayer dan Song

class Song {
  Song(String name, byte[] content);
}
class MusicPlayer {
  @Injectable
  MusicPlayer(AudioDevice device);
  play(Song song);
}

Perhatikan bahwa Lagu hanya menanyakan objek yang Dapat Dipakai. Ini membuatnya sangat mudah untuk instantiate Lagu dalam ujian. MusicPlayer sepenuhnya Injectable, seperti argumen AudioDevice-nya, sehingga dapat diperoleh dari kerangka kerja untuk injeksi ketergantungan.

Sekarang mari kita lihat apa yang terjadi jika MusicPlayer melanggar aturan dan meminta Newable dalam konstruktornya.

class Song {
  String name;
  byte[] content;
  Song(String name, byte[] content);
}
class MusicPlayer {
  AudioDevice device;
  Song song;
  @Injectable
  MusicPlayer(AudioDevice device, Song song);
  play();
}

Di sini, Song masih bisa digunakan, dan mudah dibuat dalam pengujian Anda atau dalam kode Anda. MusicPlayer sudah menjadi masalah. Jika Anda meminta MusicPlayer dari kerangka kerja Anda untuk injeksi dependensi, itu akan macet karena kerangka itu tidak akan tahu Lagu mana itu. Sebagian besar orang yang baru mengenal kerangka kerja injeksi ketergantungan jarang melakukan kesalahan ini, karena mudah diketahui: kode Anda tidak akan berfungsi.

Sekarang mari kita lihat apa yang terjadi jika Song melanggar aturan dan meminta Injectable dalam konstruktornya.

class MusicPlayer {
  AudioDevice device;
  @Injectable
  MusicPlayer(AudioDevice device);
}
class Song {
  String name;
  byte[] content;
  MusicPlayer palyer;
  Song(String name, byte[] content, MusicPlayer player);
  play();
}
class SongReader {
  MusicPlayer player
  @Injectable
  SongReader(MusicPlayer player) {
    this.player = player;
  }
  Song read(File file) {
    return new Song(file.getName(),
                    readBytes(file),
                    player);
  }
}

Sekilas, semuanya baik-baik saja. Tapi pikirkan bagaimana Lagu akan dibuat. Agaknya, lagu disimpan di disk, jadi kita perlu SongReader. SongReader harus meminta MusicPlayer sehingga saat memanggil baru untuk Song, ia dapat memuaskan ketergantungan Song pada MusicPlayer. Pernahkah Anda memperhatikan ada yang salah di sini? Ketakutan apa yang perlu diketahui SongReader tentang MusicPlayer? Ini merupakan pelanggaran hukum Demeter. SongReader seharusnya tidak tahu tentang MusicPlayer. Setidaknya karena SongReader tidak memanggil metode dengan MusicPlayer. Dia hanya tahu tentang MusicPlayer karena Song telah melanggar pemisahan Newable / Injectable. SongReader membayar kesalahan pada Song. Karena tempat di mana kesalahan dibuat dan di mana konsekuensi terwujud bukan hal yang sama, kesalahan ini sangat halus dan sulit untuk didiagnosis. Ini juga berarti bahwa banyak orang cenderung membuat kesalahan ini.

Dalam hal pengujian, ini adalah rasa sakit yang nyata. Misalkan Anda memiliki SongWriter dan ingin memastikan itu membuat serialisasi Song ke disk dengan benar. Anda perlu membuat MockMusicPlayer sehingga Anda bisa meneruskannya ke Song sehingga Anda bisa meneruskannya ke SongWritter. Mengapa kita menemukan MusicPlayer di sini? Mari kita lihat sebaliknya. Lagu adalah apa yang Anda ingin serialkan, dan cara termudah untuk melakukannya adalah dengan menggunakan serialisasi Java. Jadi, kami membuat serial bukan hanya Lagu, tetapi juga MusicPlayer dan AudioDevice. Baik MusicPlayer maupun AudioDevice tidak boleh diserialisasi. Seperti yang Anda lihat, perubahan kecil sangat memudahkan testabilitas.

Seperti yang Anda lihat, bekerja dengan kode lebih mudah jika kita memisahkan kedua jenis objek ini. Jika Anda mencampurnya, kode Anda akan sulit untuk diuji. Newable adalah objek yang ada di akhir grafik objek aplikasi Anda. Newable dapat bergantung pada Newable lainnya, misalnya, bagaimana CreditCard dapat bergantung pada Alamat, yang mungkin bergantung pada Kota - hal-hal ini adalah lembaran grafik aplikasi. Karena mereka adalah lembaran dan tidak berkomunikasi dengan layanan eksternal (layanan eksternal dapat disuntikkan), mereka tidak perlu melakukan bertopik. Tidak ada yang lebih mirip String daripada String itu sendiri. Mengapa saya harus membuat rintisan untuk Pengguna, jika saya bisa memanggil Pengguna baru, mengapa membuat tulisan rintisan untuk semua ini: Email, MailMessage, Pengguna, CreditCard, Lagu? Panggil saja yang baru dan akhiri saja.

Sekarang mari kita perhatikan sesuatu yang sangat halus. Adalah normal bagi Newable untuk mengetahui tentang Suntikan. Apa yang tidak normal adalah bahwa Newable memiliki referensi ke Injectable sebagai bidang. Dengan kata lain, Song bisa tahu tentang MusicPlayer. Sebagai contoh, itu normal untuk MusicPlayer Suntikan harus melewati tumpukan ke Lagu yang Dapat Digunakan. Karena melewati tumpukan tidak tergantung pada kerangka kerja untuk injeksi ketergantungan. Seperti dalam contoh ini:

class Song {
  Song(String name, byte[] content);
  boolean isPlayable(MusicPlayer player);
}

Masalah terjadi ketika Lagu memiliki bidang tautan ke MusicPlayer. Bidang tautan diatur melalui konstruktor, yang akan menyebabkan pelanggaran hukum Demeter bagi pemanggil, dan kesulitan untuk pengujian kami.

Pelajari lebih lanjut tentang kursus



All Articles