2010-11-04 13 views
6

Łączę wyjaśnienie i przykład kodu tego wzoru projektu, próbując pomóc innym wokół mnie uchwycić (wraz z pomaganiem sobie w opanowaniu wzoru).Fabryczny wzór projektu (wymagający krytyki)

Czego szukam to opinie & lub krytyka mojego wyjaśnienia i próbki kodu ... dzięki!

Czym jest wzór fabryczny? Fabryczny wzór wykorzystuje specjalny dedykowany "obiekt twórcy obiektu" do obsługi tworzenia - i najczęściej tworzenia instancji - obiektów, podobnych do fabryki świata rzeczywistego.

rzeczywistym świecie przykładem
myśleć o fabryce samochodów będącej twórcą różnych rodzajów samochodów. Jedna z linii montażowych w tej fabryce samochodów może wyprodukować jeden samochód ciężarowy, ale w innym dniu może zostać przeprojektowana do produkcji samochodów. Załóżmy, że przedstawiciel handlowy składa zamówienie na 10 samochodów do przypisanego działu obsługi klienta. Oddział ten następnie wykorzystuje pewną fabrykę i zamawia 10 samochodów. Osoby zajmujące się obsługą rachunków nie są zainteresowane samodzielnym produkowaniem samochodów (wyobraźcie sobie kiepskie wyniki), że pracują tylko z produktem końcowym, zapewniając, że dealerskie pojazdy zostaną zakupione.

Nowy model tego samego samochodu pojawi się w przyszłym roku, a zamówienia zaczną napływać. Kontrahenci (wciąż niezwiązani z produkcją samochodu) składają zamówienia, ale teraz samochód, który otrzymują, jest inny, Metoda składania, a nawet może fabryka w ogóle może być inna, ale osoby zajmujące się obsługą kont nie muszą się tym martwić. Dodatkowa myśl: fabryczne asemblery pojazdów mogą dokładnie wiedzieć, jakie działania podjąć, jeśli jakiś handlarz konta złoży zamówienie (tj. Handler X załączy zamówienie, asembler fabryczny wie, że dla handlarza kont X, produkuje 10 pojazdów typu Y). Inną opcją może być to, że program obsługi klienta informuje asemblera, jaki typ pojazdu ma wyprodukować.

Jeśli osoby zajmujące się obsługą obsługi zajmowały się również tworzeniem pojazdów (tj. Były powiązane), za każdym razem, gdy pojazd zmieniał się w jakikolwiek sposób, każdy z osób zajmujących się obsługą konta musiałby zostać przeszkolony w produkcji tego pojazdu. Stworzyłoby to problemy z jakością, ponieważ jest o wiele więcej osób zajmujących się obsługą rachunków, niż byłoby fabryk ... błędy by się wydarzyły, koszt byłby znacznie większy.

Krążąc z powrotem do OOP
fabryce obiekt jako wzorzec wzór stosowany do inżynierii oprogramowania jest podobny do powyższego przykładu w koncepcji ... Fabryka produkuje masę różnego rodzaju innych obiektów, można korzystać z linii montażowej (object asembler), który wytwarza określony typ obiektu, zwrócony w określony sposób. Asembler może albo kontrolować klienta żądającego i obsługiwać, albo klient może powiedzieć asemblerowi, jakiego obiektu potrzebuje. Teraz ... jesteś na projekcie i tworzysz fabrykę obiektów i różne asemblery, a później na trasie projektu, wymagania zmieniają się nieznacznie, teraz jesteś proszony o zmianę zawartości obiektów i sposobu, w jaki ich klienci obsługują ten obiekt. Ponieważ używałeś wzoru fabrycznego, jest to prosta zmiana iw jednym miejscu możesz zmienić lub dodać obiekty, które fabryka produkuje, i zmienić format, w którym asemblery wyrzucają zawartość obiektu.

Niefortunnym sposobem na wykonanie tej czynności byłby brak metody fabryki, instancji każdej instancji obiektu i formatowania zawartości obiektów w samych klientach ... powiedzmy, że użyłeś tego konkretnego obiektu w 20 klientach. Teraz musisz udać się do każdego z klientów, zmienić każdą instancję i format obiektu ... co za strata czasu ... Bądź leniwy ... zrób to właściwie za pierwszym razem, oszczędzając siebie (i innych) czas i wysiłek później.

przykład kodu (C#)
Poniżej jest przykład wykorzystując fabrykę żywności i różnych potraw Przedmioty

Factory module 
    public enum FoodType 
    { 
    //enumerated foodtype value, if client wants to specify type of object, coupling still occurs 
     Hamburger, Pizza, HotDog 
    } 
  
    /// <summary> 
    /// Object to be overridden (logical) 
    /// </summary> 
    public abstract class Food 
    { 
     public abstract double FoodPrice { get; } 
    } 
  
    /// <summary> 
    /// Factory object to be overridden (logical) 
    /// </summary> 
    public abstract class FoodFactory 
    { 
     public abstract Food CreateFood(FoodType type); 
    } 
  
    //------------------------------------------------------------------------- 
    #region various food objects 
    class Hamburger : Food 
    { 
     double _foodPrice = 3.59; 
     public override double FoodPrice 
     { 
      get { return _foodPrice; } 
     } 
    } 
  
    class Pizza : Food 
    { 
     double _foodPrice = 2.49; 
     public override double FoodPrice 
     { 
      get { return _foodPrice; } 
     } 
    } 
  
    class HotDog : Food 
    { 
     double _foodPrice = 1.49; 
     public override double FoodPrice 
     { 
      get { return _foodPrice; } 
     } 
    } 
    #endregion 
    //-------------------------------------------------------------------------- 
  
  
    /// <summary> 
    /// Physical factory 
    /// </summary> 
    public class ConcreteFoodFactory : FoodFactory 
    { 
     public override Food CreateFood(FoodType foodType) 
     { 
      switch (foodType) 
      { 
       case FoodType.Hamburger: 
        return new Hamburger(); 
        break; 
       case FoodType.HotDog: 
        return new HotDog(); 
        break; 
       case FoodType.Pizza: 
        return new Pizza(); 
        break; 
       default: 
        return null; 
        break; 
      } 
     } 
    } 
  
    /// <summary> 
    /// Assemblers 
    /// </summary> 
    public class FoodAssembler 
    { 
     public string AssembleFoodAsString(object sender, FoodFactory factory) 
     { 
      Food food = factory.CreateFood(FoodType.Hamburger); 
      if (sender.GetType().Name == "default_aspx") 
      { 
       return string.Format("The price for the hamburger is: ${0}", food.FoodPrice.ToString()); 
      } 
      else 
      { 
       return food.FoodPrice.ToString(); 
      } 
     } 
  
     public Food AssembleFoodObject(FoodFactory factory) 
     { 
      Food food = factory.CreateFood(FoodType.Hamburger); 
      return food; 
     } 
    } 

Calling factory 
FoodFactory factory = new ConcreteFoodFactory(); //create an instance of the factoryenter code here 
lblUser.Text = new FoodAssembler().AssembleFoodAsString(this, factory); //call the assembler which formats for string output 

Object o = new FoodAssembler().AssembleFoodObject(factory); //example: instantiating anon object, initialized with created food object 
+0

Może to wiki społecznościowe? –

+0

Dlaczego głosy bliskie? Pytanie jest ważne, chociaż jest trochę długie :) – jgauffin

Odpowiedz

14

Przepraszamy. To dość nieelastyczna fabryka. Odbicie może giva niektóre POWWAH !!

public interface IFood 
{ 
    bool IsTasty { get; } 
} 
public class Hamburger : IFood 
{ 
    public bool IsTasty {get{ return true;}} 
} 
public class PeaSoup : IFood 
{ 
    public bool IsTasty { get { return false; } } 
} 

public class FoodFactory 
{ 
    private Dictionary<string, Type> _foundFoodTypes = 
     new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase); 

    /// <summary> 
    /// Scan all specified assemblies after food. 
    /// </summary> 
    public void ScanForFood(params Assembly[] assemblies) 
    { 
     var foodType = typeof (IFood); 
     foreach (var assembly in assemblies) 
     { 
      foreach (var type in assembly.GetTypes()) 
      { 
       if (!foodType.IsAssignableFrom(type) || type.IsAbstract || type.IsInterface) 
        continue; 
       _foundFoodTypes.Add(type.Name, type); 
      } 
     } 

    } 

    /// <summary> 
    /// Create some food! 
    /// </summary> 
    /// <param name="name"></param> 
    /// <returns></returns> 
    public IFood Create(string name) 
    { 
     Type type; 
     if (!_foundFoodTypes.TryGetValue(name, out type)) 
      throw new ArgumentException("Failed to find food named '" + name + "'."); 

     return (IFood)Activator.CreateInstance(type); 
    } 

} 

Zastosowanie:

var factory = new FoodFactory(); 
factory.ScanForFood(Assembly.GetExecutingAssembly()); 

Console.WriteLine("Is a hamburger tasty? " + factory.Create("Hamburger").IsTasty); 

Edit Odpowiedź na kodzie:

Przede wszystkim, fabryki są wykorzystywane, aby móc tworzyć obiekty z nieco zmian w kodzie, jak to możliwe, gdy dodawanie nowych typów implementacji. Korzystanie z wyliczenia oznacza, że ​​wszystkie miejsca, które wywołują fabrykę, muszą używać wyliczenia i być aktualizowane po zmianie wyliczenia.

Oczywiście, nadal jest trochę lepiej niż tworzenie typów bezpośrednio.

Drugi problem z kodem polega na tym, że używasz instrukcji switch (ale jest to najlepszy sposób, aby to zrobić, jeśli wymóg jest wymagany). Lepiej być w stanie zarejestrować wszystkie różne klasy w jakiś sposób. Z pliku konfiguracyjnego lub zezwalając na rejestrację samych rzeczywistych implementacji (na przykład klasy Hamburger). Wymaga to, aby fabryka podążała za wzorcem singleton.

Nadchodzi Reflection na ratunek. Odbicie umożliwia przeglądanie wszystkich typów plików DLL i EXE. Możemy więc wyszukiwać wszystkie klasy, które implementują nasz interfejs i dlatego będą w stanie zbudować słownik dla wszystkich klas.

+0

To jest rodzaj opinii, na które liczyłem, czy możesz wypowiedzieć się na mój przykład na wyjaśnienie tego komuś, kto ma nigdy wcześniej nie słyszałem o tym wzorze? Nie mam pewności, czy mój przykład jest dokładny i ma sens. Dzięki za wejście, oznaczenie jako odpowiedź. – dbobrowski

+0

Dlaczego warto korzystać z odbicia, gdy można używać generycznych zamiast tego, chyba że chcesz odczytać rodzaje żywności z pliku lub coś podobnego, użycie łańcuchów znaków dla nazw klas sprawi, że będzie on bardziej podatny na błędy. – Doggett

+0

Mówienie "hamburger" nie narzuca w żaden sposób, jak wygląda implementacja. Mówiąc fabryka. Uzyskaj () wymusza wszystkie swoje wdrożenia, aby uzyskać z Hamburger. Inną dobrą rzeczą z alternatywnym ciągiem jest to, że kod może wykorzystywać implementacje, których nie ma żadnej wiedzy (na przykład z dynamicznie ładowanego zespołu). – jgauffin

0

Proponuję użyć interfejsów zamiast klas abstrakcyjnych/dziedziczenia. Poza tym wygląda na to, że jest OK.

+2

Nieeee. Używanie instrukcji switch i enums – jgauffin

3

Myślę, że twoje wyjaśnienie, w tym przykład z prawdziwego świata, jest dobre. Jednak nie sądzę, że twój przykładowy kod pokazuje rzeczywiste zalety wzoru.

Możliwe zmiany:

  • bym nie enum równolegle do rodzajów. Wygląda na to, że musisz zaktualizować wyliczenie za każdym razem, gdy zostanie dodany typ. Właściwsze może być przekazanie typu System.Type. Wtedy możesz nawet uczynić fabrykę generyczną z argumentem szablonu.
  • Myślę, że wzór jest bardziej "imponujący", jeśli użyje się go do stworzenia czegoś podobnego do interfejsu sprzętowego. Będziesz wtedy miał "AbstractNetworkDevice", a wszyscy twoi rozmówcy nie będą wiedzieli, jaką masz konfigurację sprzętu. Ale fabryka może utworzyć "TcpNetworkDevice" lub "SerialNetworkDevice" lub cokolwiek na podstawie konfiguracji dokonanej przy starcie.
+0

nie jest w porządku, dziękuję za porady, phillipp, chciałbym upvote ten, ale wciąż zbyt nowy – dbobrowski