Gunakan metode konstruktor atau penyetel?

16

Saya sedang mengerjakan kode UI di mana saya memiliki Actionkelas, sesuatu seperti ini -

public class MyAction extends Action {
    public MyAction() {
        setText("My Action Text");
        setToolTip("My Action Tool tip");
        setImage("Some Image");
    }
}

Ketika kelas Action ini dibuat, cukup banyak diasumsikan bahwa Actionkelas tidak akan dikustomisasi (dalam arti - teks, tooltip atau gambar tidak akan diubah di mana pun dalam kode). Sekarang, kita perlu mengubah teks tindakan di beberapa lokasi dalam kode. Jadi, saya menyarankan rekan kerja saya untuk menghapus teks tindakan hardcode dari konstruktor dan menerimanya sebagai argumen, sehingga semua orang dipaksa untuk melewati teks tindakan. Sesuatu seperti kode di bawah ini -

public class MyAction extends Action {
    public MyAction(String actionText) {
        setText(actionText);
        setTooltip("My Action tool tip"); 
        setImage("My Image"); 
    }
}

Dia, bagaimanapun, berpikir bahwa karena setText()metode milik kelas dasar dapat secara fleksibel digunakan untuk melewatkan teks tindakan di mana pun instance tindakan dibuat. Dengan begitu, tidak perlu mengubah MyActionkelas yang ada . Jadi kodenya akan terlihat seperti ini.

MyAction action = new MyAction(); //this creates action instance with the hardcoded text
action.setText("User required new action text"); //overwrite the existing text.

Saya tidak yakin apakah itu cara yang benar untuk menangani masalah. Saya pikir dalam kasus yang disebutkan di atas pengguna tetap akan mengubah teks, jadi mengapa tidak memaksanya saat membangun tindakan? Satu-satunya manfaat yang saya lihat dengan kode asli adalah pengguna dapat membuat kelas Tindakan tanpa banyak berpikir tentang pengaturan teks.

zswap
sumber
1
Bahasa yang Anda gunakan tidak mengizinkan konstruktor yang berlebihan?
Mat
1
Saya menggunakan Java. Jadi ya, itu memungkinkan dan saya pikir itu bisa menjadi salah satu cara untuk mengatasi ini
zswap
2
Saya akan mencatat bahwa jika Anda tidak memiliki cara publik untuk mengatur anggota kelas setelah fakta, kelas Anda secara efektif tidak dapat diubah . Dengan mengizinkan setter publik, kelas Anda sekarang bisa berubah , dan Anda mungkin perlu mempertimbangkan ini jika Anda bergantung pada ketidakberdayaan.
cbojar
Saya akan mengatakan bahwa jika itu perlu diatur agar objek Anda menjadi valid, letakkan di setiap konstruktor ... jika itu opsional (memiliki default yang masuk akal) dan Anda tidak peduli tentang keabadian, letakkan di setter. Seharusnya tidak mungkin untuk instantiate objek Anda ke dalam keadaan tidak valid atau setelah instantiasi memasukkannya ke dalam keadaan tidak valid sedapat mungkin.
Bill K

Jawaban:

15

Satu-satunya manfaat yang saya lihat dengan kode asli adalah pengguna dapat membuat kelas Tindakan tanpa banyak berpikir tentang pengaturan teks.

Itu sebenarnya bukan manfaat, untuk sebagian besar tujuan itu adalah kelemahan dan dalam kasus yang tersisa saya akan menyebutnya dasi. Bagaimana jika seseorang lupa memanggil setText () setelah konstruksi? Bagaimana jika itu adalah kasus dalam beberapa kasus yang tidak biasa, mungkin penangan kesalahan? Jika Anda ingin benar-benar memaksa teks untuk ditetapkan, Anda harus memaksanya pada waktu kompilasi, karena hanya kesalahan waktu kompilasi yang berakibat fatal . Apa pun yang terjadi pada saat run-time tergantung pada jalur kode tertentu yang sedang dieksekusi.

Saya melihat dua jalur yang jelas ke depan:

  1. Gunakan parameter konstruktor, seperti yang Anda sarankan. Jika Anda benar-benar ingin, Anda bisa meneruskan nullatau string kosong, tetapi kemudian fakta bahwa Anda tidak menetapkan teks lebih eksplisit daripada implisit. Sangat mudah untuk melihat keberadaan nullparameter dan melihat bahwa mungkin ada beberapa pemikiran yang dimasukkan ke dalamnya, tetapi tidak begitu mudah untuk melihat kurangnya pemanggilan metode dan menentukan apakah kurangnya seperti itu disengaja atau tidak. Untuk kasus sederhana seperti ini, ini mungkin pendekatan yang akan saya ambil.
  2. Gunakan pola pabrik / pembangun. Ini mungkin berlebihan untuk skenario sederhana seperti itu, tetapi dalam kasus yang lebih umum sangat fleksibel karena memungkinkan Anda untuk mengatur sejumlah parameter, dan memeriksa prasyarat sebelum atau selama instantiasi objek (jika membangun objek adalah operasi besar dan / atau kelas dapat digunakan dalam lebih dari satu cara, ini bisa menjadi keuntungan besar ). Khususnya di Jawa, ini juga merupakan idiom yang umum, dan mengikuti pola yang sudah mapan dalam bahasa dan kerangka kerja yang Anda gunakan jarang merupakan hal yang buruk.
sebuah CVn
sumber
10

Overloading konstruktor akan menjadi solusi sederhana dan langsung di sini:

public class MyAction extends Action {
    public MyAction(String actionText) {
        setText(actionText);
        setTooltip("My Action tool tip"); 
        setImage("My Image"); 
    }
    public MyAction() {
        this("My Action Text");
    }
}

Itu lebih baik daripada menelepon .setTextnanti, karena dengan cara ini tidak ada yang perlu ditimpa, actionTextdapat menjadi hal yang dimaksudkan sejak awal.

Ketika kode Anda berkembang dan Anda akan membutuhkan lebih banyak fleksibilitas (yang pasti akan terjadi), Anda akan mendapat manfaat dari pola pabrik / pembangun yang disarankan oleh jawaban lain.

janos
sumber
Apa yang terjadi ketika mereka ingin menyesuaikan properti kedua?
kevin cline
3
Untuk properti ke-2, ke-3, .., Anda bisa menerapkan teknik yang sama, tetapi semakin banyak properti yang ingin Anda sesuaikan, semakin sulit ini menjadi. Pada titik tertentu akan lebih masuk akal untuk menerapkan pola pabrik / pembangun, demikian juga kata @ michael-kjorling dalam jawabannya.
janos
6

Tambahkan metode 'setText' yang lancar:

public class MyAction ... {
  ...
  public MyAction setText(String text) { ... ; return this; }
}

MyAction a = new MyAction().setText("xxx");

Apa yang bisa lebih jelas dari itu? Jika Anda memutuskan untuk menambahkan properti lain yang dapat disesuaikan, tidak ada masalah.

kevin cline
sumber
+1, saya setuju, dan menambahkan jawaban lain yang melengkapi dengan lebih banyak properti. Saya pikir API yang lancar lebih jelas untuk dipahami ketika Anda memiliki lebih dari 1 properti tunggal sebagai contoh.
Machado
Saya suka antarmuka yang lancar, terutama untuk pembangun yang menghasilkan objek yang tidak berubah! Semakin banyak parameter, semakin baik kerjanya. Tetapi melihat contoh spesifik dalam pertanyaan ini, saya menduga itu setText()didefinisikan pada kelas Action yang diwarisi dari MyAction. Kemungkinan sudah memiliki jenis void return.
GlenPeterson
1

Sama seperti kata kevin cline dalam jawabannya, saya pikir cara untuk pergi adalah membuat API yang lancar . Saya hanya ingin menambahkan bahwa API lancar berfungsi lebih baik ketika Anda memiliki lebih dari satu properti yang dapat Anda gunakan.

Ini akan membuat kode Anda lebih mudah dibaca, dan dari sudut pandang saya lebih mudah dan, aham , "seksi" untuk menulis.

Dalam kasus Anda itu akan seperti ini (maaf untuk kesalahan ketik apa pun, sudah setahun sejak saya menulis program java terakhir saya):

 public class MyAction extends Action {
    private String _text     = "";
    private String _tooltip  = "";
    private String _imageUrl = "";

    public MyAction()
    {
       // nothing to do here.
    }

    public MyAction text(string value)
    {
       this._text = value;
       return this;
    }

    public MyAction tooltip(string value)
    {
       this._tooltip = value;
       return this;
    }

    public MyAction image(string value)
    {
       this._imageUrl = value;
       return this;
    }
}

Dan penggunaannya akan seperti ini:

MyAction action = new MyAction()
    .text("My Action Text")
    .tooltip("My Action Tool tip")
    .image("Some Image");
Machado
sumber
Gagasan buruk, bagaimana jika mereka lupa mengatur teks atau hal penting.
Prakhar
1

Saran untuk menggunakan konstruktor atau pembangun baik-baik saja secara umum, tetapi, dalam pengalaman saya, kehilangan beberapa poin utama untuk Tindakan, yang

  1. Mungkin perlu diinternasionalkan
  2. Kemungkinan pemasaran berubah pada menit terakhir.

Saya sangat menyarankan agar nama, tooltip, ikon dll ... dibaca dari file properti, XML, dll. Misalnya, untuk tindakan File-Open, Anda bisa mengirimkan Properties dan akan mencari

File.open.name=Open
File.open.tooltip=Open a file
File.open.icon=somedir/open.jpg

Ini adalah format yang cukup mudah untuk diterjemahkan ke bahasa Prancis, untuk mencoba ikon baru yang lebih baik, dll. Tanpa waktu programmer atau kompilasi ulang.

Ini hanya garis besar kasar, banyak yang tersisa untuk pembaca ... Carilah contoh internasionalisasi lainnya.

pengguna949300
sumber
0

Tidak berguna untuk memanggil setText (actionText) atau setTooltip ("Tip alat tindakan saya") di dalam konstruktor; lebih mudah (dan Anda mendapatkan lebih banyak kinerja) jika Anda langsung menginisialisasi bidang yang sesuai:

    public MyAction(String actionText) {
        this.actionText = actionText;
    }

Jika Anda mengubah actionText selama masa hidup objek terkait MyAction, Anda harus menempatkan metode setter; jika tidak menginisialisasi bidang hanya dalam konstruktor tanpa menyediakan metode penyetel.

Karena tooltip dan gambar adalah konstanta, perlakukan mereka sebagai konstanta; memiliki bidang:

private (or even public) final static String TOOLTIP = "My Action Tooltip";

Sebenarnya, ketika mendesain objek umum (bukan kacang atau objek yang mewakili struktur data ketat) adalah ide yang buruk untuk menyediakan setter dan getter, karena mereka semacam memecah enkapsulasi.

m3th0dman
sumber
4
Kompilator kompeten setengah jalan dan JITter harus menyejajarkan panggilan setText () dll, sehingga perbedaan kinerja antara pemanggilan fungsi untuk melakukan penugasan, dan menjadikan penugasan sebagai pengganti pemanggilan fungsi, harus dapat diabaikan paling banyak, dan lebih besar kemungkinannya daripada tidak nol.
CVn
0

Saya pikir ini benar jika kita akan membuat kelas tindakan umum (seperti pembaruan, yang digunakan untuk memperbarui Karyawan, Departemen ...). Itu semua tergantung skenario. Jika kelas tindakan tertentu (seperti karyawan pembaruan) (digunakan banyak tempat dalam aplikasi - Perbarui karyawan) dibuat dengan niat untuk menyimpan teks, tooltip, dan gambar yang sama di setiap tempat dalam aplikasi (untuk sudut pandang konsistensi). Jadi hardcoding dapat dilakukan untuk teks, tooltip dan gambar untuk menyediakan teks, tooltip dan gambar default. Masih untuk memberikan lebih banyak fleksibilitas, untuk menyesuaikan ini, harus ada metode setter yang sesuai. Mengingat hanya 10% tempat yang harus kita ubah. Mengambil teks tindakan setiap saat dari pengguna dapat menyebabkan teks berbeda setiap kali untuk tindakan yang sama. Seperti 'Perbarui Emp', 'Perbarui Karyawan', 'Ubah Karyawan' atau 'Edit Karyawan'.

Sandy
sumber
Saya pikir konstruktor kelebihan beban masih harus menyelesaikan masalah. Karena dalam semua kasus "10%", Anda akan terlebih dahulu membuat Tindakan dengan teks default dan kemudian mengubah teks tindakan menggunakan metode "setText ()". Mengapa tidak mengatur teks yang sesuai saat menyusun tindakan.
zswap
0

Pikirkan bagaimana mesin virtual akan digunakan dan gunakan solusi yang akan memandu, atau bahkan memaksa, pengguna untuk menggunakan mesin virtual tersebut dengan cara yang benar, atau setidaknya yang terbaik. Seorang programmer yang menggunakan kelas ini akan memiliki banyak hal lain yang perlu dikhawatirkan dan dipikirkan. Kelas ini tidak boleh ditambahkan ke daftar.

Misalnya, jika kelas MyAction seharusnya tidak dapat diubah setelah konstruksi (dan mungkin inisialisasi lainnya), seharusnya tidak memiliki metode penyetel. Jika sebagian besar waktu akan menggunakan "Teks Tindakan Saya" default, harus ada konstruktor tanpa parameter, ditambah konstruktor yang memungkinkan teks opsional. Sekarang pengguna tidak perlu berpikir untuk menggunakan kelas dengan benar 90% dari waktu. Jika pengguna biasanya harus memikirkan teks, lewati konstruktor tanpa parameter. Sekarang pengguna dipaksa untuk berpikir bila perlu dan tidak bisa mengabaikan langkah yang diperlukan.

Jika sebuah MyAction instance harus bisa berubah setelah konstruksi penuh maka Anda memerlukan setter untuk teks. Sangat menggoda untuk melewatkan pengaturan nilai dalam konstruktor (prinsip KERING - "Don't Repeat Yourself") dan, jika nilai default biasanya cukup baik, saya akan melakukannya. Tetapi jika tidak, mengharuskan teks di konstruktor memaksa pengguna untuk berpikir kapan mereka harus.

Perhatikan bahwa pengguna ini tidak bodoh . Mereka hanya memiliki terlalu banyak masalah nyata untuk dikhawatirkan. Dengan memikirkan "antarmuka" kelas Anda, Anda dapat mencegahnya menjadi masalah nyata - dan yang tidak perlu.

RalphChapin
sumber
0

Dalam solusi yang diusulkan berikut, superclass adalah abstrak dan memiliki ketiga anggota diatur ke nilai default.

Subclass memiliki konstruktor yang berbeda sehingga programmer dapat membuat instance.

Jika konstruktor pertama digunakan, semua anggota akan memiliki nilai default.

Jika konstruktor kedua digunakan, Anda memberikan nilai awal untuk anggota actionText meninggalkan dua anggota lainnya dengan nilai default ...

Jika konstruktor ketiga digunakan, Anda instantiate dengan nilai baru untuk actionText dan toolTip, meninggalkan imageURl dengan nilai default ...

Dan seterusnya.

public abstract class Action {
    protected String text = "Default action text";
    protected String toolTip = "Default action tool tip";
    protected String imageURl = "http://myserver.com/images/default.png";

    .... rest of code, I guess setters and getters
}

public class MyAction extends Action {


    public MyAction() {

    }

    public MyAction(String actionText) {
        setText(actionText);
    }

    public MyAction(String actionText, String toolTip_) {
        setText(actionText);
        setToolTip(toolTip_);   
    }

    public MyAction(String actionText, String toolTip_; String imageURL_) {
        setText(actionText);
        setToolTip(toolTip_);
        setImageURL(imageURL_);
    }


}
Tulains Córdova
sumber