Bagaimana cara menghindari kelebihan metode yang berlebihan?

16

Kami memiliki cukup banyak tempat dalam kode sumber aplikasi kami, di mana satu kelas memiliki banyak metode dengan nama dan parameter yang berbeda. Metode-metode itu selalu memiliki semua parameter metode 'sebelumnya' plus satu lagi.

Ini adalah hasil dari evolusi yang panjang (kode warisan) dan pemikiran ini (saya percaya):

" Ada metode M yang melakukan hal A. Saya perlu melakukan A + B. OK, saya tahu ... Saya akan menambahkan parameter baru ke M, buat metode baru untuk itu, pindahkan kode dari M ke metode baru dengan satu parameter lagi, lakukan A + B di sana dan panggil metode baru dari M dengan nilai default dari parameter baru. "

Berikut ini sebuah contoh (dalam bahasa mirip Java):

class DocumentHome {

  (...)

  public Document createDocument(String name) {
    // just calls another method with default value of its parameter
    return createDocument(name, -1);
  }

  public Document createDocument(String name, int minPagesCount) {
    // just calls another method with default value of its parameter
    return createDocument(name, minPagesCount, false);
  }

  public Document createDocument(String name, int minPagesCount, boolean firstPageBlank) {
    // just calls another method with default value of its parameter
    return createDocument(name, minPagesCount, false, "");
  }

  public Document createDocument(String name, int minPagesCount, boolean firstPageBlank, String title) {
    // here the real work gets done
    (...)
  }

  (...)
}

Saya merasa ini salah. Tidak hanya itu kami tidak dapat terus menambahkan parameter baru seperti ini selamanya, tetapi kode sulit diperluas / diubah karena semua dependensi antara metode.

Berikut adalah beberapa cara untuk melakukan ini dengan lebih baik:

  1. Memperkenalkan objek parameter:

    class DocumentCreationParams {
    
      String name;
      int minPagesCount;
      boolean firstPageBlank;
      String title;
    
      (...)
    }
    
    class DokumentHome {
    
      public Document createDocument(DocumentCreationParams p) {
        // here the real work gets done
        (...)
      }
    }
    
  2. Atur parameter ke DocumentHomeobjek sebelum kita memanggilcreateDocument()

      @In
      DocumentHome dh = null;
    
      (...)
    
      dh.setName(...);
      dh.setMinPagesCount(...);
      dh.setFirstPageBlank(...);
    
      Document newDocument = dh.createDocument();
    
  3. Pisahkan pekerjaan menjadi berbagai metode dan panggil mereka sesuai kebutuhan:

      @In
      DocumentHome dh = null;
    
      Document newDocument = dh.createDocument();
      dh.changeName(newDocument, "name");
      dh.addFirstBlankPage(newDocument);
      dh.changeMinPagesCount(new Document, 10);
    

Pertanyaan saya:

  1. Apakah masalah yang dijelaskan benar-benar masalah?
  2. Apa pendapat Anda tentang solusi yang disarankan? Yang mana yang Anda pilih (berdasarkan pengalaman Anda)?
  3. Bisakah Anda memikirkan solusi lain?
Ytus
sumber
1
Bahasa apa yang Anda targetkan atau apakah itu hanya umum?
Knerd
Tidak ada bahasa tertentu, hanya umum. Jangan ragu untuk menunjukkan bagaimana fitur dalam bahasa lain dapat membantu dalam hal ini.
Ytus
seperti yang saya katakan di sini programmers.stackexchange.com/questions/235096/... C # dan C ++ memiliki beberapa fitur.
Knerd
Cukup jelas bahwa pertanyaan ini berlaku untuk setiap bahasa yang mendukung kelebihan metode semacam itu.
Doc Brown
1
@DocBrown ok, tapi tidak semua bahasa mendukung alternatif yang sama;)
Knerd

Jawaban:

20

Mungkin mencoba pola pembangun ? (catatan: hasil Google cukup acak :)

var document = new DocumentBuilder()
                   .FirstPageBlank()
                   .Name("doc1final(2).doc")
                   .MinimumNumberOfPages(4)
                   .Build();

Saya tidak dapat memberikan ikhtisar lengkap mengapa saya lebih suka builder daripada opsi yang Anda berikan, tetapi Anda telah mengidentifikasi masalah besar dengan banyak kode. Jika Anda berpikir Anda membutuhkan lebih dari dua parameter untuk suatu metode, Anda mungkin memiliki kode yang terstruktur secara salah (dan beberapa orang akan membantahnya!).

Masalah dengan objek params adalah (kecuali objek yang Anda buat benar-benar nyata), Anda hanya perlu mendorong masalah ke atas, dan Anda berakhir dengan sekelompok parameter yang tidak terkait yang membentuk 'objek'.

Upaya Anda yang lain terlihat bagi saya seperti seseorang meraih pola pembangun tetapi tidak cukup sampai di sana :)

Froome
sumber
Terima kasih. Jawaban Anda bisa menjadi solusi num. 4 ya Saya mencari lebih banyak jawaban dengan cara ini: 'Dalam pengalaman saya ini mengarah ke ... dan dapat diperbaiki ...' atau 'Ketika saya melihat ini dalam kode rekan saya, saya sarankan dia untuk ... sebaliknya.'
Ytus
Saya tidak tahu ... menurut saya Anda hanya memindahkan masalahnya. Sebagai contoh, jika saya dapat membuat dokumen di berbagai bagian aplikasi, lebih baik untuk mengatur dan menguji ini mengisolasi konstruksi dokumen ini pada kelas yang terpisah (seperti menggunakan a DocumentoFactory). Memiliki builderdi tempat yang berbeda sulit untuk mengontrol perubahan masa depan pada konstruksi Dokumen (seperti menambahkan bidang wajib baru ke dokumen, misalnya) dan menambahkan kode boilerplate tambahan pada tes untuk memenuhi kebutuhan pembuat dokumen di kelas yang menggunakan pembangun.
Dherik
1

Menggunakan objek parameter adalah cara yang baik untuk menghindari kelebihan (berlebihan) metode:

  • itu membersihkan kode
  • memisahkan data dari fungsi
  • membuat kode lebih mudah dikelola

Namun, saya tidak akan terlalu jauh dengan itu.

Memiliki kelebihan di sana-sini bukan hal yang buruk. Ini didukung oleh bahasa pemrograman, jadi gunakan untuk keuntungan Anda.

Saya tidak mengetahui pola pembangun, tetapi telah menggunakannya "secara tidak sengaja" pada beberapa kesempatan. Hal yang sama berlaku di sini: jangan berlebihan. Kode dalam contoh Anda akan mendapat manfaat dari itu, tetapi menghabiskan banyak waktu mengimplementasikannya untuk setiap metode yang memiliki metode kelebihan tunggal tidak terlalu efisien.

Hanya 2 sen saya.

rampok
sumber
0

Jujur saya tidak melihat masalah besar dengan kode. Dalam C # dan C ++ Anda dapat menggunakan parameter opsional, itu akan menjadi alternatif, tetapi sejauh yang saya tahu Java tidak mendukung parameter semacam itu.

Dalam C # Anda dapat membuat semua kelebihan beban menjadi pribadi dan satu metode dengan parameter opsional adalah publik untuk memanggil hal-hal tersebut.

Untuk menjawab pertanyaan Anda bagian 2, saya akan mengambil objek parameter atau bahkan kamus / HashMap.

Seperti itu:

class DokumentHome {

  public Document createDocument(Map<string, object> params) {
    if (params.containsKey("yourkey")) {
       // do that
    }
    // here the real work gets done
    (...)
  }
}

Sebagai penafian, saya pertama-tama seorang programmer C # dan JavaScript dan kemudian seorang programmer Java.

Knerd
sumber
4
Ini solusi, tapi saya tidak berpikir itu solusi yang baik (setidaknya, tidak dalam konteks di mana typesafety diharapkan).
Doc Brown
Betul. Karena kasus seperti itu, saya suka metode kelebihan beban atau parameter opsional.
Knerd
2
Menggunakan kamus sebagai parameter adalah cara mudah untuk mengurangi jumlah parameter yang tampak ke suatu metode, tetapi itu mengaburkan dependensi aktual dari metode tersebut. Ini memaksa penelepon untuk mencari di tempat lain untuk apa yang sebenarnya perlu dalam kamus, apakah itu di komentar, panggilan lain ke metode, atau implementasi metode itu sendiri.
Mike Partridge
Gunakan kamus hanya untuk parameter yang benar-benar opsional sehingga kasing kasus dasar tidak perlu terlalu banyak membaca dokumentasi.
user949300
0

Saya pikir ini adalah kandidat yang baik untuk pola pembangun. Pola pembangun berguna ketika Anda ingin membuat objek dengan tipe yang sama, tetapi dengan representasi yang berbeda.

Dalam kasus Anda, saya akan memiliki pembangun dengan metode berikut:

SetTitle (Document document) { ... }
SetFirstPageBlank (Document document) { ... }
SetMinimumPageCount (Document document) { ... }

Anda kemudian dapat menggunakan pembuat dengan cara berikut:

_builder.SetTitle(document);
_builder.SetFirstPageBlank(document);

Di samping catatan, saya tidak keberatan beberapa kelebihan sederhana - apa-apaan, .NET framework menggunakannya di semua tempat dengan bantuan HTML. Namun, saya akan mengevaluasi kembali apa yang saya lakukan jika saya harus melewati lebih dari dua parameter untuk setiap metode.

CodeART
sumber
0

Saya pikir buildersolusinya dapat bekerja pada sebagian besar skenario, tetapi pada kasus yang lebih kompleks pembangun Anda juga akan rumit untuk diatur , karena mudah untuk melakukan beberapa kesalahan pada urutan metode, metode apa yang perlu diungkapkan atau tidak , dll. Jadi, banyak dari kita akan lebih suka solusi yang paling sederhana.

Jika Anda hanya membuat pembangun sederhana untuk membangun dokumen dan menyebarkan kode ini di berbagai bagian (kelas) aplikasi, akan sulit untuk:

  • mengatur : Anda akan memiliki banyak kelas membangun dokumen dengan berbagai cara
  • memelihara : setiap perubahan pada instantiasi dokumen (seperti menambahkan wajib baru diajukan) akan mengarahkan Anda ke Bedah Shotgun
  • test : jika Anda menguji classe yang membuat dokumen, Anda perlu menambahkan kode boilerplate hanya untuk memenuhi instantiasi dokumen.

Tapi ini tidak menjawab pertanyaan OP.

Alternatif untuk kelebihan beban

Beberapa alternatif:

  • Mengubah nama nama metode : jika nama metode yang sama adalah menciptakan beberapa kebingungan, mencoba untuk mengubah nama metode untuk membuat nama yang bermakna lebih baik dari createDocument, seperti: createLicenseDriveDocument, createDocumentWithOptionalFields, dll Tentu saja, ini dapat menyebabkan Anda untuk nama metode raksasa, jadi ini bukan solusi untuk semua kasus.
  • Gunakan metode statis . Pendekatan ini agak mirip jika dibandingkan dengan alternatif pertama di atas. Anda dapat menggunakan nama-nama bermakna untuk setiap kasus dan instantiate dokumen dari metode statis pada Document, seperti: Document.createLicenseDriveDocument().
  • Buat antarmuka umum : Anda dapat membuat metode tunggal yang disebut createDocument(InterfaceDocument interfaceDocument)dan membuat implementasi yang berbeda untuk InterfaceDocument. Per contoh: createDocument(new DocumentMinPagesCount("name")). Tentu saja Anda tidak memerlukan implementasi tunggal untuk setiap kasus, karena Anda dapat membuat lebih dari satu konstruktor pada setiap implementasi, mengelompokkan beberapa bidang yang masuk akal pada implementasi itu. Pola ini disebut konstruktor telescoping .

Atau Hanya tinggal dengan solusi kelebihan . Bahkan, kadang-kadang, solusi jelek, tidak ada banyak kelemahan dalam menggunakannya. Dalam hal ini, saya lebih suka menggunakan metode overload pada kelas yang terpisah, seperti DocumentoFactoryyang dapat disuntikkan sebagai ketergantungan pada kelas yang perlu membuat dokumen. Saya dapat mengatur dan memvalidasi bidang tanpa kerumitan membuat pembangun yang baik dan mempertahankan kode di satu tempat juga.

Dherik
sumber