Kelas dasar sebagai pabrik?

14

Saya sedang menulis beberapa kode selama akhir pekan dan saya mendapati diri saya ingin menulis sebuah pabrik sebagai metode statis di kelas dasar.

Pertanyaan saya hanya untuk mengetahui apakah ini merupakan pendekatan otomatis?

Perasaan saya bahwa itu mungkin tidak berasal dari fakta bahwa kelas dasar memiliki pengetahuan tentang kelas turunan.

Yang mengatakan, saya tidak yakin cara yang lebih sederhana untuk mendapatkan hasil yang sama. Seluruh kelas pabrik lain sepertinya (setidaknya bagi saya) seperti kompleksitas yang tidak diperlukan (?)

Sesuatu seperti:

class Animal
{
  public static Animal CreateAnimal(string name)
  {
     switch(name)
     {
        case "Shark":
          return new SeaAnimal();
          break;
        case "Dog":
          return new LandAnimal();
          break;
        default:
          throw new Exception("unknown animal");
     }
  }
}
class LandAnimal : Animal
{
}

class SeaAnimal : Animal
{
}
Aaron Anodide
sumber
Bagaimana Anda akan menguji pabrik Anda?
dengan kode dalam pertanyaan ini, saya tidak mau. tetapi jawabannya telah membantu saya dalam mengintegrasikan pengujian ke gaya pengkodean saya
Aaron Anodide
Pertimbangkan bahwa telah menarik Seaworld dan Landworld di kelas Hewan Anda, semuanya menjadikannya lebih sulit untuk ditangani dalam lingkungan yang terisolasi.

Jawaban:

13

Nah, keuntungan dari kelas pabrik yang terpisah adalah dapat diejek dalam unit test.

Tetapi jika Anda tidak akan melakukan itu, atau membuatnya polimorfik dengan cara lain, maka metode Pabrik statis di kelas itu sendiri tidak masalah.

pdr
sumber
terima kasih, bisakah Anda memberikan contoh dari apa yang Anda maksud ketika Anda mengatakan polimorfik dengan cara lain?
Aaron Anodide
Maksud saya, jika Anda ingin dapat mengganti satu metode pabrik dengan metode lainnya. Mengejeknya untuk tujuan pengujian hanyalah salah satu contoh paling umum dari itu.
pdr
18

Anda bisa menggunakan Generics untuk menghindari pernyataan switch dan memisahkan implementasi downstream dari kelas dasar juga .:

 public static T CreateAnimal<T>() where T: new, Animal
 {
    return new T();
 }

Pemakaian:

LandAnimal rabbit = Animal.CreateAnimal();  //Type inference should should just figure out the T by the return indicated.

atau

 var rabbit = Animal.CreateAnimal<LandAnimal>(); 
Nick Nieslanik
sumber
2
@PeterK. Fowler merujuk pada pergantian pernyataan dalam Code Smells, tetapi solusinya adalah mereka harus diekstraksi ke kelas Factory, daripada digantikan. Yang mengatakan, jika Anda selalu akan tahu jenisnya pada waktu pengembangan, obat generik adalah solusi yang lebih baik. Tetapi jika Anda tidak (seperti contoh asli tersirat) maka saya akan berpendapat bahwa menggunakan refleksi untuk menghindari beralihnya metode pabrik dapat menjadi ekonomi yang salah.
pdr
beralih pernyataan dan rantai if-else-if adalah sama. saya menggunakan yang pertama karena waktu kompilasi memeriksa yang memaksa kasus default untuk ditangani
Aaron Anodide
4
@ PeterK, dalam pernyataan switch kode berada di satu tempat. Dalam full-blown OO kode yang sama dapat tersebar di beberapa kelas yang terpisah. Ada area abu-abu di mana kompleksitas tambahan memiliki banyak snippet kurang diinginkan daripada pernyataan switch.
@ Thorbjorn, saya sudah memikirkan hal itu, tetapi tidak pernah secara ringkas, terima kasih telah mengatakannya
Aaron Anodide
5

Dibawa ke ekstrim, pabrik bisa menjadi generik juga.

interface IFactory<K, T> where K : IComparable
{
    T Create(K key);
}

Kemudian seseorang dapat membuat segala jenis objek pabrik, yang pada gilirannya dapat membuat semua jenis objek. Saya tidak yakin apakah itu lebih sederhana, tentu lebih generik.

Saya tidak melihat ada yang salah dengan pernyataan beralih untuk implementasi pabrik kecil. Setelah Anda ke sejumlah besar objek atau kemungkinan hierarki objek yang berbeda, saya pikir pendekatan yang lebih umum lebih cocok.

Jon Raynor
sumber
1

Ide buruk. Pertama, itu melanggar prinsip buka-tutup. Untuk hewan baru apa pun, Anda harus mengacaukan kelas dasar Anda lagi dan Anda berpotensi melanggarnya. Ketergantungan akan salah jalan.

Jika Anda perlu membuat hewan dari konfigurasi, konstruksi seperti ini akan agak OK, meskipun menggunakan refleksi untuk mendapatkan jenis yang cocok dengan nama dan instantiate menggunakan informasi jenis yang diperoleh akan menjadi pilihan yang lebih baik.

Tetapi Anda harus tetap membuat kelas pabrik khusus, terlepas dari hierarki kelas hewan, dan mengembalikan antarmuka IAnimal daripada tipe dasar. Maka itu akan menjadi berguna, Anda akan mencapai beberapa decoupling.

Martin Maat
sumber
0

Pertanyaan ini tepat waktu untuk saya - saya menulis hampir persis kode kemarin. Ganti saja "Animal" dengan apa pun yang relevan dalam proyek saya, meskipun saya akan tetap dengan "Animal" demi diskusi di sini. Alih-alih pernyataan 'beralih', saya memiliki serangkaian pernyataan 'jika' yang agak lebih rumit, yang melibatkan lebih dari sekadar membandingkan satu variabel dengan nilai-nilai tetap tertentu. Tapi itu detail. Metode pabrik statis tampak seperti cara yang rapi untuk merancang berbagai hal, karena desain muncul dari refactoring yang sebelumnya berantakan cepat dan kotor kode.

Saya menolak desain ini dengan alasan kelas dasar memiliki pengetahuan tentang kelas turunan. Jika kelas LandAnimal dan SeaAnimal kecil, rapi, dan mudah, mereka bisa berada di file sumber yang sama. Tapi saya punya metode berantakan besar untuk membaca file teks yang tidak sesuai dengan standar yang ditetapkan secara resmi - Saya ingin kelas LandAnimal saya di file sumbernya sendiri.

Itu mengarah ke ketergantungan file melingkar - LandAnimal berasal dari Animal, tetapi Animal perlu sudah tahu bahwa LandAnimal, SeaAnimal dan lima belas kelas lain ada. Saya mengeluarkan metode pabrik, memasukkannya ke file sendiri (dalam aplikasi utama saya, bukan perpustakaan Hewan saya). Memiliki metode pabrik statis tampak lucu dan pintar, tetapi saya menyadari bahwa itu tidak benar-benar menyelesaikan masalah desain.

Saya tidak tahu bagaimana ini berhubungan dengan C # idiomatik, karena saya banyak berganti bahasa, saya biasanya mengabaikan idiom dan konvensi yang khas bahasa dan tumpukan dev di luar pekerjaan saya yang biasa. Jika ada C # saya mungkin terlihat "pythonic" jika itu bermakna. Saya bertujuan untuk kejelasan umum.

Juga, saya tidak tahu apakah ada keuntungan menggunakan metode pabrik statis dalam kasus kecil, kelas sederhana yang tidak mungkin diperpanjang dalam pekerjaan di masa depan - memiliki semuanya dalam satu file sumber mungkin bagus dalam beberapa kasus.

DarW
sumber