Fabrika olarak temel sınıflar?


14

Hafta sonu bazı kodlar yazıyordum ve kendimi bir fabrikaya temel sınıfta statik yöntem olarak yazmak istediğimi fark ettim.

Sorum basitçe bunun ac # idomatik bir yaklaşım olup olmadığını bilmek?

Olamayabileceğimi düşünüyorum, temel sınıfın türetilmiş sınıf hakkında bilgi sahibi olması gerçeğinden geliyor.

Bununla birlikte, aynı sonucu elde etmenin daha basit bir yolundan emin değilim. Başka bir fabrika sınıfı (en azından bana göre) gereksiz karmaşıklık (?) Gibi görünüyor

Gibi bir şey:

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
{
}

Fabrikalarınızı nasıl test edeceksiniz?

bu soruda kodu ile, ben olmaz. ama cevap, kodlama
stilime

Hayvan sınıfınızda hem Seaworld hem de Landworld'ü çektiğinizde, tümüyle yalıtılmış bir ortamda işlemeyi zorlaştırmayı düşünün.

Yanıtlar:


13

Ayrı bir fabrika sınıfının avantajı, birim testlerde taklit edilebilmesidir.

Ancak bunu yapmayacaksanız veya başka bir şekilde polimorfik yapmayacaksanız, sınıfın kendisinde statik bir Fabrika yöntemi uygundur.


teşekkürler, başka bir şekilde polimorfik derken neye atıfta bulunduğunuzu gösteren bir örnek verebilir misiniz?
Aaron Anodide

Demek istediğim, eğer bir fabrika yöntemini başka bir yöntemle değiştirmek isterseniz. Test amacıyla alay etmek bunun en yaygın örneklerinden sadece bir tanesidir.
pdr

18

Switch deyimini önlemek ve alt sınıf uygulamalarını temel sınıftan ayırmak için Generics'i kullanabilirsiniz.

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

Kullanımı:

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

veya

 var rabbit = Animal.CreateAnimal<LandAnimal>(); 


2
@PeterK. Fowler, Kod Kokularındaki anahtar ifadelere atıfta bulunur, ancak çözümü, değiştirilmek yerine Fabrika sınıflarına çıkarılması gerektiğidir. Bununla birlikte, geliştirme zamanında türü her zaman bilecekseniz, jenerikler daha da iyi bir çözümdür. Ancak (orijinal örnekte ima edildiği gibi) yapmazsanız, o zaman fabrika yönteminde bir geçişten kaçınmak için yansıma kullanmanın yanlış bir ekonomi olabileceğini iddia ederim.
pdr

switch deyimleri ve if-else-if zincirleri aynıdır. Ben eski dava ele alınmasını zorlar derleme zamanı kontrol nedeniyle eski kullanın
Aaron Anodide

4
@PeterK, bir switch deyiminde kod tek bir yerde bulunur. Tam gelişmiş OO'da aynı kod, birden fazla ayrı sınıfa yayılabilir. Çok sayıda parçacığa sahip olmanın ilave karmaşıklığının bir anahtar ifadesinden daha az arzu edildiği gri bir alan vardır .

@Thorbjorn, tam olarak bu düşünceyi yaşadım, ama hiç bu kadar özlü bir şekilde, kelimelere
döktüğünüz

5

Aşırıya kaçıldığında, fabrika da genel olabilir.

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

Daha sonra herhangi bir nesne fabrikası yaratılabilir ve bu da herhangi bir nesne türü yaratabilir. Bunun daha basit olup olmadığından emin değilim, kesinlikle daha genel.

Küçük bir fabrika uygulaması için bir anahtar deyiminde yanlış bir şey görmüyorum. Çok sayıda nesneye veya olası farklı sınıf hiyerarşilerine girdikten sonra, daha genel bir yaklaşımın daha uygun olduğunu düşünüyorum.


1

Kötü bir fikir. Birincisi, açık-kapalı prensibini ihlal ediyor. Herhangi bir yeni hayvan için tekrar temel sınıfınızla uğraşmak zorunda kalacaksınız ve potansiyel olarak onu kıracaksınız. Bağımlılıklar yanlış gidiyordu.

Bir yapılandırmadan hayvanlar oluşturmanız gerekiyorsa, bunun gibi bir yapı bir tür Tamam olur, ancak yansıma adıyla eşleşen türü almak ve elde edilen bu tür bilgileri kullanarak örneklemek daha iyi bir seçenek olacaktır.

Ancak yine de, hayvan sınıfı hiyerarşisinden bağımsız bir fabrika sınıfı oluşturmalı ve bir taban türü yerine bir IAnimal arabirimi döndürmelisiniz. O zaman faydalı olur, biraz ayrışmayı başarırdınız.


0

Bu soru benim için zamanında - dün neredeyse tam olarak böyle bir kod yazıyordum. Burada tartışmak için “Hayvan” ı projemle alakalı olanlarla değiştirin. Bir 'switch' ifadesi yerine, sadece bir değişkeni belirli sabit değerlerle karşılaştırmaktan daha fazlasını içeren, biraz daha karmaşık 'if' ifadeleri dizisine sahiptim. Ama bu bir detay. Statik bir fabrika yöntemi, tasarımın hızlı ve kirli kod karmaşasını yeniden düzenlemekten ortaya çıktığı için, bir şeyler tasarlamak için düzgün bir yol gibi görünüyordu.

Bu tasarımı türetilmiş sınıf hakkında bilgi sahibi olan temel sınıfın gerekçesiyle reddettim. LandAnimal ve SeaAnimal sınıfları küçük, temiz ve kolaysa, aynı kaynak dosyada olabilirler. Ama resmi olarak tanımlanmış standartlara uymayan metin dosyalarını okumak için büyük dağınık yöntemlerim var - LandAnimal sınıfımı kendi kaynak dosyasında istiyorum.

Bu, dairesel dosya bağımlılığına yol açar - LandAnimal, Animal'den türetilir, ancak Animal'in LandAnimal, SeaAnimal ve on beş diğer sınıfın var olduğunu zaten bilmesi gerekir. Fabrika yöntemini çıkardım, kendi dosyasına koydum (ana uygulamamda, Hayvanlar kütüphanemde değil) Statik bir fabrika yöntemine sahip olmak sevimli ve zeki görünüyordu, ancak aslında herhangi bir tasarım problemini çözmediğini fark ettim.

Bunun deyimsel C # ile nasıl bir ilgisi olduğuna dair hiçbir fikrim yok, dilleri çok değiştirdiğimden, genellikle her zamanki işimin dışındaki dillere ve geliştirici yığınlara özgü deyimleri ve kuralları görmezden gelirim. Eğer bir şey varsa benim C # anlamlı ise "pitonik" görünebilir. Genel netliği hedefliyorum.

Ayrıca, gelecekteki çalışmalarda genişletilmesi muhtemel olmayan küçük, basit sınıflar için statik fabrika yöntemini kullanmanın bir avantajı olup olmadığını bilmiyorum - hepsinin bir kaynak dosyada olması bazı durumlarda güzel olabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.