Saya menggunakan perpustakaan pihak ketiga. Mereka memberi saya POJO yang, untuk maksud dan tujuan kami, mungkin dilaksanakan seperti ini:
public class OurData {
private String foo;
private String bar;
private String baz;
private String quux;
// A lot more than this
// IMPORTANT: NOTE THAT THIS IS A PACKAGE PRIVATE CONSTRUCTOR
OurData(/* I don't know what they do */) {
// some stuff
}
public String getFoo() {
return foo;
}
// etc.
}
Untuk banyak alasan, termasuk tetapi tidak terbatas pada merangkum API mereka dan memfasilitasi pengujian unit, saya ingin membungkus data mereka. Tetapi saya tidak ingin kelas inti saya bergantung pada data mereka (sekali lagi, untuk alasan pengujian)! Jadi sekarang saya punya sesuatu seperti ini:
public class DataTypeOne implements DataInterface {
private String foo;
private int bar;
private double baz;
public DataTypeOne(String foo, int bar, double baz) {
this.foo = foo;
this.bar = bar;
this.baz = baz;
}
}
public class DataTypeTwo implements DataInterface {
private String foo;
private int bar;
private double baz;
public DataTypeOne(String foo, int bar, double baz, String quux) {
this.foo = foo;
this.bar = bar;
this.baz = baz;
this.quux = quux;
}
}
Dan kemudian ini:
public class ThirdPartyAdapter {
public static makeMyData(OurData data) {
if(data.getQuux() == null) {
return new DataTypeOne(
data.getFoo(),
Integer.parseInt(data.getBar()),
Double.parseDouble(data.getBaz()),
);
} else {
return new DataTypeTwo(
data.getFoo(),
Integer.parseInt(data.getBar()),
Double.parseDouble(data.getBaz()),
data.getQuux();
);
}
}
Kelas adaptor ini digabungkan dengan beberapa kelas lain yang HARUS tahu tentang API pihak ketiga, membatasi pervasiveness melalui seluruh sistem saya. Namun ... solusi ini adalah KOTOR! Dalam Kode Bersih, halaman 40:
Lebih dari tiga argumen (polyadic) membutuhkan pembenaran yang sangat khusus - dan karenanya tidak seharusnya digunakan.
Hal-hal yang saya pertimbangkan:
- Membuat objek pabrik daripada metode pembantu statis
- Tidak memecahkan masalah memiliki argumen bajillion
- Membuat subkelas DataTypeOne dan DataTypeTwo yang memiliki konstruktor dependen
- Masih memiliki konstruktor yang dilindungi polyadic
- Buat implementasi yang sepenuhnya terpisah yang sesuai dengan antarmuka yang sama
- Banyak dari ide-ide di atas secara bersamaan
Bagaimana seharusnya situasi ini ditangani?
Perhatikan ini bukan situasi lapisan anti korupsi . Tidak ada yang salah dengan API mereka. Masalahnya adalah:
- Saya tidak ingin memiliki struktur data MY
import com.third.party.library.SomeDataStructure;
- Saya tidak bisa membuat struktur data mereka dalam kasus pengujian saya
- Solusi saya saat ini menghasilkan jumlah argumen yang sangat tinggi. Saya ingin menjaga agar jumlah argumen tetap rendah, TANPA meneruskan struktur data mereka.
- Pertanyaan itu adalah " apa itu lapisan anti korupsi?". Pertanyaan saya adalah " bagaimana saya bisa menggunakan pola, pola apa saja, untuk menyelesaikan skenario ini?"
Saya juga tidak meminta kode (jika tidak pertanyaan ini ada di SO), hanya cukup meminta jawaban untuk memungkinkan saya menulis kode secara efektif (yang tidak disediakan pertanyaan itu).
sumber
The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification — and then shouldn’t be used anyway.
Jawaban:
Strategi yang saya gunakan ketika ada beberapa parameter inisialisasi adalah membuat tipe yang hanya berisi parameter untuk inisialisasi
Kemudian konstruktor untuk DataTypeTwo mengambil objek DataTypeTwoParameters, dan DataTypeTwo dibangun melalui:
Ini memberi banyak peluang untuk menjelaskan semua parameter yang masuk ke DataTypeTwo dan apa artinya. Anda juga dapat memberikan default yang masuk akal dalam konstruktor DataTypeTwoParameters sehingga hanya nilai yang perlu diatur yang dapat dilakukan dengan urutan apa pun yang disukai konsumen API.
sumber
Integer.parseInt
? Di setter, atau di luar kelas parameter?p.bar = Integer.parseInt("4")
.DataTypeTwoParameters
menjadiDataTypeTwo
.Anda benar-benar memiliki dua masalah terpisah di sini: membungkus API dan menjaga agar argumen tetap rendah.
Saat membungkus API, idenya adalah mendesain antarmuka seolah-olah dari awal, tidak tahu apa-apa selain persyaratan. Anda mengatakan tidak ada yang salah dengan API mereka, maka dalam daftar nafas yang sama sejumlah hal yang salah dengan API mereka: testability, constructability, terlalu banyak parameter dalam satu objek, dll. Tuliskan API yang Anda inginkan . Jika itu membutuhkan beberapa objek, bukan satu, lakukan itu. Jika memerlukan pembungkus satu tingkat lebih tinggi, ke objek yang membuat POJO, lakukan itu.
Kemudian setelah Anda memiliki API yang Anda inginkan, jumlah parameter mungkin tidak lagi menjadi masalah. Jika ya, ada sejumlah pola umum untuk dipertimbangkan:
Perhatikan bahwa pola kreasi ini sering kali berakhir memanggil konstruktor poladik, yang harus Anda pertimbangkan baik-baik saja ketika dienkapsulasi. Masalah dengan konstruktor polyadic tidak memanggilnya sekali, itu adalah ketika Anda dipaksa untuk memanggil mereka setiap kali Anda perlu membangun suatu objek.
Perhatikan bahwa biasanya lebih mudah dan lebih mudah dikelola untuk melewati API yang mendasarinya dengan menyimpan referensi ke
OurData
objek dan meneruskan panggilan metode, daripada mencoba menerapkan kembali internal-nya. Sebagai contoh:sumber
OurData
Objek" - inilah yang saya coba hindari, setidaknya di kelas dasar, untuk memastikan tidak ada ketergantungan.DataInterface
. Anda membuat implementasi lain untuk objek tiruan Anda.Saya pikir Anda mungkin menafsirkan rekomendasi Paman Bob terlalu ketat. Untuk kelas normal, dengan logika dan metode dan konstruktor dan semacamnya, konstruktor poladik memang terasa sangat mirip dengan kode bau. Tetapi untuk sesuatu yang benar-benar wadah data yang mengekspos bidang, dan dihasilkan oleh apa yang pada dasarnya adalah objek Pabrik, saya tidak berpikir itu terlalu buruk.
Anda dapat menggunakan pola Objek Parameter, seperti yang disarankan dalam komentar, dapat membungkus parameter konstruktor ini untuk Anda, apa pembungkus tipe data lokal Anda sudah , pada dasarnya, objek Parameter. Semua objek Parameter Anda akan lakukan adalah mengemas parameter (Bagaimana Anda akan membuatnya? Dengan konstruktor poladik?) Dan kemudian membongkar mereka sedetik kemudian menjadi objek yang hampir identik.
Jika Anda tidak ingin mengekspos seter untuk bidang Anda dan memanggilnya, saya pikir menempel pada konstruktor poladik di dalam pabrik yang didefinisikan dengan baik dan dienkapsulasi baik-baik saja.
sumber