Metode panjang refactoring: meninggalkan apa adanya vs memisahkan menjadi metode vs menggunakan fungsi lokal

9

Misalkan saya punya metode panjang seperti ini:

public void SomeLongMethod()
{
    // Some task #1
    ...

    // Some task #2
    ...
}

Metode ini tidak memiliki bagian berulang yang harus dipindahkan ke metode terpisah atau fungsi lokal.

Ada banyak orang (termasuk saya) yang berpikir bahwa metode panjang adalah bau kode. Juga saya tidak suka ide untuk menggunakan #regiondi sini dan ada jawaban yang sangat populer menjelaskan mengapa ini buruk .


Tetapi jika saya memisahkan kode ini menjadi metode

public void SomeLongMethod()
{
    Task1();

    Task2();
}

private void Task1()
{
    // Some task #1
    ...
}

private void Task2()
{
    // Some task #1
    ...
}

Saya melihat masalah berikut:

  • Mencemari lingkup definisi kelas dengan definisi yang digunakan secara internal oleh metode tunggal dan yang saya maksud harus mendokumentasikan suatu tempat yang Task1dan Task2dimaksudkan hanya untuk internal SomeLongMethod(atau setiap orang yang membaca kode saya harus menyimpulkan ide ini).

  • Mencemari IDE pelengkapan otomatis (misalnya Intellisense) dari metode yang hanya akan digunakan sekali di dalam SomeLongMethodmetode tunggal .


Jadi jika saya memisahkan kode metode ini menjadi fungsi-fungsi lokal

public void SomeLongMethod()
{
    Task1();

    Task2();

    void Task1()
    {
        // Some task #1
        ...
    }

    void Task2()
    {
        // Some task #1
        ...
    }
}

maka ini tidak memiliki kelemahan dari metode yang terpisah tetapi ini tidak terlihat lebih baik (setidaknya bagi saya) daripada metode aslinya.


Versi mana SomeLongMethodyang lebih mudah dikelola dan dibaca untuk Anda dan mengapa?

Vadim Ovchinnikov
sumber
@gnat pertanyaan saya khusus untuk c # , bukan java . Perhatian utama saya adalah tentang metode klasik vs fungsi lokal, tidak hanya terpisah menjadi metode atau tidak.
Vadim Ovchinnikov
@gnat Juga AFAIK Java (tidak seperti C #) tidak mendukung fungsi bersarang.
Vadim Ovchinnikov
1
kata kunci pribadi pasti melindungi 'ruang lingkup definisi kelas' Anda?
Ewan
1
tunggu ...... seberapa besar kelasmu?
Ewan

Jawaban:

13

Tugas mandiri harus dipindahkan ke metode mandiri. Tidak ada kekurangan yang cukup besar untuk mengesampingkan tujuan ini.

Anda mengatakan bahwa mereka mencemari namespace kelas, tetapi itu bukan trade-off yang harus Anda lihat ketika memfaktorkan kode Anda. Pembaca kelas yang akan datang (misalnya Anda) jauh lebih mungkin untuk bertanya, "Apa yang dilakukan metode spesifik ini dan bagaimana saya harus mengubahnya agar dapat melakukan apa yang dikatakan persyaratan yang diubah?" daripada "Apa tujuan dan arti dari semua metode di kelas? " Oleh karena itu, membuat masing-masing metode lebih mudah untuk dipahami (dengan membuatnya memiliki lebih sedikit elemen) jauh lebih penting daripada membuat seluruh kelas lebih mudah untuk dipahami (dengan membuatnya memiliki metode lebih sedikit).

Tentu saja, jika tugas tidak benar-benar mandiri tetapi memerlukan beberapa variabel status untuk bergerak bersama, maka objek metode, objek pembantu atau konstruksi serupa dapat menjadi pilihan yang tepat. Dan jika tugas-tugas yang benar-benar mandiri dan tidak terikat dengan domain aplikasi sama sekali (misalnya hanya tali format, maka menurut pikiran mereka harus pergi ke (utilitas) seluruh kelas yang berbeda. Tapi itu semua tidak mengubah fakta bahwa " menjaga metode per kelas ke bawah "adalah tujuan terbaik anak perusahaan.

Kilian Foth
sumber
Jadi Anda untuk metode, bukan fungsi lokal, ya?
Vadim Ovchinnikov
2
@VadimOvchinnikov Dengan IDE kode-lipat modern ada sedikit perbedaan. Keuntungan terbesar dari fungsi lokal adalah ia dapat mengakses variabel yang seharusnya Anda berikan sebagai parameter, tetapi jika ada banyak, maka tugasnya tidak benar-benar independen untuk memulainya.
Kilian Foth
6

Saya tidak suka pendekatan mana pun. Anda tidak memberikan konteks yang lebih luas, tetapi seringkali terlihat seperti ini:

Anda memiliki kelas yang melakukan beberapa pekerjaan dengan menggabungkan kekuatan beberapa layanan menjadi satu hasil.

class SomeClass
{
    private readonly Service1 _service1;
    private readonly Service2 _service2;

    public void SomeLongMethod()
    {
        _service1.Task1();
        _service2.Task2();       
    }
}

Setiap layanan Anda hanya mengimplementasikan sebagian dari logika. Sesuatu yang istimewa, yang hanya dapat dilakukan oleh servcice ini. Jika memiliki metode pribadi selain yang mendukung API utama, Anda dapat mengurutkannya dengan mudah dan toh seharusnya tidak terlalu banyak.

class Service1
{
    public void Task1()
    {
        // Some task #1
        ...
    }

    private void SomeHelperMethod() {}
}

class Service2
{
    void Task2()
    {
        // Some task #1
        ...
    }
}

Intinya adalah: jika Anda mulai membuat wilayah dalam kode Anda untuk menggunakan metode gorup, sudah waktunya untuk mulai berpikir mengenkapsulasi logika mereka dengan layanan baru.

t3chb0t
sumber
2

Masalah dengan membuat fungsi dalam metode ini adalah tidak mengurangi panjang metode.

Jika Anda ingin tingkat perlindungan ekstra 'privat ke metode' maka Anda dapat membuat kelas baru

public class BigClass
{
    public void SomeLongMethod();

    private class SomeLongMethodRunner
    {
        private void Task1();
        private void Task2();
    }
}

Tapi kelas-kelas di kelas jelek sekali: /

Ewan
sumber
2

Meminimalkan ruang lingkup konstruksi bahasa seperti variabel dan metode adalah praktik yang baik. Misalnya menghindari variabel global .. Ini membuat kode lebih mudah dipahami dan membantu menghindari penyalahgunaan karena konstruk dapat diakses di lebih sedikit tempat.

Ini mendukung penggunaan fungsi lokal.

fsl
sumber
1
hmm pasti menggunakan kembali kode == berbagi metode di antara banyak penelepon. yaitu memaksimalkan cakupannya
Ewan
1

Saya setuju bahwa metode yang panjang sering kali merupakan bau kode, tetapi saya rasa itu bukan gambaran keseluruhannya, tetapi karena itulah metode yang panjang itu mengindikasikan adanya masalah. Aturan umum saya adalah jika kode melakukan beberapa tugas berbeda maka masing-masing tugas tersebut harus menjadi metode tersendiri. Bagi saya tidak masalah apakah bagian-bagian kode itu berulang atau tidak; kecuali Anda benar-benar yakin bahwa tidak ada yang ingin secara individual menyebutnya secara terpisah di masa depan (dan itu hal yang sangat sulit untuk dipastikan!) menempatkan mereka dalam metode lain (dan menjadikannya pribadi - yang seharusnya menjadi indikator yang sangat kuat Anda tidak mengharapkannya digunakan di luar kelas).

Saya harus mengakui bahwa saya belum menggunakan fungsi lokal, jadi pemahaman saya jauh dari lengkap di sini, tetapi tautan yang Anda berikan menunjukkan bahwa mereka akan sering digunakan dengan cara yang mirip dengan ekspresi lambda (meskipun di sini ada sejumlah kasus penggunaan) di mana mereka mungkin lebih tepat daripada lambdas, atau di mana Anda tidak dapat menggunakan lambda lihat fungsi lokal dibandingkan dengan ekspresi lambda untuk spesifik). Di sini kemudian, saya pikir itu bisa sampai ke rincian tugas-tugas 'lain'; jika mereka cukup sepele maka mungkin fungsi lokal akan beres? Di sisi lain saya telah melihat contoh ekspresi lambda raksasa (mungkin di mana pengembang baru-baru ini menemukan LINQ) yang membuat kode jauh lebih mudah dimengerti dan dipelihara daripada jika hanya dipanggil dari metode lain.

The Fungsi lokal Halaman menyatakan bahwa:

'Fungsi lokal memperjelas maksud kode Anda. Siapa pun yang membaca kode Anda dapat melihat bahwa metode ini tidak dapat dipanggil kecuali dengan metode yang mengandung. Untuk proyek tim, mereka juga membuat mustahil bagi pengembang lain untuk secara keliru memanggil metode langsung dari tempat lain di kelas atau stuct. '

Saya tidak yakin saya benar-benar dengan MS yang satu ini sebagai alasan untuk digunakan. Ruang lingkup metode Anda (bersama dengan nama dan komentar) harus membuat jelas apa maksud Anda adalah pada saat itu . Dengan Fungsi Lokal saya bisa melihat itu, devs lain mungkin melihatnya sebagai lebih sakral, tetapi berpotensi ke titik di mana jika perubahan datang di masa depan yang mengharuskan fungsionalitas untuk digunakan kembali mereka menulis prosedur mereka sendiri dan meninggalkan Fungsi Lokal di tempat ; dengan demikian menciptakan masalah duplikasi kode. Kalimat terakhir dari kutipan di atas bisa relevan jika Anda tidak mempercayai anggota tim untuk memahami metode pribadi sebelum memanggilnya, dan menyebutnya dengan tidak tepat (sehingga keputusan Anda dapat dipengaruhi oleh hal itu, meskipun pendidikan akan menjadi pilihan yang lebih baik di sini) .

Singkatnya, secara umum, saya lebih suka memisahkan menjadi metode tetapi MS menulis Fungsi Lokal karena suatu alasan, jadi saya pasti tidak akan mengatakan tidak menggunakannya,

d219
sumber