Kelas beton mengejek - Tidak dianjurkan

11

Saya baru saja membaca kutipan dari buku "Growing Object-Oriented Software" yang menjelaskan beberapa alasan mengapa mengejek kelas beton tidak dianjurkan.

Di sini beberapa contoh kode unit-test untuk kelas MusicCentre:

public class MusicCentreTest {
  @Test public void startsCdPlayerAtTimeRequested() {
    final MutableTime scheduledTime = new MutableTime();
    CdPlayer player = new CdPlayer() { 
      @Override 
      public void scheduleToStartAt(Time startTime) {
        scheduledTime.set(startTime);
      }
    }

    MusicCentre centre = new MusicCentre(player);
    centre.startMediaAt(LATER);

    assertEquals(LATER, scheduledTime.get());
  }
}

Dan penjelasan pertamanya:

Masalah dengan pendekatan ini adalah bahwa ia meninggalkan hubungan antara objek yang tersirat. Saya harap kami telah menjelaskan sekarang bahwa tujuan Pengembangan Didorong-Tes dengan Mock Objects adalah untuk menemukan hubungan antara objek. Jika saya subkelas, tidak ada dalam kode domain untuk membuat hubungan seperti itu terlihat, hanya metode pada objek. Ini membuatnya lebih sulit untuk melihat apakah layanan yang mendukung hubungan ini mungkin relevan di tempat lain dan saya harus melakukan analisis lagi lain kali saya bekerja dengan kelas.

Saya tidak tahu apa yang dia maksud ketika dia mengatakan:

Ini membuatnya lebih sulit untuk melihat apakah layanan yang mendukung hubungan ini mungkin relevan di tempat lain dan saya harus melakukan analisis lagi lain kali saya bekerja dengan kelas.

Saya mengerti bahwa layanan ini sesuai dengan MusicCentremetode yang disebut startMediaAt.

Apa yang dia maksud dengan "di tempat lain"?

Kutipan lengkap ada di sini: http://www.mockobjects.com/2007/04/test-smell-mocking-concrete-classes.html

Mik378
sumber
Menambahkan komentar di blognya, karena saya tidak dapat mengetahui apa yang dia maksud dari kutipan ini.
oligofren
@oligofren Ini benar-benar teka-teki hebat :) ...
Mik378

Jawaban:

6

Penulis posting itu mempromosikan penggunaan Antarmuka atas penggunaan kelas anggota.

It turns out that my MusicCentre object only uses the starting and stopping methods on the CdPlayer, the rest are used by some other part of the system. I'm over-specifying my MediaCentre by requiring it to talk to a CdPlayer, what it actually needs is a ScheduledDevice.

Hubungan yang dia khawatirkan akan ditemukan kembali nanti adalah fakta bahwa kelas MediaCentre tidak membutuhkan semua objek CdPlayer. Klaimnya adalah bahwa dengan menggunakan Antarmuka (mungkin terbatas hanya mulai | berhenti) bahwa lebih mudah untuk memahami apa sebenarnya interaksi itu.

"di tempat lain" berarti bahwa objek lain mungkin memiliki hubungan yang sama terbatasnya, dan objek anggota penuh tidak diperlukan - subset dari fungsionalitas yang dibungkus melalui Antarmuka harus memadai.

Klaim mulai lebih masuk akal ketika Anda meledakkan semua fungsionalitas potensial:

  • Mulailah
  • berhenti
  • jeda
  • merekam
  • urutan bermain acak
  • trek sampel, awal lagu
  • sampel lagu, sampel lagu acak
  • memberikan informasi media
  • ...

Sekarang klaimnya tentang "Aku hanya perlu mulai & berhenti" lebih masuk akal. Menggunakan objek anggota konkret alih-alih Antarmuka membuatnya kurang jelas bagi pengembang masa depan tentang apa yang benar - benar diperlukan. Menjalankan tes unit dari MediaCentre pada semua fungsi lain di dalam CdPlayer adalah usaha pengujian yang sia-sia karena mereka termasuk dalam status "tidak peduli". Jika Recordfungsi ini tidak berfungsi dalam kasus ini, kami benar-benar tidak peduli karena tidak diperlukan. Tetapi pengelola masa depan tidak perlu mengetahui hal itu berdasarkan kode, seperti yang tertulis.

Pada akhirnya, premis penulis adalah hanya menggunakan apa yang dibutuhkan dan menjelaskan kepada pengelola masa depan apa yang diperlukan sebelumnya. Tujuannya adalah untuk meminimalkan pengerjaan ulang / analisis ulang modul kode selama pemeliharaan selanjutnya.


sumber
Terima kasih atas jawaban yang bagus ini. Namun Anda berkata: "Menjalankan unit test pada semua fungsi lainnya adalah usaha percobaan yang sia-sia karena mereka termasuk dalam status" tidak peduli "." Bukankah itu lebih tepatnya: "Membuat tiruan untuk masing-masing fungsi lainnya adalah usaha percobaan yang sia-sia karena mereka termasuk dalam kondisi" tidak peduli "."
Mik378
@ Mik378 - ya, itulah yang saya maksudkan, saya hanya mengucapkannya secara berbeda. Dan saya memperbarui jawaban saya untuk membuatnya lebih jelas.
Tetapi saya menemukan istilah "menjalankan unit test" membingungkan. Itu berarti MusicCentre akan menguji unit kolaborasinya ... padahal sebenarnya MOCKS kolaboratornya untuk menguji unit layanan SENDIRI. Ngomong-ngomong, aku sekarang mengerti artinya :)
Mik378
@ Mik378 - kami mengatakan hal yang sama, dan saya mungkin menggunakan terminologi yang kurang tepat untuk melakukannya. Permintaan maaf untuk kebingungan.
4

Ini membuatnya lebih sulit untuk melihat apakah layanan yang mendukung hubungan ini mungkin relevan di tempat lain dan saya harus melakukan analisis lagi lain kali saya bekerja dengan kelas.

Setelah banyak memikirkannya, saya mendapatkan interpretasi yang mungkin dari kutipan ini:

"Layanan" yang dikutip sesuai dengan "fakta penjadwalan". Ini dapat diekspresikan oleh antarmuka yang diberi nama dan "fokus-pada-satu-peran" bernama "ScheduledDevice" atau dinyatakan secara implisit oleh implementasi metode konkret yang tidak bergantung pada antarmuka apa pun.

Dalam sampel di atas, penjadwalan diekspresikan oleh seluruh objek berfitur lengkap bernama CDPlayer. Dengan demikian, itu masih mengarah pada hubungan implisit antara MusicCentredan "fakta penjadwalan".

Jadi jika kita mulai menyuntikkan kelas beton dan mengejeknya ke objek tingkat tinggi; ketika kita ingin menguji yang ini, kita harus menganalisis setiap objek "konkrit" yang disuntikkan untuk melihat apakah itu menyajikan hubungan spesifik yang HARUS DILAKUKAN karena mereka HIDDEN (implisit). Sebaliknya, pengkodean SELALU melalui antarmuka memungkinkan pengembang untuk mengetahui secara langsung hubungan seperti apa yang akan dilayani oleh objek tingkat tinggi dan karenanya mendeteksi fitur yang harus diejek untuk mengisolasi unit-test.

Mik378
sumber
Saya pikir Anda sudah mendapatkannya sekarang. Sayangnya, saya tidak mendapat pemberitahuan atas komentar Anda.
Steve Freeman
3

Layanan yang saya maksud di sini adalah CDPlayer.scheduleToStartAt (). Itulah yang disebut MediaCentre - kolaborator yang perlu berfungsi. MediaCentre adalah objek yang diuji.

Idenya adalah bahwa jika saya membuat eksplisit apa yang bergantung pada MediaCentre, bukan kelas implementasi, saya dapat memberikan peran dependensi itu nama dan membicarakannya. Yang perlu diketahui MediaCentre adalah bahwa ia berbicara dengan ScheduledDevices. Seiring perubahan sistem lainnya, saya tidak perlu mengubah MediaCentre kecuali fitur-fiturnya berubah.

Apakah itu membantu?

Steve Freeman
sumber
(penulis artikel hebat ini :)) apa yang ingin saya tafsirkan adalah kalimat ini: "Ini membuat lebih sulit untuk melihat apakah layanan yang mendukung hubungan ini mungkin relevan di tempat lain dan saya harus melakukan analisis lagi lain kali saya bekerja dengan kelas ". Analisis macam apa? Fakta mendeteksi metode objek mana yang seharusnya menerapkan hubungan karena yang satu ini jelas disembunyikan?
Mik378