Pemrograman Berorientasi Objek: getter / setter atau nama logis

12

Saat ini saya sedang memikirkan antarmuka ke kelas yang saya tulis. Kelas ini berisi gaya untuk karakter, misalnya apakah karakter itu tebal, miring, bergaris bawah, dll. Saya telah berdebat dengan diri saya sendiri selama dua hari apakah saya harus menggunakan getter / setter atau nama logis untuk metode yang mengubah nilai menjadi gaya-gaya ini. Walaupun saya cenderung lebih suka nama logis, itu berarti menulis kode yang tidak seefisien dan tidak logis. Biarkan saya memberi Anda sebuah contoh.

Aku punya kelas CharacterStylesyang memiliki variabel anggota bold, italic, underline(dan beberapa orang lain, tapi aku akan meninggalkan mereka untuk tetap sederhana). Cara termudah untuk mengizinkan bagian lain dari program untuk mengakses variabel-variabel ini, adalah dengan menulis metode pengambil / penyetel, sehingga Anda dapat melakukannya styles.setBold(true)dan styles.setItalic(false).

Tapi saya tidak suka ini. Bukan hanya karena banyak orang mengatakan bahwa getter / seter menghancurkan enkapsulasi (apakah ini benar-benar seburuk itu?), Tetapi kebanyakan karena itu tidak masuk akal bagi saya. Saya berharap untuk mendesain karakter melalui satu metode, styles.format("bold", true)atau sesuatu seperti itu, tetapi tidak melalui semua metode ini.

Namun ada satu masalah. Karena Anda tidak dapat mengakses variabel anggota objek dengan konten string dalam C ++, saya harus menulis wadah if-statement / switch besar untuk semua gaya, atau saya harus menyimpan gaya dalam array asosiatif ( peta).

Saya tidak tahu apa cara terbaik. Satu saat saya pikir saya harus menulis getter / setter, dan saat berikutnya saya condong ke arah lain. Pertanyaan saya adalah: apa yang akan Anda lakukan? Dan mengapa Anda melakukan itu?

Katak
sumber
Apakah kelas CharacterStyles benar-benar melakukan apa pun selain bundel tiga boolean? Bagaimana cara dikonsumsi?
Mark Canlas
Ya, karena CharacterStyles harus dapat mewarisi dari CharacterStyles lain. Ini berarti bahwa jika saya memiliki CharacterStyles dengan boldset ke truedan variabel lain tidak terdefinisi, metode pengambil untuk variabel lain harus mengembalikan nilai gaya induk (yang disimpan di properti lain), sedangkan pengambil untuk boldproperti harus kembali true. Ada beberapa hal lain seperti nama dan cara itu ditampilkan di antarmuka juga.
Frog
Anda bisa menggunakan enum untuk menentukan berbagai opsi gaya, lalu gunakan pernyataan sakelar. Btw, bagaimana Anda menangani undefined?
Tyanna
@Tyanna: Memang, saya juga berpikir tentang menggunakan enum, tapi itu hanya detail kecil. Saya berencana untuk hanya menetapkan nilai yang tidak ditentukan ke NULL. Bukankah itu cara terbaik?
Katak

Jawaban:

6

Ya, getter / setters memecah enkapsulasi - mereka pada dasarnya hanya lapisan tambahan antara secara langsung mengakses bidang yang mendasarinya. Anda mungkin juga langsung mengaksesnya.

Sekarang, jika Anda ingin metode yang lebih kompleks untuk mengakses bidang, itu sah, tetapi alih-alih mengekspos bidang, Anda perlu memikirkan metode apa yang harus ditawarkan oleh kelas. yaitu. alih-alih kelas Bank dengan nilai Uang yang terpapar menggunakan properti, Anda perlu memikirkan jenis akses yang harus ditawarkan oleh objek Bank (menambah uang, menarik, mendapatkan saldo) dan mengimplementasikannya. Properti pada variabel Uang hanya secara sintaksis berbeda dari mengekspos variabel Uang secara langsung.

DrDobbs memiliki artikel yang mengatakan lebih banyak.

Untuk masalah Anda, saya punya 2 metode setStyle dan clearStyle (atau apa pun) yang memerlukan enum dari gaya yang mungkin. Pernyataan peralihan di dalam metode ini kemudian akan menerapkan nilai yang relevan ke variabel kelas yang sesuai. Dengan cara ini, Anda dapat mengubah representasi internal dari gaya ke sesuatu yang lain jika nanti Anda memutuskan untuk menyimpannya sebagai string (untuk digunakan dalam HTML misalnya) - sesuatu yang akan mengharuskan semua pengguna kelas Anda untuk diubah juga jika Anda menggunakan dapatkan / atur properti.

Anda masih dapat mengaktifkan string jika Anda ingin mengambil nilai arbitrer, baik memiliki pernyataan if-then besar (jika ada beberapa), atau peta nilai string ke pointer metode menggunakan std :: mem_fun (atau std :: function ) jadi "bold" akan disimpan dalam kunci peta dengan nilainya menjadi sts :: mem_fun ke metode yang menetapkan variabel bold menjadi true (jika string sama dengan nama variabel anggota, maka Anda juga dapat menggunakan yang makro stringifying untuk mengurangi jumlah kode yang Anda butuhkan untuk menulis)

gbjbaanb
sumber
Jawaban yang sangat bagus! Terutama bagian tentang menyimpan data sebagai HTML menurut saya sebagai alasan yang bagus untuk memang turun jalan ini. Jadi, Anda akan menyarankan menyimpan berbagai gaya dalam enum dan kemudian mengatur gaya seperti styles.setStyle(BOLD, true)? Apakah itu benar?
Katak
ya, hanya saya yang punya 2 metode - setStyle (BOLD) dan clearstyle (BOLD). lupakan parameter ke-2, saya hanya lebih suka seperti itu dan Anda bisa membebani clearStyle untuk tidak mengambil parameter untuk menghapus semua flag style.
gbjbaanb
Itu tidak akan berhasil, karena beberapa jenis (seperti ukuran font) memerlukan parameter. Tapi tetap terima kasih atas jawabannya!
Frog
Saya sudah mencoba mengimplementasikan ini, tetapi ada satu masalah yang tidak saya pertimbangkan: bagaimana Anda mengimplementasikan styles.getStyle(BOLD)ketika Anda tidak hanya memiliki variabel anggota tipe boolean, tetapi juga tipe integer dan string (misalnya styles.getStyle(FONTSIZE):). Karena Anda tidak dapat membebani jenis pengembalian, bagaimana Anda memprogram ini? Saya tahu Anda bisa menggunakan pointer kosong, tapi itu terdengar seperti cara yang sangat buruk bagi saya. Apakah Anda punya petunjuk tentang cara melakukan ini?
Frog
Anda bisa mengembalikan serikat pekerja, atau struct, atau dengan standar baru, Anda dapat membebani berdasarkan jenis pengembalian. Yang mengatakan, apakah Anda mencoba menerapkan metode tunggal untuk mengatur gaya, di mana gaya adalah bendera, dan font-family, dan ukuran? Mungkin Anda mencoba memaksakan abstraksi terlalu banyak dalam hal ini.
gbjbaanb
11

Satu ide yang mungkin belum Anda pertimbangkan adalah Pola Dekorator . Daripada mengatur bendera di suatu objek dan kemudian menerapkan bendera itu untuk apa pun yang Anda tulis, Anda membungkus kelas yang menulis di Dekorator, yang pada gilirannya menerapkan gaya.

Kode panggilan tidak perlu tahu berapa banyak pembungkus yang Anda letakkan di sekitar teks Anda, Anda hanya memanggil metode pada objek luar dan memanggil tumpukan.

Untuk contoh pseudocode:

class TextWriter : TextDrawingInterface {
    public:
        void WriteString(string x) {
            // write some text somewhere somehow
        }
}

class BoldDecorator : TextDrawingInterface {
    public:
        void WriteString(string x) {
            // bold application on
            m_textWriter.WriteString(x);
            // bold application off
        }

        ctor (TextDrawingInterface textWriter) {
            m_textWriter = textWriter;
        }

    private:
        TextWriter m_TextWriter;
}

Dan seterusnya, untuk setiap gaya dekorasi. Dalam penggunaannya yang paling sederhana, Anda bisa mengatakannya

TextDrawingInterface GetDecoratedTextWriter() {
    return new BoldDecorator(new ItalicDecorator(new TextWriter()));
}

Dan kode yang memanggil metode ini tidak perlu mengetahui detail dari apa yang diterimanya. Selama itu SESUATU yang dapat menggambar teks melalui metode WriteString.

pdr
sumber
Hmmm, saya akan melihatnya besok dengan pikiran segar dan akan kembali kepada Anda. Terima kasih atas jawabannya.
Katak
Saya penggemar pola dekorator. Fakta bahwa pola ini menyerupai HTML (di mana operasi intinya adalah melampirkan string input dengan tag) adalah pengesahan bahwa pendekatan ini dapat memenuhi semua kebutuhan pengguna antarmuka Anda. Satu hal yang perlu diingat adalah bahwa antarmuka yang baik dan mudah digunakan, mungkin memiliki implementasi mendasar yang secara teknis rumit. Misalnya, jika Anda benar-benar mengimplementasikan renderer teks sendiri, Anda mungkin TextWriterperlu berbicara dengan keduanya BoldDecoratordan ItalicDecorator(dua arah) untuk menyelesaikan pekerjaan.
rwong
sementara ini adalah jawaban yang bagus, bukankah itu memperkenalkan kembali pertanyaan op? "aplikasi berani pada" -> bagaimana ini akan dilakukan? menggunakan setter / getter seperti SetBold ()? Yang persis pertanyaan op.
stijn
1
@stijn: Tidak juga. Ada beberapa cara untuk menghindari situasi itu. Misalnya, Anda bisa membungkusnya dalam pembangun dengan metode toggleStyle (argumen enum) yang menambah dan menghapus dekorator dari array, hanya menghiasi item terakhir saat Anda memanggil BuildDecoratedTextWriter. Atau Anda dapat melakukan seperti yang disarankan dan menyulitkan kelas dasar. Bergantung pada keadaan dan OP tidak cukup spesifik tentang spesifikasi keseluruhan untuk menebak mana yang terbaik untuknya. Dia tampaknya cukup pintar untuk bisa mengetahuinya begitu dia mendapatkan polanya.
pdr
1
Saya selalu berpikir Pola Penghias menyiratkan pemesanan untuk dekorasi. Pertimbangkan contoh kode yang ditunjukkan; bagaimana cara menghapus ItalicDecorator? Namun, saya pikir sesuatu seperti Dekorator adalah jalan yang harus ditempuh. Berani, miring, dan garis bawah bukan hanya tiga gaya. Ada yang tipis, setengah botak, padat, hitam, lebar, miring dengan huruf miring, topi kecil, dll. Anda mungkin perlu cara untuk menerapkan serangkaian gaya sewenang-wenang yang besar pada karakter.
Barry Brown
0

Saya akan condong ke solusi kedua. Terlihat lebih elegan dan fleksibel. Meskipun sulit untuk membayangkan tipe lain selain tebal, miring, dan bergaris bawah (overline?), Akan sulit untuk menambahkan tipe baru menggunakan variabel anggota.

Saya menggunakan pendekatan ini di salah satu proyek terbaru saya. Saya memiliki kelas yang dapat memiliki beberapa atribut boolean. Jumlah dan nama atribut dapat berubah dalam waktu. Saya menyimpannya di kamus. Jika atribut tidak ada, saya menganggap nilainya "salah". Saya juga harus menyimpan daftar nama atribut yang tersedia, tapi itu cerita lain.

Andrzej Bobak
sumber
1
Terlepas dari jenis ini saya akan menambahkan coret, ukuran font, font-keluarga, superskrip, subskrip, dan mungkin yang lain nanti. Tetapi mengapa Anda memilih metode itu dalam proyek Anda? Tidakkah menurut Anda itu kode kotor karena harus menyimpan daftar atribut yang diizinkan? Saya benar-benar ingin tahu apa jawaban Anda untuk pertanyaan-pertanyaan ini!
Katak
Saya harus berurusan dengan situasi di mana beberapa atribut dapat didefinisikan oleh klien ketika aplikasi sudah dikerahkan. Beberapa atribut lain diperlukan untuk beberapa klien tetapi usang untuk yang lain. Saya bisa menyesuaikan formulir, set data yang disimpan dan mengatasi masalah lain menggunakan pendekatan ini. Saya tidak berpikir itu menghasilkan kode kotor. Terkadang Anda tidak dapat memprediksi informasi seperti apa yang akan dibutuhkan oleh pelanggan Anda.
Andrzej Bobak
Tetapi pelanggan saya tidak perlu menambahkan atribut khusus. Dan dalam hal ini saya pikir itu terlihat agak "retas". Apakah kamu tidak setuju?
Katak
Jika Anda tidak menghadapi masalah jumlah atribut dinamis, Anda tidak perlu ruang fleksibel untuk menyimpannya. Itu benar :) Aku tidak setuju dengan bagian yang lain. Mengukur atribut dalam kamus / hashmap / etc bukan pendekatan yang buruk menurut saya.
Andrzej Bobak
0

Saya pikir Anda terlalu memikirkan masalah ini.

styles.setBold(true) dan baik styles.setItalic(false)-baik saja

Tulains Córdova
sumber