Cara terbersih untuk menulis perangkat lunak prosedural secara logis dalam bahasa OO

12

Saya seorang insinyur listrik dan saya tidak tahu apa yang saya lakukan. Harap simpan pengelola kode saya di masa mendatang.

Baru-baru ini saya telah mengerjakan sejumlah program yang lebih kecil (dalam C #) yang fungsinya secara logis "prosedural". Sebagai contoh, salah satunya adalah program yang mengumpulkan informasi dari database yang berbeda, menggunakan informasi itu untuk menghasilkan semacam halaman ringkasan, mencetaknya, dan kemudian keluar.

Logika yang diperlukan untuk semua itu adalah sekitar 2000 baris. Saya tentu saja tidak ingin memasukkan semua itu dalam satu main()dan kemudian "membersihkannya" dengan #regions seperti yang dilakukan beberapa pengembang sebelumnya (bergidik).

Berikut adalah beberapa hal yang sudah saya coba tanpa banyak kepuasan:

Buat utilitas statis untuk setiap fungsionalitas kasar, misalnya DatabaseInfoGetter, SummaryPageGenerator, dan PrintUtility. Membuat fungsi utama terlihat seperti:

int main()
{
    var thisThing = DatabaseInfoGetter.GetThis();
    var thatThing = DatabaseInfoGetter.GetThat();
    var summary = SummaryPageGenerator.GeneratePage(thisThing, thatThing);

    PrintUtility.Print(summary);
}

Untuk satu program saya bahkan pergi dengan antarmuka

int main()
{
    /* pardon the psuedocode */

    List<Function> toDoList = new List<Function>();
    toDoList.Add(new DatabaseInfoGetter(serverUrl));
    toDoList.Add(new SummaryPageGenerator());
    toDoList.Add(new PrintUtility());

    foreach (Function f in toDoList)
        f.Do();
}

Semua ini terasa benar. Ketika kode menjadi lebih panjang, kedua pendekatan ini mulai menjadi jelek.

Apa cara yang baik untuk menyusun hal-hal seperti ini?

Lombardia
sumber
2
Saya tidak yakin ini benar-benar duplikat, tapi tolong baca Metode tunggal dengan banyak parameter vs banyak metode yang harus dipanggil secara berurutan .
@Snowman, sebagai seseorang yang baru menulis potongan kode yang lebih besar, sangat meyakinkan untuk melihat bahwa saya bukan satu-satunya orang yang mengalami jenis masalah ini. Saya menyukai jawaban Anda untuk pertanyaan itu. Itu membantu.
Lombardia
@ Lombard ide mengurangi kompleksitas ke tingkat yang dapat dikelola adalah keterampilan yang sangat baik untuk dikembangkan. Tidak ada yang bisa mengerti basis kode yang besar, bahkan Superman (mungkin Batman sekalipun). Menemukan cara untuk mengekspresikan ide secara sederhana dan mendokumentasikan sendiri kode itu sangat penting.
"Baru-baru ini saya telah mengerjakan sejumlah program yang lebih kecil (dalam C #) yang fungsinya secara logis" prosedural ". Sebagai contoh, salah satunya adalah program yang mengumpulkan informasi dari database yang berbeda, menggunakan informasi itu untuk menghasilkan semacam ringkasan halaman, cetak, dan kemudian keluar. ": Apakah Anda yakin Anda tidak membingungkan" prosedural "dengan" imperatif "? Baik berorientasi prosedural dan objek (dapat) penting, yaitu mereka dapat mengekspresikan operasi yang harus dilakukan secara berurutan.
Giorgio

Jawaban:

18

Karena Anda bukan programmer profesional, saya akan merekomendasikan untuk tetap berpegang pada kesederhanaan. Ini akan menjadi BANYAK lebih mudah bagi programmer untuk mengambil kode prosedural Anda, termodulasi dan membuatnya OO nanti, daripada bagi mereka untuk memperbaiki program OO yang ditulis dengan buruk. Jika Anda tidak berpengalaman, dimungkinkan untuk membuat program OO yang dapat berubah menjadi kekacauan yang tidak suci yang tidak akan membantu Anda atau siapa pun yang datang setelah Anda.

Saya pikir insting pertama Anda, kode "benda-hal ini" dalam contoh pertama adalah jalur yang benar. Jelas dan jelas apa yang ingin Anda lakukan. Jangan terlalu khawatir tentang efisiensi kode, kejelasan jauh lebih penting.

Jika segmen kode terlalu panjang, pecah menjadi potongan-potongan berukuran gigitan, masing-masing memiliki fungsinya sendiri. Jika terlalu pendek, pertimbangkan untuk menggunakan lebih sedikit modul dan membuat lebih banyak sambungan.

---- Catatan tambahan: Perangkap Desain OO

Bekerja dengan sukses dengan pemrograman OO bisa rumit. Bahkan ada beberapa orang yang menganggap seluruh model cacat. Ada buku yang sangat bagus yang saya gunakan ketika pertama kali belajar pemrograman OO bernama Thinking in Java (sekarang dalam edisi ke-4). Penulis yang sama memiliki buku yang sesuai untuk C ++. Sebenarnya ada pertanyaan lain pada Programmer yang berurusan dengan perangkap umum dalam pemrograman berorientasi objek .

Beberapa perangkap sangat canggih, tetapi ada banyak cara untuk menciptakan masalah dengan cara yang sangat mendasar. Sebagai contoh, beberapa tahun yang lalu ada seorang magang di perusahaan saya yang menulis versi pertama dari beberapa perangkat lunak yang saya warisi dan dia membuat antarmuka untuk semua yang mungkinsuatu hari nanti memiliki beberapa implementasi. Tentu saja, dalam 98% kasus hanya ada satu implementasi tunggal, jadi kode itu dimuat dengan antarmuka yang tidak digunakan yang membuat debugging sangat menjengkelkan karena Anda tidak dapat mundur melalui panggilan antarmuka, sehingga Anda akhirnya harus melakukan pencarian teks untuk implementasi (meskipun sekarang saya menggunakan IntelliJ memiliki fitur "Tampilkan Semua Implementasi", tetapi pada hari saya tidak memilikinya). Aturannya di sini sama dengan pemrograman prosedural: selalu hardcode satu hal. Hanya ketika Anda memiliki dua hal atau lebih, buat abstraksi.

Jenis kesalahan desain yang serupa dapat ditemukan di Java Swing API. Mereka menggunakan model terbitkan-berlangganan untuk sistem menu Swing. Ini membuat membuat dan men-debug menu di Swing menjadi mimpi buruk yang lengkap. Ironinya adalah itu sama sekali tidak ada gunanya. Hampir tidak pernah ada situasi di mana beberapa fungsi perlu "berlangganan" ke klik menu. Juga, terbitkan-berlangganan adalah macet total di sana karena sistem menu biasanya selalu digunakan. Ini tidak seperti fungsi berlangganan dan kemudian berhenti berlangganan secara acak. Fakta bahwa pengembang "profesional" di Sun membuat kesalahan seperti ini hanya menunjukkan betapa mudahnya bahkan bagi para profesional untuk membuat kesalahan besar dalam desain OO.

Saya adalah pengembang yang sangat ahli dengan pengalaman puluhan tahun dalam pemrograman OO, tetapi bahkan saya akan menjadi orang pertama yang mengakui ada banyak yang tidak saya ketahui dan bahkan sekarang saya sangat berhati-hati dalam menggunakan banyak OO. Saya biasa mendengarkan ceramah panjang dari seorang rekan kerja yang adalah seorang fanatik OO tentang bagaimana melakukan desain tertentu. Dia benar-benar tahu apa yang dia lakukan, tetapi sejujurnya saya kesulitan memahami program-programnya karena mereka memiliki model desain yang begitu canggih.

Tyler Durden
sumber
1
+1 untuk "kejelasan jauh lebih penting [daripada efisiensi]"
Stephen
Bisakah Anda mengarahkan saya ke tautan yang menggambarkan apa yang merupakan "program OO yang ditulis dengan buruk" atau menambahkan beberapa informasi tentang hal itu dalam sebuah pengeditan?
Lombard
@ Bardard saya melakukan itu.
Tyler Durden
1

Pendekatan pertama baik-baik saja. Dalam bahasa prosedural, seperti C, pendekatan standar adalah untuk membagi fungsionalitas menjadi ruang nama - menggunakan kelas statis seperti DatabaseInfoGetterpada dasarnya adalah hal yang sama. Jelas pendekatan ini mengarah pada kode yang lebih sederhana, lebih modular dan lebih mudah dibaca / dipelihara daripada mendorong semuanya menjadi satu kelas, bahkan jika semuanya dipecah menjadi metode.

Omong-omong, cobalah membatasi metode untuk melakukan tindakan sesedikit mungkin. Beberapa programmer lebih suka sedikit kurang granularity, tetapi metode besar selalu dianggap berbahaya.

Jika Anda masih berjuang dengan kompleksitas, kemungkinan besar Anda perlu memecah program Anda lebih jauh. Buat lebih banyak level dalam hierarki - mungkin DatabaseInfoGetterperlu merujuk beberapa kelas lain seperti ProductTableEntryatau sesuatu. Anda sedang menulis kode prosedural tetapi ingat, Anda ADALAH menggunakan C #, dan OOP memberi Anda banyak alat untuk mengurangi kerumitan, misalnya:

int main() 
{
    var thisthat = Database.GetThisThat();
}

public class ThisThatClass
{
    public String This;
    public String That;
}

public static class Database 
{
    public ThisThatClass GetThisThat()
    {
        return new ThisThatClass 
        {
            This = GetThis(),
            That = GetThat()
        };
    }

    public static String GetThis() 
    { 
        //stuff
    }
    public static String GetThat() 
    { 
        //stuff
    }

}

Juga, berhati-hatilah dengan kelas statis. Kelas basis data adalah kandidat yang baik .. selama Anda biasanya memiliki satu basis data .. Anda mendapatkan idenya.

Pada akhirnya jika Anda berpikir dalam fungsi matematika dan C # tidak sesuai dengan gaya Anda, coba sesuatu seperti Scala, Haskell, dll. Mereka juga keren.

Keraguan
sumber
0

Saya merespons terlambat 4 tahun tetapi ketika Anda mencoba melakukan hal-hal prosedural dalam bahasa OO maka Anda bekerja pada antipattern. Itu bukan sesuatu yang harus Anda lakukan! Saya menemukan sebuah blog yang membahas antipattern ini dan menunjukkan solusi untuknya, tetapi membutuhkan banyak refactoring dan perubahan pola pikir. Lihat kode prosedural dalam kode Berorientasi Objek untuk lebih jelasnya.
Seperti yang Anda sebutkan "ini" dan "itu", Anda pada dasarnya menyarankan bahwa Anda memiliki dua kelas yang berbeda. A Kelas ini (nama buruk!) Dan kelas itu. Dan misalnya Anda bisa menerjemahkan ini:

var summary = SummaryPageGenerator.GeneratePage(thisThing, thatThing);

dalam hal ini:

var summary = SummaryPageGenerator.GeneratePage(new This(DatabaseInfoGetter), new That(DatabaseInfoGetter));

Sekarang, akan menarik untuk melihat 2.000+ baris kode prosedural dan menentukan bagaimana berbagai bagian dapat dikelompokkan bersama dalam kelas yang terpisah dan memindahkan berbagai prosedur ke dalam kelas tersebut.
Setiap kelas harus sederhana dan fokus hanya pada melakukan satu hal. Dan sepertinya bagi saya bahwa beberapa bagian dari kode sudah berurusan dengan kacamata super, yang sebenarnya terlalu besar untuk berguna. DatabaseInfoGetter, misalnya, terdengar membingungkan. Apa itu? Kedengarannya terlalu generik. Namun SummaryPageGenerator sangat spesifik! Dan PrintUtility menjadi generik lagi.
Jadi, untuk memulai, Anda harus menghindari nama generik untuk kelas Anda dan mulai menggunakan nama yang lebih spesifik. Kelas PrintUtility, misalnya, akan lebih berupa kelas Print dengan kelas Header, kelas Footer, kelas TextBlock, kelas gambar dan mungkin beberapa cara untuk menunjukkan bagaimana mereka akan mengalir dalam cetakan itu sendiri. Semua ini bisa di PrintUtility namespace , meskipun.
Untuk database, cerita serupa. Bahkan jika Anda menggunakan Kerangka Entitas untuk akses basis data, Anda harus membuatnya terbatas pada hal-hal yang Anda gunakan, dan membaginya menjadi grup logis.
Tapi saya ingin tahu apakah Anda masih berurusan dengan masalah ini setelah 4 tahun. Jika demikian, maka itu adalah pola pikir yang perlu diubah dari "konsep prosedural" dan menjadi "konsep berorientasi objek". Yang menantang jika Anda tidak memiliki pengalaman ...

Wim sepuluh Brink
sumber
-3

Saya berpendapat, Anda harus mengubah kode Anda sehingga Sederhana, Konsisten, dan Mudah Dibaca.

Saya menulis beberapa posting blog tentang topik ini, dan percayalah, mereka mudah dimengerti: Apa itu kode yang baik

Sederhana: Sama seperti papan sirkuit berisi bagian yang berbeda, masing-masing dengan tanggung jawab. Kode harus dibagi menjadi bagian-bagian yang lebih kecil dan lebih sederhana.

Konsisten: Gunakan patters, standar untuk penamaan elemen dan hal-hal. Anda menyukai alat yang bekerja dengan cara yang sama sepanjang waktu dan Anda ingin tahu di mana menemukannya. Sama dengan kode.

Dapat dibaca: Jangan menggunakan akronim kecuali mereka digunakan secara luas dan dikenal dalam domain yang ditargetkan oleh perangkat lunak (mattnz), lebih suka nama yang diucapkan yang jelas dan mudah dipahami.

Pablo Granados
sumber
1
Akronim dapat diterima jika digunakan secara luas dan dikenal dalam domain yang ditargetkan oleh perangkat lunak. Cobalah menulis perangkat lunak Kontrol Lalu Lintas Udara tanpa menggunakan - kami memiliki akronim yang dibuat dari akronim - tidak ada yang akan tahu apa yang Anda bicarakan adalah Anda menggunakan nama lengkap. Hal-hal seperti HTTP untuk jaringan, ABS di otomotif dll sepenuhnya dapat diterima.
mattnz