상세 컨텐츠

본문 제목

[Factory Method Pattern] 팩토리 메서드 패턴 C#

C#

by 메타샤워 2023. 7. 25. 12:45

본문

Design Pattern by Csharp

Factory Method Pattern ( 팩토리 매서드 패턴 )

  

1. Factory Method Pattern 정의

- 객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브 클래스에서 결정 팩토리 메소드 패턴을 이용하면 클래스의 인스턴스를 만드는 일을 서브 클래스에게 맡기게 된다.

( Define an interface for creating an object, but let subClass decide which class to instantiate.

Factory Method lets a class defer instantiation to subclasses.)

- 팩토리 메소드 패턴을 사용하는데에는 몇가지 이유가 있지만, 결론적으로 인터페이스를 가지는 클래스를 생성하는 것이다.

, 동일한 인터페이스를 준수하는 클래스들을 생성을 한다.

  

2. UML Diagram

3. 사용목적 및 용도, 장점

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

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

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

  

4. 소스코드

using System;
 
namespace DesignPattern
{
   
    class MainApp
    {
        static void Main()
        {
            Creator[] creators = new Creator[2];
 
            creators[0] = new ConcreteCreatorA();
            creators[1] = new ConcreteCreatorB();
 
            foreach (Creator creator in creators)
            {
                Product product = creator.FactoryMethod();
                Console.WriteLine("Created {0}", product.GetType().Name);
            }
        }
    }
 
    abstract class Product { }
 
    class ConcreteProductA : Product { }
 
    class ConcreteProductB : Product { }
 
    abstract class Creator
    {
        public abstract Product FactoryMethod();
    }
 
    class ConcreteCreatorA : Creator
    {
        public override Product FactoryMethod()
        {
            return new ConcreteProductA();
        }
    }
    class ConcreteCreatorB : Creator
    {
        public override Product FactoryMethod()
        {
            return new ConcreteProductB();
        }
    }
}

< 실제 적용 >

  

1. UML Diagram

 

2. 사용 목적 및 용도

- 스타크래프트에서 팩토리 메소드 패턴을 적용에 가장 알맞은 곳은 바로 유닛생성부분이다. 

단순히 팩토리 메소드 패턴만으로는 한계가 있을수도 있지만 좋은 예가 된다. (실제로는 여러 패턴이 어우러져있을 것이다.)

- 스타크래프트에서 건물에서 생성되는 유닛들은 모두 비슷한 명령들을 가지고 있다. 이동/공격/죽음/멈춤등의 액션이 존재하는데 이 액션들이 Unit에서 구현될 인터페이스가 되며 Marine이나 Dropship같은 객체들은 해당 인터페이스에 대한 각자 특색있는 기능을 구현하게 된다. (예를들어, Marine은 이동을 하면 지상을 이동하지만 Dropship은 공중에서 이동이 된다.)

- Barraks에서 유닛을 생성할 때, 유닛 빌드타임 (유닛 생성시간) -> 유닛을 전역변수에 등록 -> 유닛 생성 종료 알림 -> 유닛 화면 표출등의 일련의 과정들이 일어나게 되는데 이것들은 Barraks의 유닛생성 부분에서 정의를 하게 해서, 여러군데에서 유닛을 만들때마다 하는 작업을 줄일수가 있으며 하나를 빼먹는 실수를 하지 않을 수가 있다.

- Barraks가 생상할 수 있는 신규 유닛추가할때는, 간단히 Unit의 인터페이스를 구현하는 클래스를 만들고 Barraks에서 해당 유닛을 행성하는 부분만 추가해주면 나머지는 수정할 필요가 없게된다. ( 사용자가 유닛을 다루는 부분에서는 실제 Marine 객체를 가지고 하는 것이 아니라 Unit이라는 상위 추상 객체를 이용하기 때문에 신규 객체가 추가되더라도 소스의 수정이 없게 되는 것이다. )

  

3. 소스

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace DesignPattern
{
    class Factory_method_inGame
    {
        static void Main()
        {
            Buliding[] buildings = new Buliding[2];
            buildings[0] = new Barraks();
            buildings[1] = new StarPort();
 
            List<Unit> allUnit = new List<Unit>();
            allUnit.Add(buildings[0].makeUnit("적 마린"));
            allUnit.Add(buildings[1].makeUnit("아군 드랍십"));
 
            allUnit[0].Move("언덕");
            Unit attactingMarine = allUnit[0];
            allUnit[1].Attacked(ref attactingMarine);
 
        }
    }
 
    public abstract class Unit
    {
        public string m_strName;
        public int m_intAttackPower;
        public int m_intHealth;
 
        public abstract void Move(string _strPoint);
        public abstract void Attacked(ref Unit _unitTarget);
 
       
    }
 
    public class Marine : Unit
    {
        public Marine(string _strName)
        {
            this.m_strName = _strName;
            this.m_intAttackPower = 15;
            this.m_intHealth = 100;
            Console.WriteLine(_strName + " : 생성완료");
        }
 
        public override void Move(string _strPoint)
        {
            Console.WriteLine(m_strName + " : " + _strPoint + " 이동 완료 ");
        }
        public override void Attacked(ref Unit _unitTarget)
        {
            this.m_intHealth -= _unitTarget.m_intAttackPower;
            Console.WriteLine(m_strName + " 공격당함 : 공격자 -> " + _unitTarget.m_strName + " : 남은 체력 " + this.m_intHealth.ToString());
        }
    }
 
    public class DropShip : Unit
    {
        public DropShip(string _strName)
        {
            this.m_strName = _strName;
            this.m_intAttackPower = 0;
            this.m_intHealth = 100;
            Console.WriteLine(_strName + " : 생성완료 ");
        }
 
        public override void Move(string _strPoint)
        {
            Console.WriteLine(m_strName + " : " + _strPoint + " 이동완료 ");
        }
 
        public override void Attacked(ref Unit _unitTarget)
        {
            this.m_intHealth -= +_unitTarget.m_intAttackPower;
            Console.WriteLine(m_strName + " 공격당함 : 공격자 ->" + _unitTarget.m_strName + " : 남은 체력 " + this.m_intHealth.ToString());
        }
    }
 
    public abstract class Buliding
    {
        public abstract Unit makeUnit(string _strName);
    }
    public class Barraks : Buliding
    {
        public override Unit makeUnit(string _strName)
        {
            return new Marine(_strName);
        }
    }
 
    public class StarPort : Buliding
    {
        public override Unit makeUnit(string _strName)
        {
            return new DropShip(_strName);
        }
    }
}

4. 결과

* Feedback

- allUnit[0].Move("언덕“) 여기에서처럼 유닛에 상관없이 다양한 종류의 타입들을 동리한 코드로 처리가 가능하다.

보통 스타크래프트에서 유닛들을 이동시킬 떄 마우스 스크롤로 지정해서 한번에 이동하는데, 마린이나 메딕, 파벳등의

다른 타입의 유닛들이 동시에 옴직이게 된다.

- 스타크래프트 이외에 다른 용도로는 많이 있겠지만, 데이터베이스 접속을 할때도 응용이 가능하다.

보통 프로그램에서 DB를 접속해서 데이터를 처리하는데 하나의 DB만 있는 것이 아니라 MSSQL, MySQL, Orcle등등의

것들을 하나의 인터페이스로 처리가 가능하다.

- 이 패턴을 알기전에는 DB에 접속에 대해서 단순히 심플팩토리와 인터페이스를 이용해서 구현을 하였다.

사용 및 효율 면에서 이 패턴과 거의 동일하게 동작한다. 확장성 및 소스유지관리 측면에서는 팩토리 매소트 패턴이 좋다. 

 

'C#' 카테고리의 다른 글

[C#] 컬렉션 Collection  (0) 2023.07.25
[C#] 프로퍼티  (0) 2023.07.25
[C#] 프로그램 중복실행 방지 ( 단일 프로세스만 실행하기 )  (0) 2023.07.18
[C#] 동적 컴파일  (0) 2023.07.18
.NET Serialization 닷넷 객체직렬화  (0) 2023.07.18

관련글 더보기