Dua struct dengan anggota yang sama tetapi penamaan yang berbeda, apakah itu ide yang bagus?


49

Saya menulis sebuah program yang melibatkan bekerja dengan koordinat kutub dan Cartesian.

Apakah masuk akal untuk membuat dua struct yang berbeda untuk setiap jenis poin, satu dengan Xdan Yanggota dan satu dengan Rdan Thetaanggota.

Atau itu terlalu banyak dan lebih baik hanya memiliki satu struct dengan firstdan secondsebagai anggota.

Apa yang saya tulis sederhana dan tidak banyak berubah. Tapi saya ingin tahu apa yang lebih baik dari sudut pandang desain.

Saya pikir opsi pertama lebih baik. Tampaknya lebih mudah dibaca dan saya akan mendapatkan manfaat dari pengecekan tipe.


11
Saya selalu membuat struct / kelas baru per tujuan. Butuh vektor 3d, buat 3d_vector struct dengan tiga mengapung. Butuh representasi uvw, buat struct tekstur_coords dengan tiga mengapung. Butuh posisi di lingkungan 3d, buat posisi struct dengan tiga pelampung. Anda mengerti maksudnya. Ini memungkinkan pembacaan yang jauh lebih baik daripada menggunakan hal yang sama di seluruh. Jika Anda terganggu dengan mendefinisikan hal yang sama beberapa kali, gunakan basis 3-float-struct dan tentukan beberapa nama menjadi struktur yang sama.
Kevin

Jika mereka memiliki beberapa metode umum maka mungkin satu. Apakah Anda perlu membandingkan kesetaraan keduanya?
paparazzo

8
@ Sidney Kecuali jika Anda benar-benar membutuhkan fungsionalitas saya tidak akan. Anda perlu melakukan operasi dosa / arcsin untuk mengkonversi antara dua representasi. Ini akan memperkenalkan sedikit bitrot dalam bit paling tidak signifikan setiap kali Anda melakukan konversi. Saya hampir yakin Anda akan berakhir dengan rasa sakit yang sama dengan apa yang saya alami mencoba berurusan dengan kelas yang menyediakan frekuensi acara dan waktu antara representasi peristiwa (x dan 1 / x) dari sesuatu. Melacak representasi mana yang kanonis di kelas dan berurusan dengan semua sakit kepala yang membulat bukanlah sesuatu yang ingin saya lakukan lagi.
Dan Neely

3
Contoh yang baik dari tipe data yang dapat mewakili banyak hal adalah string, tetapi "stringed typed" adalah antipattern. Wrt contoh Anda. Cobalah untuk mengimplementasikan produk titik untuk jenis yang mendukung kedua sistem koordinat.
Nathan Cooper

1
Anda harus berusaha untuk menggunakan berbagai jenis kapan pun berlaku, untuk mendapatkan manfaat dari keamanan jenis - ini akan mencegah Anda mengirim jenis yang salah ke / dari suatu fungsi (menggunakan pengecekan kebenaran waktu kompilasi, tergantung pada bahasa pemrograman Anda). Ini akan memudahkan perawatan karena Anda dapat menemukan semua kegunaan sebenarnya dari beberapa jenis (tanpa penggunaan yang salah karena jenis yang sedang dikonfigurasikan).
Erik Eidt

Jawaban:


17

Saya telah melihat kedua solusi, jadi pasti tergantung pada konteks.

Untuk keterbacaan, memiliki beberapa struct seperti yang Anda sarankan sangat efektif. Namun, di beberapa lingkungan, Anda ingin melakukan manipulasi umum untuk struct ini, dan Anda mendapati diri Anda menduplikasi kode, seperti operasi vektor * matriks. Ini bisa membuat frustasi ketika operasi tertentu tidak tersedia dalam rasa vektor Anda karena tidak ada yang porting di sana.

Solusi ekstrem (yang akhirnya kami berikan) adalah memiliki kelas dasar templat CRTP dengan fungsi mendapatkan <0> () mendapatkan <1> () dan mendapatkan <2> () untuk mendapatkan elemen secara generik. Fungsi-fungsi tersebut kemudian didefinisikan dalam struct Cartesian atau Polar yang berasal dari kelas dasar ini. Ini memecahkan semua masalah, tetapi datang dengan harga yang agak konyol: harus belajar pemrograman template. Namun, jika metaprogramming template sudah merupakan permainan yang adil untuk proyek Anda, itu mungkin cocok.


1
Jawaban Anda sangat menarik. apakah mungkin memberikan contoh?
Moha unta maha kuasa

1
Saya dapat mencari contoh dari apa yang telah saya lakukan: FIR menyaring kutub, kartesius dan vektornya. Matematikanya sangat mirip, tanpa sudut pembungkus, kode memiliki beberapa bagian yang digandakan karena alasan kinerja dan kami menggunakan template untuk tempat yang sama. Menggunakan nama yang berbeda untuk semua barang. "Solusi ekstrem" Cort bisa menyelamatkan beberapa duplikasi, tetapi tidak semuanya.
Eugene Ryabtsev

1
Reaksi pertama saya adalah bahwa situasi seperti ini akan lebih baik diselesaikan dengan casting, tetapi ternyata agak berisiko .
200_sukses

114

Ya, itu sangat masuk akal.

Nilai struct tidak hanya merangkum data dengan nama praktis. Nilainya adalah bahwa ia mengodifikasi niat Anda sehingga kompiler dapat membantu Anda memverifikasi bahwa Anda tidak akan melanggarnya suatu hari nanti (misalnya kesalahan kumpulan koordinat kutub untuk kumpulan koordinat kartesius).

Orang-orang buruk dalam mengingat detail-detail picik seperti itu, tetapi pandai menciptakan rencana yang berani dan inventif. Komputer bagus dalam hal detail yang mengganggu dan buruk dalam rencana kreatif. Oleh karena itu, selalu merupakan ide bagus untuk mengalihkan sebanyak mungkin pemeliharaan detail-rewel ke komputer, membuat pikiran Anda bebas untuk mengerjakan rencana besar.


6
+1 Deskripsi sempurna tentang penggunaan komputer untuk melakukan apa yang bisa dilakukan komputer dengan baik, dan membiarkan otak Anda fokus pada pekerjaan Anda sendiri.
BrianH

8
"Program harus ditulis untuk dibaca orang, dan hanya secara kebetulan untuk dijalankan mesin." - dari "Struktur dan Interpretasi Program Komputer" oleh Abelson dan Sussman.
hlovdal

18

Ya, sementara Cartesian dan Polar sama-sama (di tempat mereka) skema representasi koordinat yang masuk akal, mereka idealnya tidak boleh dicampur (jika Anda memiliki titik Cartesian {1,1}, itu adalah poin yang sangat berbeda dari Polar {1,1) }).

Tergantung pada kebutuhan Anda, itu juga mungkin layak menerapkan Berkoordinasi antarmuka, dengan metode seperti X(), Y(), Displacement()dan Angle()(atau mungkin Radius()dan Theta(), tergantung pada).


Lebih penting lagi jika OP membuat kelas dari struct tersebut, karena operasi pada koordinat kartesius dan kutub berbeda.
Mindwin

1
+1 untuk paragraf terakhir, itu adalah solusi ideal. Titik adalah ruang adalah objek; representasi internal dari titik itu seharusnya tidak masalah. Tentu saja, masalah dunia nyata (kinerja, kesalahan pembulatan) mungkin menyebabkan masalah. Itu semua tergantung pada apa ini digunakan.
BlueRaja - Danny Pflughoeft

Dan juga, untuk contoh ini, tidak mungkin berubah, tetapi jika mereka adalah 2 kelas lain, tidak ada yang memberitahu Anda bahwa mereka mungkin berbeda pada beberapa titik.
dyesdyes

8

Pada akhirnya, tujuan pemrograman adalah beralih bit transistor untuk melakukan pekerjaan yang bermanfaat. Tetapi berpikir pada tingkat rendah seperti itu akan menyebabkan kegilaan yang tidak terkendali, itulah sebabnya ada bahasa pemrograman tingkat tinggi untuk membantu Anda menyembunyikan kerumitan.

Jika Anda hanya membuat satu struct dengan anggota bernama firstdan second, maka nama tidak berarti apa-apa; Anda pada dasarnya akan memperlakukan mereka sebagai alamat memori. Itu mengalahkan tujuan dari bahasa pemrograman tingkat tinggi.

Lebih jauh lagi, hanya karena mereka semua bisa direpresentasikan sebagai doubletidak berarti bahwa Anda dapat menggunakannya secara bergantian. Misalnya, θ adalah sudut tanpa dimensi, sedangkan y memiliki satuan panjang. Karena jenis tidak dapat diganti secara logis, mereka harus dua struct yang tidak kompatibel.

Jika Anda benar - benar perlu memainkan trik penggunaan kembali memori - dan Anda hampir pasti tidak seharusnya melakukannya - Anda dapat menggunakan uniontipe dalam C untuk memperjelas niat Anda.


Jawaban terbaik, IMHO
Dean Radcliffe

2

Pertama, miliki keduanya secara eksplisit, sesuai jawaban sepenuhnya @ @ Kilian-foth.

Namun, saya ingin menambahkan:

Tanyakan: Apakah Anda benar-benar memiliki operasi yang generik untuk keduanya ketika dianggap sebagai pasangan double? Perhatikan ini tidak sama dengan mengatakan bahwa Anda memiliki operasi yang berlaku untuk keduanya dalam istilah mereka sendiri. Misalnya 'plot (Coord)' peduli apakah Coorditu Polar atau Cartesian. Di sisi lain, tetap mengajukan file hanya memperlakukan data apa adanya. Jika Anda benar-benar memiliki operasi generik, pertimbangkan untuk mendefinisikan kelas dasar, atau mendefinisikan konverter ke std::pair<double, double>atau tupleatau apa pun yang Anda miliki dalam bahasa Anda.

Lebih jauh, satu pendekatan mungkin memperlakukan satu jenis Koordinat sebagai yang lebih mendasar dan yang lainnya hanya sebagai dukungan untuk interaksi pengguna atau eksternal.

Jadi Anda mungkin memastikan semua operasi dasar dikodekan untuk Cartesiankemudian memberikan dukungan untuk mengkonversi Polarke Cartesian. Ini menghindari penerapan berbagai versi operasi yang berbeda.


1

Solusi yang mungkin, tergantung pada bahasa dan jika Anda tahu bahwa kedua kelas akan memiliki metode dan operasi yang sama, akan mendefinisikan kelas sekali dan menggunakan alias tipe untuk secara eksplisit menyebutkan jenisnya secara berbeda.

Ini juga memiliki keuntungan bahwa selama kelas-kelasnya persis sama, Anda dapat mempertahankan hanya satu, tetapi segera setelah Anda perlu mengubahnya, Anda tidak perlu memodifikasi kode menggunakan mereka karena jenis telah digunakan secara khusus.

Pilihan lain, tergantung lagi pada penggunaan kelas (jika Anda memerlukan polimorfisme dan semacamnya) adalah menggunakan warisan publik untuk kedua tipe baru, sehingga mereka memiliki antarmuka publik yang sama dengan tipe umum yang sama-sama mereka wakili. Ini juga memungkinkan untuk evolusi tipe yang terpisah.


Nama-nama anggota di kedua kelas tidak sama. sebenarnya nama-nama adalah satu-satunya perbedaan antara dua kelas
Moha unta Mahakuasa

@ Mhd.Tahawi Anda dapat menerapkan getter dan setter dengan nama yang sesuai, memastikan bahwa kelas yang Anda gunakan adalah sama, tetapi memberikan nama yang sesuai untuk operasi yang ingin Anda gunakan. Itu menjadi sedikit lebih verbose, tetapi Anda harus menduplikasi kode lebih sedikit.
Svalorzen

0

Saya percaya bahwa memiliki nama anggota yang sama adalah ide yang buruk dalam kasus ini, karena membuat kode lebih rentan kesalahan.

Bayangkan skenario: Anda memiliki beberapa poin cartesian: pntA dan pntB. Kemudian Anda memutuskan, untuk beberapa alasan, bahwa mereka harus lebih baik diwakili dalam koordinat kutub, dan mengubah deklarasi dan konstruktor.

Sekarang, jika semua operasi Anda hanya pemanggilan metode seperti:

double distance = pntA.distanceFrom(pntB);

maka kamu baik-baik saja. Tetapi bagaimana jika Anda menggunakan anggota secara eksplisit? Membandingkan

double leftMargin = abs(pntA.x - pntB.x);
double leftMargin = abs(pntA.first - pntB.first);

Dalam kasus pertama, kode tidak akan dikompilasi. Anda akan segera melihat kesalahan dan dapat memperbaikinya. Tetapi jika Anda memiliki nama anggota yang sama, kesalahannya hanya pada tingkat logis, jauh lebih sulit untuk dideteksi.

Jika Anda menulis dalam bahasa yang tidak berorientasi objek, maka lebih mudah untuk mengirimkan struct yang salah ke fungsi tersebut. Apa yang membuat Anda berhenti menulis kode berikut?

double distance = calculate_distance_polar(cartesianPointA, polarPointB);

Jenis data yang berbeda, di sisi lain, akan memungkinkan Anda untuk menemukan kesalahan selama kompilasi.

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.