Bagaimana Anda dapat menguraikan konstruktor?


21

Katakanlah saya memiliki kelas Musuh, dan konstruktor akan terlihat seperti:

public Enemy(String name, float width, float height, Vector2 position, 
             float speed, int maxHp, int attackDamage, int defense... etc.){}

Ini terlihat buruk karena konstruktor memiliki begitu banyak parameter, tetapi ketika saya membuat contoh Musuh saya perlu menentukan semua hal ini. Saya juga ingin atribut ini di kelas Musuh, sehingga saya bisa beralih melalui daftar mereka dan mendapatkan / mengatur parameter ini. Saya berpikir mungkin mensubklasifikasikan Musuh menjadi Musuh, Musuh, sementara hardcoding maxHp mereka, dan atribut spesifik lainnya, tetapi kemudian saya akan kehilangan akses ke atribut hardcoded mereka jika saya ingin beralih melalui daftar Musuh (terdiri dari EnemyA, EnemyB, dan EnemyC's).

Saya hanya mencoba mempelajari cara membuat kode dengan bersih. Jika ada bedanya, saya bekerja di Java / C ++ / C #. Titik mana pun di arah yang benar dihargai.


5
Tidak ada yang buruk tentang memiliki satu konstruktor yang mengikat semua atribut. Bahkan, di beberapa lingkungan kegigihan, diperlukan. Tidak ada yang mengatakan Anda tidak dapat memiliki beberapa konstruktor, mungkin dengan metode pengecekan validitas yang akan dipanggil setelah melakukan konstruksi per potong.
BobDalgleish

1
Saya harus mempertanyakan apakah Anda pernah bermaksud untuk membangun objek Musuh dalam kode menggunakan literal. Jika tidak, dan saya tidak mengerti mengapa Anda melakukannya, maka buat konstruktor yang menarik data dari antarmuka basis data, atau string serialisasi, atau ...
Zan Lynx


Jawaban:


58

Solusinya adalah menggabungkan parameter menjadi tipe komposit. Lebar dan Tinggi terkait secara konseptual - mereka menentukan dimensi musuh dan biasanya akan dibutuhkan bersama. Mereka dapat diganti dengan Dimensionstipe, atau mungkin Rectangletipe yang juga mencakup posisi. Di sisi lain, mungkin lebih masuk akal untuk mengelompokkan positiondan speedmenjadi MovementDatatipe, terutama jika akselerasi kemudian memasuki gambar. Dari konteks Saya berasumsi maxHp, attackDamage, defense, dll juga milik bersama dalam Statsjenis. Jadi, tanda tangan yang direvisi mungkin terlihat seperti ini:

public Enemy(String name, Dimensions dimensions, MovementData movementData, Stats stats)

Detail halus tempat menggambar garis akan tergantung pada sisa kode Anda dan data apa yang biasanya digunakan bersama.


21
Saya juga akan menambahkan bahwa memiliki begitu banyak nilai mungkin mengindikasikan pelanggaran Prinsip Tanggung Jawab Tunggal. Dan mengelompokkan nilai-nilai ke dalam objek spesifik adalah langkah pertama untuk memisahkan tanggung jawab tersebut.
Euforia

2
Saya tidak berpikir bahwa daftar nilai adalah masalah SRP; kebanyakan dari mereka mungkin ditujukan untuk konstruktor kelas dasar. Setiap kelas dalam hierarki dapat memiliki satu tanggung jawab. Enemyhanya kelas yang menargetkan Player, tetapi kelas dasar umum mereka Combatantmembutuhkan statistik pertarungan.
MSalters

@ MSalters Itu tidak selalu menunjukkan masalah SRP, tetapi bisa. Jika dia perlu melakukan cukup angka-angka, fungsi-fungsi itu bisa menemukan jalan mereka ke kelas Musuh ketika mereka harus menjadi fungsi statis / bebas (jika dia menggunakan Dimensions/ MovementDatasebagai wadah data lama biasa) atau metode (jika dia mengubahnya menjadi data abstrak jenis / objek). Sebagai contoh, jika dia belum membuat Vector2jenis, dia mungkin akhirnya melakukan matematika vektor Enemy.
Doval

24

Anda mungkin ingin melihat pola Builder . Dari tautan (dengan contoh pola versus alternatif):

[Pola] Builder adalah pilihan yang baik ketika merancang kelas yang konstruktor atau pabrik statisnya akan memiliki lebih dari beberapa parameter, terutama jika sebagian besar parameter tersebut adalah opsional. Kode klien jauh lebih mudah untuk dibaca dan ditulis dengan pembangun daripada dengan pola konstruktor teleskop tradisional, dan pembangun jauh lebih aman daripada JavaBeans.


4
Cuplikan kode pendek akan sangat membantu. Ini adalah pola yang bagus untuk membangun objek atau struktur rumit dengan berbagai input. Anda juga dapat mengkhususkan pembuatnya, seperti EnemyABuilder, EnemyBBuilder, dll. Yang merangkum berbagai properti bersama. Ini adalah sisi lain dari pola Pabrik (seperti yang dijawab di bawah), tetapi preferensi pribadi saya adalah untuk Builder.
Rob

1
Terima kasih, baik pola Builder maupun pola Factory terlihat cocok dengan apa yang saya coba lakukan secara keseluruhan. Saya pikir kombinasi Builder / Factory dan saran Doval mungkin apa yang saya cari. Sunting: Saya kira saya hanya dapat menandai satu jawaban; Saya akan memberikannya kepada Doval karena menjawab pertanyaan topik, tetapi yang lain sama-sama membantu untuk masalah spesifik saya. Terima kasih semua.
Travis

Saya pikir perlu dicatat bahwa jika bahasa Anda mendukung jenis hantu, maka Anda dapat menulis pola pembangun yang menegakkan bahwa beberapa / semua fungsi SetX dipanggil. Itu juga memungkinkan seseorang memastikan bahwa mereka dipanggil hanya sekali juga (jika diinginkan).
Thomas Eding

1
@ Mark16 Seperti disebutkan dalam tautan, > Pola Builder mensimulasikan parameter opsional bernama seperti yang ditemukan di Ada dan Python. Anda menyebutkan bahwa Anda juga menggunakan C # dalam pertanyaan, dan bahasa itu mendukung argumen bernama / opsional (pada C # 4.0), jadi itu mungkin pilihan lain.
Bob

5

Menggunakan subclass untuk mempreset beberapa nilai tidak diinginkan. Hanya subkelas ketika tipe musuh baru memiliki perilaku atau atribut baru yang berbeda.

The pola pabrik biasanya digunakan untuk abstrak di atas kelas tepat digunakan, tetapi juga dapat digunakan untuk menyediakan template untuk penciptaan objek:

class EnemyFactory {

    // each of these methods is essentially a template for a kind of enemy

    Enemy enemyA(String name, ...) {
        return new Enemy(name, ..., presetValue, ...);
    }

    Enemy enemyB(String name, ...) {
        return new Enemy(name, ..., otherValue, ...);
    }

    Enemy enemyC(String name, ...) {
        return new EnemySubclass(name, ..., otherValue, ...);
    }

    ...
}

EnemyFactory factory = new EnemyFactory();
Enemy a = factory.enemyA("fred", ...);
Enemy b = factory.enemyB("willy", ...);

0

Saya akan memesan subklasifikasi ke kelas yang mewakili objek yang mungkin ingin Anda gunakan secara mandiri, misalnya kelas karakter di mana semua karakter, bukan hanya musuh yang memiliki nama, kecepatan, maxHp, atau kelas untuk mewakili sprite yang memiliki tampilan di layar dengan lebar, tinggi, posisi.

Saya tidak melihat ada sesuatu yang salah dengan konstruktor dengan banyak parameter input tetapi jika Anda ingin membaginya sedikit maka Anda dapat memiliki satu konstruktor yang mengatur sebagian besar parameter dan konstruktor lain (kelebihan beban) yang dapat digunakan untuk mengatur yang spesifik dan membuat orang lain mengatur ke nilai default.

Bergantung pada bahasa apa yang Anda pilih untuk digunakan, beberapa dapat menetapkan nilai default untuk parameter input konstruktor Anda seperti:

Enemy(float height = 42, float width = 42);

0

Contoh kode untuk ditambahkan ke jawaban Rory Hunter (di Jawa):

public class Enemy{
   private String name;
   private float width;
   ...

   public static class Builder{
       private Enemy instance;

       public Builder(){
           this.instance = new Enemy();
       }


       public Builder withName(String name){
           instance.name = name;
           return this;
       }

       ...

       public Enemy build(){
           return instance;
       }
   }
}

Sekarang, Anda dapat membuat instance baru Musuh seperti ini:

Enemy myEnemy = new Enemy.Builder().withName("John").withX(x).build();

1
Programmer adalah tur pertanyaan konseptual dan jawaban diharapkan dapat menjelaskan hal-hal . Melempar kesedihan alih-alih penjelasannya seperti menyalin kode dari IDE ke papan tulis: mungkin terlihat biasa dan bahkan kadang-kadang dapat dimengerti, tetapi rasanya aneh ... hanya aneh. Papan tulis tidak memiliki kompiler
nyamuk
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.