Wzorce projektowe – Builder

W tej serii będę opisywał najpopularniejsze wzorce projektowe. Nie odkryję tu Ameryki, jednak posty te będą powstawać głównie dla utrwalenia wiedzy i możliwości szybkiego jej przejrzenia, a jeżeli komuś się to przyda to tym lepiej.

Na pierwszy ogień idzie jeden z podstawowych wzorców konstrukcyjnych a więc tytułowy „Builder”

Zalety: Za pomocą tego wzorca oddzielamy proces tworzenia obiektu od jego implementacji (za przykład mogą tu posłużyć wszelkiej maści konwertery potrafiące dać wiele formatów wynikowych).

Składowe:

  1. Builder – jest interfejsem abstrakcyjnym służącym do tworzenia obiektów.
  2. ConcreteBuilder 
    • Tworzy i łączy poszczególne składniki ze sobą.
    • Generuje i śledzi poszczególne wygenerowane składniki.
    • Udostępnia interfejs do pobrania gotowego obiektu.
  3. Director – tworzy obiekt za pomocą interfejsu klasy „Builder”.
  4. Product
    •  To on reprezentuje nasz wygenerowany złożony obiekt
    • Zawiera klasy definiujące składowe obiektu, oraz interfejsy do ich łączenia

 

Na diagramie przedstawia się to następująco:

 

Zastosowanie:

  • Wzorzec ten powinien być zastosowany wówczas, gdy algorytm tworzący obiekt powinien być nie zależny od samego procesu tworzenia finalnego obiektu.
  • Jeżeli za pomocą jednego procesu konstrukcji obiektu chcemy wygenerować różne reprezentacje danego obiektu.

 

Czas na odrobinę kodu

  1. Tworzymy model naszego pożądanego obiektu (Product), który będzie tworzony przy użyciu Director’a implementującego interfejs Buildera
    namespace Builder.Product
    {
        public class Computer
        {
            private readonly string _computerOwner;
            public Cpu cpu { get; set; }
            public RamMemory ramMemory { get; set; }
            public HardDriveType hardDriveType { get; set; }
    
            public Computer(string computerOwner)
            {
                _computerOwner = computerOwner;
            }
    
            public override string ToString()
            {
                return string.Format("Owner: {0}\nCPU: {1}\nMemory: {2} GB\nHard Drive: {3}\n",
                    _computerOwner, cpu, (int)ramMemory, hardDriveType);
            }
        }
    }
  2. Kolej na nasz abstrakcyjny interfejs (Builder), który pomoże stworzyć nasz pożądany obiekt (Product).
    namespace Builder
    {
        public interface IComputerBuilder
        {
             void BuildCpu();
             void BuildRamMemory();
             void BuildHardDriveType();
             Computer GetComputer();
        }
    }
    

    Dla ułatwienia tego przykładu utworzyłem klasę Assembly

    namespace Builder.Model
    {
        public class Assembly
        {
            public enum Cpu
            {
                i3,
                i5,
                i7
            };
            
            public enum RamMemory
            {
                One = 1,
                Two = 2,
                Four = 4,
                Eight = 8
            }
    
            public enum HardDriveType
            {
                HDD,
                SSD,
                HYBRID
            }
        }
    
  3. Czas na Concrete Builder, który implementuje nasz interfejs (Builder). W moim wypadku są tą trzy klasy.
    namespace ConcreteBuilder
    {
        public class CheapComputerBuilder : IComputerBuilder
        {
            Computer _computer;
            public CheapComputerBuilder(string ownerName)
            {
                _computer = new Computer(ownerName);
            }
            public void BuildCpu()
            {
                _computer.cpu = Cpu.i3;
            }
    
            public void BuildHardDriveType()
            {
                _computer.hardDriveType = HardDriveType.HDD;
            }
    
            public void BuildRamMemory()
            {
                _computer.ramMemory = RamMemory.Two;
            }
    
            public Computer GetComputer()
            {
                return _computer;
            }
        }
    }
    
     public class ExpensiveComputerBuilder : IComputerBuilder
        {
            Computer _computer;
            public ExpensiveComputerBuilder(string computerOwner)
            {
                _computer = new Computer(computerOwner);
            }
            public void BuildCpu()
            {
                _computer.cpu = Cpu.i7;
            }
    
            public void BuildHardDriveType()
            {
                _computer.hardDriveType = HardDriveType.SSD;
            }
    
            public void BuildRamMemory()
            {
                _computer.ramMemory = RamMemory.Eight;
            }
    
            public Computer GetComputer()
            {
                return _computer;
            }
        }
    
      public class NormalPriceComputerBuilder : IComputerBuilder
        {
            Computer _computer;
            public NormalPriceComputerBuilder(string computerOwner)
            {
                _computer = new Computer(computerOwner);
            }
            public void BuildCpu()
            {
                _computer.cpu = Cpu.i5;
            }
    
            public void BuildHardDriveType()
            {
                _computer.hardDriveType = HardDriveType.HYBRID;
            }
    
            public void BuildRamMemory()
            {
                _computer.ramMemory = RamMemory.Four;
            }
    
            public Computer GetComputer()
            {
                return _computer;
            }
        }
    
  4. Director, odpowiada za prawidłową sekwencję budowania naszego obiektu.
    namespace Builder.Director
    {
        public class Manufacturer
        {
            public void BuildComputer(IComputerBuilder computerBuilder)
            {
                computerBuilder.BuildCpu();
                computerBuilder.BuildHardDriveType();
                computerBuilder.BuildRamMemory();
                computerBuilder.GetComputer();
            }
        }
    }
    
  5. Gotowy działający program
        class Program
        {
            static void Main(string[] args)
            {
                Manufacturer newManufacturer = new Manufacturer();
    
                // Lets have the Builder class ready
                IComputerBuilder computerBuilder = null;
    
                // Create a new cheap computer
                computerBuilder = new CheapComputerBuilder("Dave");
                newManufacturer.BuildComputer(computerBuilder);
                Console.WriteLine("A new Computer built:\n\n{0}", computerBuilder.GetComputer().ToString());
    
                // Create a new normal price computer
                computerBuilder = new NormalPriceComputerBuilder("Tom");
                newManufacturer.BuildComputer(computerBuilder);
                Console.WriteLine("A new Computer built:\n\n{0}", computerBuilder.GetComputer().ToString());
    
                // Create a new expensive computer
                computerBuilder = new ExpensiveComputerBuilder("Joe");
                newManufacturer.BuildComputer(computerBuilder);
                var computer = computerBuilder.GetComputer();
                Console.WriteLine("A new Computer built:\n\n{0}", computerBuilder.GetComputer().ToString());
            }
        }
    

A poniżej wynik finalny działania naszego programu

Jedna myśl na temat “Wzorce projektowe – Builder

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *