3. Abstract Factory Pattern ( 추상 팩토리 패턴 C# )

|

[읽기전에]

  UML 다이어그램 : UML클래스 다이어그램 기본상식 http://hongjinhyeon.tistory.com/25 

  포스팅되는 디자인 패턴의 예는 스타크래프트를 기본으로 하였습니다 : 디자인 패턴을 시작하며 http://hongjinhyeon.tistory.com/24


<기본 정의 >

1. Abstract Factory Pattern 정의

 - 구체적인 클래스를 명시하지 않고도 연관되어 있거나 의존적인 객체 패밀리 생성을 위한 인터페이스 제공.

   (Provide an interface for creating families of related or dependent objects without specifying their concrete classes.)

 

 - 예를 들어서,  곰 세마리 가족(아빠,엄마,아기) 에 대한 대화형 어플리케이션을 개발한다고 했을때에 그 어플리케이션에서

   두가지 종류의 곰(골든베어가족과 브라운베어가족)을 사용자가 선택해서 이야기를 진행하는 기능이 있다면 사용자의 선택

   에 따라서 자동으로 하나의 종류의 아빠 엄마 아기가 생성되어야 한다. 그때 골든베어를 선택하면 자동으로  GoldenDadBear,

   GoldenMomBear, GoldenBabyBear 를 생성해주고 반대로 브라운을 선택하면 BrownDadBear, BrownMomBear, BrownBabyBear 

   을 생성해주어야 한다. 


- 즉, 비슷한 기능을 하지만 종류가 다른 클래스들을 생성하는 인터페이스 제공한다.


2.UML Diagram



3. 사용 용도 및 장점

 -Factory Method Pattern의 장점을 거의 동일하게 가진다.

   ①객체의 생성을 한군데에서 관리를 할수가 있다. (ConcreteCreator 부분에서만 생성코드를 넣는다. )

   ②동일한 인터페이스 구현으로 새로운 객체가 추가되더라도 소스의 수정의 거의 없다. ( 생성 부분의 수정과 신규 클래스의 추가 정도 )

   ③객체를 여러군데에서 생성을 각자하면 동일한 생성을 보장 못하지만, 한군데에서 관리하게 되면 동일한 생성을 보장한다.

 -연관된 객체들을 생성을 할때 실수로 다른종류의 클래스가 생성이 되는것을 방지한다.

 -어플리케이션에서 GUI타입이 여러종류일때, 선택에 따라서 동일한 모양의 GUI타입으로 설정이 된다.


4. 소스

using System;
 
namespace DoFactory.GangOfFour.Abstract.Structural
{
  /// <summary>
  /// MainApp startup class for Structural
  /// Abstract Factory Design Pattern.
  /// </summary>
  class MainApp
  {
    /// <summary>
    /// Entry point into console application.
    /// </summary>
    public static void Main()
    {
      // Abstract factory #1
      AbstractFactory factory1 = new ConcreteFactory1();
      Client client1 = new Client(factory1);
      client1.Run();
 
      // Abstract factory #2
      AbstractFactory factory2 = new ConcreteFactory2();
      Client client2 = new Client(factory2);
      client2.Run();
 
      // Wait for user input
      Console.ReadKey();
    }
  }
 
  /// <summary>
  /// The 'AbstractFactory' abstract class
  /// </summary>
  abstract class AbstractFactory
  {
    public abstract AbstractProductA CreateProductA();
    public abstract AbstractProductB CreateProductB();
  }
 
 
  /// <summary>
  /// The 'ConcreteFactory1' class
  /// </summary>
  class ConcreteFactory1 : AbstractFactory
  {
    public override AbstractProductA CreateProductA()
    {
      return new ProductA1();
    }
    public override AbstractProductB CreateProductB()
    {
      return new ProductB1();
    }
  }
 
  /// <summary>
  /// The 'ConcreteFactory2' class
  /// </summary>
  class ConcreteFactory2 : AbstractFactory
  {
    public override AbstractProductA CreateProductA()
    {
      return new ProductA2();
    }
    public override AbstractProductB CreateProductB()
    {
      return new ProductB2();
    }
  }
 
  /// <summary>
  /// The 'AbstractProductA' abstract class
  /// </summary>
  abstract class AbstractProductA
  {
  }
 
  /// <summary>
  /// The 'AbstractProductB' abstract class
  /// </summary>
  abstract class AbstractProductB
  {
    public abstract void Interact(AbstractProductA a);
  }
 
 
  /// <summary>
  /// The 'ProductA1' class
  /// </summary>
  class ProductA1 : AbstractProductA
  {
  }
 
  /// <summary>
  /// The 'ProductB1' class
  /// </summary>
  class ProductB1 : AbstractProductB
  {
    public override void Interact(AbstractProductA a)
    {
      Console.WriteLine(this.GetType().Name +
        " interacts with " + a.GetType().Name);
    }
  }
 
  /// <summary>
  /// The 'ProductA2' class
  /// </summary>
  class ProductA2 : AbstractProductA
  {
  }
 
  /// <summary>
  /// The 'ProductB2' class
  /// </summary>
  class ProductB2 : AbstractProductB
  {
    public override void Interact(AbstractProductA a)
    {
      Console.WriteLine(this.GetType().Name +
        " interacts with " + a.GetType().Name);
    }
  }
 
  /// <summary>
  /// The 'Client' class. Interaction environment for the products.
  /// </summary>
  class Client
  {
    private AbstractProductA _abstractProductA;
    private AbstractProductB _abstractProductB;
 
    // Constructor
    public Client(AbstractFactory factory)
    {
      _abstractProductB = factory.CreateProductB();
      _abstractProductA = factory.CreateProductA();
    }
 
    public void Run()
    {
      _abstractProductB.Interact(_abstractProductA);
    }
  }
}

-Client 클래스의 생성자에서 AbstractFactory 객체를 받아서 실제  AbstractProductA, AbstractProductB 를 생성한다.


5.실행결과

 ProductB1 interacts with ProductA1

 ProductB2 interacts with ProductA2


<실제 적용>

1. UML Diagram



2. 사용 용도 및 장점

 -사용자가 선택한 종족(테란, 프로토스, 저그 )에 따라서 생성할 수 있는 빌딩들을 설정할 수 있다.

 -보통 종족마다 비슷한 종류의 기능을 하는 건물들이 70~80프로는 비슷하다. 이때 추상팩토리 패턴을 이용해서 구현이 가능하다.

 -GUI 설정(지도위에 테두리, 마우스 포인터 등등)도 AbstractProduct로 하나더 생성해서 추가하면 된다.

 -추상팩토리를 이용하면 테란이 인구확장으로 파일런을 생성하는 경우는 없앨수 있다.


3. 소스

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AbstractFactory
{
    class Program
    {
          static void Main(string[] args)
          {
              Race _race1 = new Terran();
              Game _game1 = new Game(_race1);
              _game1.Show();

              Race _race2 = new Protoss();
              Game _game2 = new Game(_race2);
              _game2.Show();

            Console.ReadKey();
         }

          #region 추상 팩토리 설정

          abstract class Race
          {
              public abstract MaincenterBuilding CreateMaincenterBuilding();
              public abstract PopulationBuilding CreatePopulationBuilding();
          }
 
         
          class Terran : Race
          {
            public override MaincenterBuilding CreateMaincenterBuilding()
            {
                return new CommandCenter();
            }
            public override PopulationBuilding CreatePopulationBuilding()
            {
                return new SupplyDepot();
            }

          }
         
          class Protoss : Race
          {
            public override MaincenterBuilding CreateMaincenterBuilding()
            {
                return new Nexus();
            }
            public override PopulationBuilding CreatePopulationBuilding()
            {
                return new Pylon();
            }
          }

          #endregion

          #region 추상 Product 클래스 생성

          abstract class MaincenterBuilding
          {
          }
 
          abstract class PopulationBuilding
          {
              public abstract void Interact(MaincenterBuilding a);
          }

          #endregion

          #region Product 클래스 생성

          class CommandCenter : MaincenterBuilding
          {
          }

          class SupplyDepot : PopulationBuilding
          {
            public override void Interact(MaincenterBuilding a)
            {
              Console.WriteLine(this.GetType().Name +
                " interacts with " + a.GetType().Name);
            }
          }

          class Nexus : MaincenterBuilding
          {
          }
 
          class Pylon : PopulationBuilding
          {
             public override void Interact(MaincenterBuilding a)
            {
              Console.WriteLine(this.GetType().Name +
                " interacts with " + a.GetType().Name);
            }
          }

          #endregion


          class Game
          {
              private MaincenterBuilding _maincenterBuilding;
              private PopulationBuilding _populationBuilding;
 
            
             public Game(Race _race)
             {
                _maincenterBuilding = _race.CreateMaincenterBuilding();
                _populationBuilding = _race.CreatePopulationBuilding();
             }
 
             public void Show()
             {
                _populationBuilding.Interact(_maincenterBuilding);
             }
          }

    }
}

4. 결과


5. 피드백

 -Factory Method 패턴과 비슷하지만 그것과는 다르게 연관된 클래스들을(family) 생성한다.

 -위의 다이어그램에서 저그가 추가될 시에는 클래스 3개가 추가되고 Game에서 해당 저그를 생성해서 사용해주면 된다.


[참조]

   -Professional VB.NET 디자인 패턴

   -http://www.dofactory.com



And