Apakah kita menyalahgunakan metode statis?

13

Beberapa bulan yang lalu saya mulai bekerja di proyek baru, dan ketika melalui kode itu membuat saya jumlah metode statis yang digunakan. Tidak hanya metode utilitas collectionToCsvString(Collection<E> elements), tetapi juga banyak logika bisnis disimpan di dalamnya.

Ketika saya bertanya kepada orang yang bertanggung jawab untuk alasan di balik ini, dia mengatakan itu adalah cara untuk melarikan diri dari tirani Spring . Ada sesuatu di sekitar proses berpikir ini: untuk menerapkan metode pembuatan tanda terima pelanggan, kami dapat memiliki layanan

@Service
public class CustomerReceiptCreationService {

    public CustomerReceipt createReceipt(Object... args) {
        CustomerReceipt receipt = new CustomerReceipt();
        // creation logic
        return receipt;
    }
}

Sekarang, pria itu mengatakan bahwa dia tidak suka memiliki kelas yang dikelola secara tidak perlu oleh Spring, pada dasarnya karena memberlakukan batasan bahwa kelas klien haruslah kacang Spring sendiri. Kami akhirnya memiliki segalanya yang dikelola oleh Spring, yang cukup banyak memaksa kami untuk bekerja dengan objek stateless dengan cara prosedural. Kurang lebih apa yang dinyatakan di sini https://www.javacodegeeks.com/2011/02/domain-driven-design-spring-aspectj.html

Jadi alih-alih kode di atas, dia punya

public class CustomerReceiptCreator {

    public static CustomerReceipt createReceipt(Object... args) {
        CustomerReceipt receipt = new CustomerReceipt();
        // creation logic
        return receipt;
    }
}

Saya bisa berdebat sampai menghindari Spring mengelola kelas kami jika memungkinkan, tetapi apa yang saya tidak lihat adalah manfaat dari memiliki semuanya yang statis. Metode statis ini juga stateless, jadi juga tidak terlalu OO. Saya akan merasa lebih nyaman dengan sesuatu

new CustomerReceiptCreator().createReceipt()

Dia mengklaim bahwa metode statis memiliki beberapa manfaat tambahan. Yaitu:

  • Lebih mudah dibaca. Impor metode statis dan kita hanya perlu peduli dengan tindakan, tidak ada kelas apa yang melakukannya.
  • Jelas merupakan metode yang bebas dari panggilan DB, jadi kinerja-bijaksana murah; dan itu adalah hal yang baik untuk membuatnya jelas, sehingga klien potensial perlu masuk ke kode dan memeriksa itu.
  • Lebih mudah menulis tes.

Tapi saya hanya merasa ada sesuatu yang tidak sepenuhnya benar dengan ini, jadi saya ingin mendengar beberapa pengembang berpengalaman tentang hal ini.

Jadi pertanyaan saya adalah, apa potensi jebakan dari cara pemrograman ini?

pengguna3748908
sumber
4
The staticmetode yang Anda menggambarkan di atas hanya sebuah metode pabrik biasa. Membuat metode pabrik statis adalah konvensi yang diterima secara umum, karena sejumlah alasan kuat. Apakah metode pabrik sesuai di sini adalah masalah yang berbeda.
Robert Harvey

Jawaban:

23

Apa perbedaan antara new CustomerReceiptCreator().createReceipt()dan CustomerReceiptCreator.createReceipt()? Cukup banyak. Satu-satunya perbedaan signifikan adalah bahwa kasus pertama memiliki sintaks yang jauh lebih canggung. Jika Anda mengikuti yang pertama dengan keyakinan bahwa entah bagaimana menghindari metode statis membuat kode Anda lebih baik OO, Anda salah besar. Membuat objek hanya untuk memanggil metode tunggal di atasnya adalah metode statis dengan sintaks tumpul.

Di mana hal-hal menjadi berbeda adalah ketika Anda menyuntikkan CustomerReceiptCreatorbukannya newing itu. Mari kita pertimbangkan sebuah contoh:

class OrderProcessor {
    @Inject CustomerReceiptCreator customerReceiptCreator;

    void processOrder(Order order) {
        ...
        CustomerReceipt receipt = customerReceiptCreator.createReceipt(order);
        ...
    }
}

Mari kita bandingkan ini versi metode statis:

void processOrder(Order order) {
    ...
    CustomerReceipt receipt = CustomerReceiptCreator.createReceipt(order);
    ...
}

Keuntungan dari versi statis adalah bahwa saya dapat dengan mudah memberi tahu Anda bagaimana ia berinteraksi dengan seluruh sistem. Tidak. Jika saya belum menggunakan variabel global, saya tahu bahwa sisa dari sistem entah bagaimana belum diubah. Saya tahu bahwa tidak ada bagian lain dari sistem yang mungkin mempengaruhi penerimaan di sini. Jika saya telah menggunakan objek yang tidak dapat diubah, saya tahu bahwa pesanan tidak berubah, dan createReceipt adalah fungsi murni. Dalam hal ini, saya dapat dengan bebas memindahkan / menghapus / etc panggilan ini tanpa khawatir tentang efek acak yang tidak terduga di tempat lain.

Saya tidak bisa membuat jaminan yang sama jika saya menyuntikkan CustomerReceiptCreator. Mungkin memiliki kondisi internal yang diubah oleh panggilan, saya mungkin terpengaruh atau mengubah status lainnya. Mungkin ada hubungan yang tak terduga antara pernyataan dalam fungsi saya sehingga mengubah urutan akan menghasilkan bug yang mengejutkan.

Di sisi lain, apa yang terjadi jika CustomerReceiptCreatortiba-tiba membutuhkan ketergantungan baru? Katakanlah itu perlu memeriksa bendera fitur. Jika kami menyuntikkan, kami dapat melakukan sesuatu seperti:

public class CustomerReceiptCreator {
    @Injected FeatureFlags featureFlags;

    public CustomerReceipt createReceipt(Order order) {
        CustomerReceipt receipt = new CustomerReceipt();
        // creation logic
        if (featureFlags.isFlagSet(Flags::FOOBAR)) {
           ...
        }
        return receipt;
    }
}

Kemudian kita selesai, karena kode panggilan akan disuntikkan dengan CustomerReceiptCreatoryang secara otomatis akan disuntikkan a FeatureFlags.

Bagaimana jika kami menggunakan metode statis?

public class CustomerReceiptCreator {
    public static CustomerReceipt createReceipt(Order order, FeatureFlags featureFlags) {
        CustomerReceipt receipt = new CustomerReceipt();
        // creation logic
        if (featureFlags.isFlagSet(Flags::FOOBAR)) {
           ...
        }
        return receipt;
    }
}

Tapi tunggu! kode panggilan juga perlu diperbarui:

void processOrder(Order order) {
    ...
    CustomerReceipt receipt = CustomerReceiptCreator.createReceipt(order, featureFlags);
    ...
}

Tentu saja, ini masih menyisakan pertanyaan dari mana processOrdermendapatkannya FeatureFlags. Jika kita beruntung, jejak berakhir di sini, jika tidak, perlunya melewati FeatureFlags didorong lebih jauh ke atas tumpukan.

Ada pertukaran di sini. Metode statis yang secara eksplisit mewariskan dependensi yang menghasilkan lebih banyak pekerjaan. Metode injeksi mengurangi pekerjaan, tetapi membuat dependensi tersirat dan dengan demikian disembunyikan membuat kode lebih sulit untuk dipikirkan.

Winston Ewert
sumber