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 X
dan Y
anggota dan satu dengan R
dan Theta
anggota.
Atau itu terlalu banyak dan lebih baik hanya memiliki satu struct dengan first
dan second
sebagai 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.
Jawaban:
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.
sumber
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.
sumber
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()
danAngle()
(atau mungkinRadius()
danTheta()
, tergantung pada).sumber
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
first
dansecond
, 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
double
tidak 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
union
tipe dalam C untuk memperjelas niat Anda.sumber
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 apakahCoord
itu 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 kestd::pair<double, double>
atautuple
atau 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
Cartesian
kemudian memberikan dukungan untuk mengkonversiPolar
keCartesian
. Ini menghindari penerapan berbagai versi operasi yang berbeda.sumber
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.
sumber
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:
maka kamu baik-baik saja. Tetapi bagaimana jika Anda menggunakan anggota secara eksplisit? Membandingkan
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?
Jenis data yang berbeda, di sisi lain, akan memungkinkan Anda untuk menemukan kesalahan selama kompilasi.
sumber