Saya selalu melihat abstraksi adalah fitur yang sangat berguna yang disediakan OO untuk mengelola basis kode. Tapi bagaimana basis kode non-OO besar dikelola? Atau apakah mereka hanya menjadi " Bola Besar Lumpur " pada akhirnya?
Pembaruan:
Sepertinya semua orang berpikir 'abstraksi' hanyalah modularisasi atau penyembunyian data. Tapi IMHO, itu juga berarti penggunaan 'Kelas Abstrak' atau 'Antarmuka' yang merupakan keharusan untuk injeksi ketergantungan dan dengan demikian menguji. Bagaimana basis kode non-OO mengelola ini? Dan juga, selain abstraksi, enkapsulasi juga banyak membantu untuk mengelola basis kode besar saat mendefinisikan dan membatasi hubungan antara data dan fungsi.
Dengan C, sangat mungkin untuk menulis kode pseudo-OO. Saya tidak tahu banyak tentang bahasa non-OO lainnya. Jadi, apakah ini cara untuk mengelola basis kode C yang besar?
sumber
Jawaban:
Anda tampaknya berpikir bahwa OOP adalah satu-satunya cara untuk mencapai abstraksi.
Meskipun OOP tentu sangat pandai dalam melakukan itu, tidak berarti satu-satunya cara. Proyek-proyek besar juga dapat dikelola dengan modularisasi tanpa kompromi (lihat saja Perl atau Python, keduanya unggul dalam hal itu, dan begitu juga bahasa fungsional seperti ML dan Haskell), dan dengan menggunakan mekanisme seperti template (dalam C ++).
sumber
static
pengubah akses terlampir.Modul, fungsi (eksternal / internal), subrutin ...
seperti Konrad katakan, OOP bukan satu-satunya cara untuk mengelola basis kode besar. Sebagai soal fakta, banyak perangkat lunak ditulis sebelum itu (sebelum C ++ *).
sumber
Prinsip modularitas tidak terbatas pada bahasa berorientasi objek.
sumber
Secara realistis baik perubahan yang jarang terjadi (pikirkan perhitungan pensiun Jaminan Sosial) dan / atau pengetahuan yang sudah berurat berakar karena orang-orang yang memelihara sistem seperti itu telah melakukannya untuk sementara waktu (yang sinis diambil adalah keamanan pekerjaan).
Solusi yang lebih baik adalah validasi berulang, yang saya maksudkan tes otomatis (misalnya pengujian unit) dan pengujian manusia yang mengikuti langkah-langkah yang dilarang (misalnya pengujian regresi) "sebagai lawan klik-klik dan lihat apa yang pecah".
Untuk mulai bergerak menuju semacam pengujian otomatis dengan basis kode yang ada, saya sarankan membaca Michael Feather's Bekerja Efektif dengan Kode Legacy , yang merinci pendekatan untuk membawa basis kode yang ada sampai semacam kerangka pengujian berulang OO atau tidak. Ini mengarah pada jenis ide yang dijawab oleh orang lain seperti modularisasi, tetapi buku ini menjelaskan pendekatan yang tepat untuk melakukannya tanpa merusak barang-barang.
sumber
Meskipun injeksi dependensi berdasarkan antarmuka atau kelas abstrak adalah cara yang sangat baik untuk melakukan pengujian, itu tidak perlu. Jangan lupa bahwa hampir semua bahasa memiliki fungsi pointer atau eval, yang dapat melakukan apa pun yang dapat Anda lakukan dengan antarmuka atau kelas abstrak (masalahnya adalah mereka dapat melakukan lebih banyak , termasuk banyak hal buruk, dan mereka tidak t sendiri menyediakan metadata). Program seperti itu sebenarnya dapat mencapai injeksi ketergantungan dengan mekanisme ini.
Saya merasa sangat teliti dengan metadata sangat membantu. Dalam bahasa OO hubungan antara bit kode didefinisikan (sampai tingkat tertentu) oleh struktur kelas, dengan cara yang cukup standar untuk memiliki hal-hal seperti API refleksi. Dalam bahasa prosedural akan sangat membantu jika Anda menciptakannya sendiri.
Saya juga menemukan pembuatan kode jauh lebih bermanfaat dalam bahasa prosedural (dibandingkan dengan bahasa berorientasi objek). Ini menjamin meta-data selaras dengan kode (karena digunakan untuk menghasilkannya) dan memberi Anda sesuatu yang mirip titik potong pemrograman berorientasi aspek - tempat Anda dapat menyuntikkan kode saat Anda membutuhkannya. Kadang-kadang itu satu-satunya cara untuk melakukan pemrograman KERING di lingkungan seperti itu yang bisa saya pikirkan.
sumber
Sebenarnya, seperti yang baru-baru ini Anda temukan , fungsi urutan pertama adalah semua yang Anda butuhkan untuk inversi ketergantungan.
C mendukung fungsi urutan pertama dan bahkan penutupan sampai batas tertentu . Dan makro C adalah fitur yang kuat untuk pemrograman generik, jika ditangani dengan hati-hati.
Semuanya ada di sana. SGLIB adalah contoh yang cukup baik tentang bagaimana C dapat digunakan untuk menulis kode yang sangat dapat digunakan kembali. Dan saya percaya ada banyak lagi di luar sana.
sumber
Bahkan tanpa abstraksi, sebagian besar program dipecah menjadi beberapa bagian. Bagian-bagian itu biasanya berhubungan dengan tugas atau kegiatan tertentu dan Anda mengerjakannya dengan cara yang sama seperti Anda bekerja pada bit paling spesifik dari program yang diabstraksi.
Dalam proyek-proyek kecil hingga menengah, ini sebenarnya lebih mudah dilakukan dengan implementasi OO murni.
sumber
Abstraksi, kelas abstrak, injeksi dependensi, enkapsulasi, antarmuka dan sebagainya, bukan satu-satunya cara mengendalikan basis kode besar; ini adil dan berorientasi objek.
Rahasia utamanya adalah untuk menghindari berpikir OOP saat mengkode non-OOP.
Modularitas adalah kunci dalam bahasa non-OO. Dalam C ini dicapai seperti yang David Thornley sebutkan dalam komentar:
sumber
Salah satu cara mengelola kode adalah menguraikannya ke dalam jenis kode berikut, di sepanjang garis arsitektur MVC (model-view-controller).
Metode organisasi kode ini bekerja dengan baik untuk perangkat lunak yang ditulis dalam bahasa OO atau non-OO karena pola desain yang umum sering terjadi pada masing-masing bidang. Juga, jenis batas kode ini sering kali paling longgar digabungkan, kecuali algoritma karena mereka menghubungkan bersama format data dari input ke model dan kemudian ke output.
Evolusi sistem sering kali berbentuk perangkat lunak Anda menangani lebih banyak jenis input, atau lebih banyak jenis output, tetapi model dan pandangannya sama dan pengontrol berperilaku sangat mirip. Atau suatu sistem mungkin dari waktu ke waktu perlu mendukung lebih banyak jenis keluaran yang berbeda walaupun input, model, algoritmenya sama, dan pengontrol dan pandangannya serupa. Atau suatu sistem dapat ditambah untuk menambahkan model dan algoritma baru untuk rangkaian input yang sama, output yang sama, dan tampilan yang serupa.
Salah satu cara pemrograman OO membuat organisasi kode sulit adalah karena beberapa kelas sangat terkait dengan struktur data yang persisten, dan beberapa tidak. Jika struktur data yang persisten terkait erat dengan hal-hal seperti hubungan cascading 1: N atau hubungan m: n, sangat sulit untuk menentukan batasan kelas sampai Anda telah mengode bagian penting dan bermakna dari sistem Anda sebelum Anda tahu Anda melakukannya dengan benar . Setiap kelas yang terkait dengan struktur data persisten akan sulit untuk berkembang ketika skema perubahan data persisten. Kelas yang menangani algoritma, pemformatan, dan penguraian cenderung rentan terhadap perubahan dalam skema struktur data persisten. Menggunakan jenis kode organisasi MVC lebih baik mengisolasi perubahan kode paling berantakan ke kode model.
sumber
Ketika bekerja dalam bahasa yang tidak memiliki struktur bawaan dan fitur organisasi (misalnya jika tidak memiliki ruang nama, paket, rakitan dll ...) atau di mana ini tidak cukup untuk menjaga basis kode dari ukuran yang terkendali, respons alami adalah untuk mengembangkan strategi kita sendiri untuk mengatur kode.
Strategi organisasi ini mungkin termasuk standar yang berkaitan dengan di mana file yang berbeda harus disimpan, hal-hal yang perlu terjadi sebelum / setelah jenis operasi tertentu, dan konvensi penamaan dan standar pengkodean lainnya, serta banyak "ini adalah bagaimana hal itu diatur - jangan macam-macam dengan itu! " ketik komentar - yang valid selama mereka menjelaskan alasannya!
Karena strategi kemungkinan besar akan berakhir disesuaikan dengan kebutuhan spesifik proyek (orang, teknologi, lingkungan dll ...) sulit untuk memberikan solusi satu ukuran untuk semua untuk mengelola basis kode yang besar.
Oleh karena itu saya percaya saran terbaik adalah merangkul strategi spesifik proyek, dan menjadikan pengelolaannya sebagai prioritas utama: mendokumentasikan struktur, mengapa demikian, proses untuk membuat perubahan, mengauditnya untuk memastikan itu dipatuhi, dan yang terpenting: ubah ketika perlu diubah.
Kita sebagian besar terbiasa dengan kelas dan metode refactoring, tetapi dengan basis kode besar dalam bahasa seperti itu itu adalah strategi pengorganisasian itu sendiri (lengkap dengan dokumentasi) yang perlu di refactored jika diperlukan.
Alasannya sama dengan untuk refactoring: Anda akan mengembangkan mental block untuk bekerja pada bagian kecil dari sistem jika Anda merasa bahwa keseluruhan organisasi itu berantakan, dan pada akhirnya akan membiarkannya memburuk (setidaknya itulah pendapat saya tentang saya t).
Peringatannya juga sama: gunakan pengujian regresi, pastikan Anda dapat dengan mudah kembali jika refactoring salah, dan rancang sedemikian untuk memfasilitasi refactoring di tempat pertama (atau Anda tidak akan melakukannya!).
Saya setuju bahwa ini jauh lebih sulit daripada refactoring kode langsung, dan lebih sulit untuk memvalidasi / menyembunyikan waktu dari manajer / klien yang mungkin tidak mengerti mengapa itu perlu dilakukan, tetapi ini juga merupakan jenis proyek yang paling rentan terhadap pembusukan perangkat lunak. disebabkan oleh desain tingkat atas yang tidak fleksibel ...
sumber
Jika Anda bertanya tentang pengelolaan basis kode yang besar, Anda bertanya bagaimana menjaga basis kode Anda terstruktur dengan baik pada tingkat yang relatif kasar (perpustakaan / modul / pembangunan subsistem / menggunakan ruang nama / memiliki dokumen yang tepat di tempat yang tepat dll.) Prinsip OO, terutama 'kelas abstrak' atau 'antarmuka', adalah prinsip untuk menjaga kode Anda tetap bersih secara internal, pada tingkat yang sangat terperinci. Dengan demikian, teknik untuk menjaga basis kode yang besar dapat dikelola tidak berbeda untuk kode OO atau non OO.
sumber
Cara penanganannya adalah Anda mengetahui batas elemen yang Anda gunakan. Misalnya, elemen-elemen berikut dalam C ++ memiliki batas yang jelas dan setiap dependensi di luar perbatasan harus dipikirkan dengan cermat:
Menggabungkan elemen-elemen ini dan mengenali batas-batasnya, Anda dapat membuat hampir semua gaya pemrograman yang Anda inginkan dalam c ++.
Contoh dari ini adalah untuk fungsi akan menyadari bahwa itu buruk untuk memanggil fungsi lain dari suatu fungsi, karena hal itu menyebabkan ketergantungan, sebaliknya, Anda hanya harus memanggil fungsi anggota dari parameter fungsi asli.
sumber
Tantangan teknis terbesar adalah masalah namespace. Tautan sebagian dapat digunakan untuk mengatasi hal ini. Pendekatan yang lebih baik adalah merancang menggunakan standar pengkodean. Kalau tidak semua simbol menjadi berantakan.
sumber
Emacs adalah contoh yang bagus untuk ini:
Tes Emacs Lisp menggunakan
skip-unless
danlet-bind
untuk melakukan deteksi fitur dan perlengkapan pengujian:Seperti SQLite. Ini dia desainnya:
sqlite3_open () → Buka koneksi ke database SQLite baru atau yang sudah ada. Konstruktor untuk sqlite3.
sqlite3 → Objek koneksi database. Dibuat oleh sqlite3_open () dan dihancurkan oleh sqlite3_close ().
sqlite3_stmt → Objek pernyataan yang disiapkan. Dibuat oleh sqlite3_prepare () dan dihancurkan oleh sqlite3_finalize ().
sqlite3_prepare () → Kompilasi teks SQL ke dalam byte-code yang akan melakukan pekerjaan query atau memperbarui database. Konstruktor untuk sqlite3_stmt.
sqlite3_bind () → Simpan data aplikasi ke dalam parameter SQL asli.
sqlite3_step () → Tingkatkan sqlite3_stmt ke baris hasil selanjutnya atau hingga selesai.
sqlite3_column () → Nilai kolom di baris hasil saat ini untuk sqlite3_stmt.
sqlite3_finalize () → Destructor untuk sqlite3_stmt.
sqlite3_exec () → Fungsi wrapper yang melakukan sqlite3_prepare (), sqlite3_step (), sqlite3_column (), dan sqlite3_finalize () untuk serangkaian satu atau lebih pernyataan SQL.
sqlite3_close () → Destructor untuk sqlite3.
SQLite menggunakan berbagai teknik pengujian termasuk:
Referensi
Pandangan Konseptual Arsitektur Emacs (pdf)
Antarmuka OS SQLite atau "VFS"
Mekanisme Tabel Virtual Dari SQLite
Pengantar Antarmuka SQLite C / C ++
Pemrograman Emacs-Elisp · GitHub
Tes dan Lingkungannya - Pengujian Regresi Lisp Emacs
Bagaimana SQLite Diuji
sumber