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

최용국·2020년 3월 8일
1

디자인패턴

목록 보기
4/6

20-0308 Factory Method Pattern

코드를 작성하다보면 일반화된 클래스의 인스턴스들의 관리가 필요할 때가 있다. 일반화된 클래스의 추가, 수정 등에 개발 리소스를 줄이고 인스턴스의 생성을 보장해줄 때 쓰인다.

어떠한 행위를 하는 객체를 일반화하기 위해 인터페이스를 정의한다. 정의된 인터페이스를 만들기로 약속한 부모 클래스가 있고 부모클래스에서는 파생된 클래스에서 정의하도록 약속을하고 파생 클래스에서 일반화된 인스턴스를 만드는 것을 정의한다.

다음 예제는 비동기로 실행되는 아이템의 갯수를 관리하는 EngineController 싱글 클래스가 있고 EngineController 클래스에 비동기가 동작할 수 있는 ItemQueue가 있다. ItemQueue에 넣을 아이템들을 CreateItem 클래스에서 일반화된 클래스들을 관리한다. 일반화된 클래스는 준비 → 실행 → 종료로 진행되고 아이템이 생성되면 준비를 하고 EngineController에 Item을 넣어준다. 콘솔창에 출력해주는 아이템과 텍스트파일로 출력하는 아이템으로 한다.

콘솔창에 파라미터를 하나만 입력하면 콘솔에 출력만하는 아이템을 쌓고 파라미터를 두 개 입력하면 첫번째는 내용, 두번째는 파일의 이름으로하는 텍스트파일 출력아이템으로 쌓는다. 최종 flush를 입력하면 쌓아있던 아이템들을 준비 후 EngineController로 쌓아준다.

interface IEngineItem

public interface IEngineItem
{
    void Ready();
    void Start();
    void Finish();
}

class BaseItem

public abstract class BaseItem : IEngineItem
{
    public string Contents { get; private set; }
    protected BaseItem(string contents)
    {
        Contents = contents;
    }

    public abstract void Finish();

    public abstract void Ready();

    public abstract void Start();
}

class ConsoleItem

public class ConsoleItem : BaseItem
{
    public ConsoleItem(string contents) : base(contents)
    {
    }

    public override void Finish()
    {
        Console.WriteLine($"{Contents} 끄읕");
    }

    public override void Ready()
    {
        Console.WriteLine($"{Contents} 준비");
    }

    public override void Start()
    {
        Console.WriteLine($"{Contents} 시작");
    }
}

class TextFileItem

public class TextFileItem : BaseItem
{
    public string FileName { get; private set; }
    private FileStream FileStream { get; set; }
    public TextFileItem(string contents, string fileName) : base(contents)
    {
        FileName = fileName;
    }

    public override void Finish()
    {
        FileStream.Close();
        Console.WriteLine($"파일 {FileName} 끄읕");
    }

    public override void Ready()
    {
        if (!File.Exists(FileName))
            FileStream = File.Create(FileName);
        else
            FileStream = File.Open(FileName, FileMode.Append);
        Console.WriteLine($"파일 {FileName} 준비");
    }

    public override void Start()
    {
        byte[] contentsByte = Encoding.UTF8.GetBytes(this.Contents);
        FileStream.Write(contentsByte, 0, contentsByte.Length);
        Console.WriteLine($"파일 {FileName}, {base.Contents} 쓰기");
    }
}

class CreateItem

public abstract class CreateItem
{
    public string Contents { get; private set; }
    public CreateItem(string contents)
    {
        this.Contents = contents;
    }
    protected abstract BaseItem Create();

    public BaseItem Ready()
    {
        var item = Create();
        item.Ready();
        return item;
    }
    
}

class CreateConsolteItem

public class CreateConstoleItem : CreateItem
{
    public CreateConstoleItem(string contents) : base(contents)
    {
    }

    protected override BaseItem Create()
    {
        return new ConsoleItem(base.Contents);
    }
}

class CreateTextFileItem

public class CreateTextFileItem : CreateItem
    {
        public readonly string FilePath = @"C:\CSharp\Study\200224\Pattern\FactoryMethodPattern";

        public readonly string Ext = @".txt";
        public string FileName { get; set; }
        public CreateTextFileItem(string contents, string fileName) : base(contents)
        {
            this.FileName = fileName;
        }

        protected override BaseItem Create()
        {
            var fileName = Path.Combine(FilePath, Path.ChangeExtension(this.FileName, Ext));
            return new TextFileItem(base.Contents, fileName);
        }
    }

class EngineController

public class EngineController
{
    private readonly int EngineCount = 4;
    private int Count = 1;
    private object ItemLock { get; set; }
    private Queue<BaseItem> ItemQueue { get; set; }

    private EngineController()
    {
        ItemLock = new object();
        ItemQueue = new Queue<BaseItem>();
    }

    static EngineController()
    {
        Instance = new EngineController();
    }
    public static EngineController Instance { get; }

    public void Finished()
    {
        lock(ItemLock)
        {
            Count--;
        }
    }
    public void Enqueue(BaseItem item)
    {
        lock(ItemLock)
        {
            ItemQueue.Enqueue(item);
        }
    }

    public BaseItem Dequeue()
    {
        lock (ItemLock)
        {
            if (ItemQueue.Count != 0)
            {
                if (Count > EngineCount)
                    return null;
                else
                {
                    Count++;
                    return ItemQueue.Dequeue();
                }
            }
            else
                return null;
        }
    }

    public async void Deploy()
    {
        await Task.Factory.StartNew(() =>
        {
            while (true)
            {
                if (Dequeue() is BaseItem item)
                {
                    Start(item);
                }
                Thread.Sleep(100);
            }
        });
    }

    public async void Start(BaseItem engineItem)
    {
        await Task.Factory.StartNew(() =>
        {
            engineItem.Start();
            Thread.Sleep(500);
            engineItem.Finish();
        }).ContinueWith((task) => Finished());
    }
        
}

Program

class Program
{
    static void Main(string[] args)
    {
        EngineController.Instance.Deploy();

        Queue<CreateItem> createItems = new Queue<CreateItem>();
        while (true)
        {
            var cmd = Console.ReadLine();

            if (string.CompareOrdinal(cmd, "flush") == 0)
            {
                while (createItems.Count != 0)
                {
                    var item = createItems.Dequeue();
                    EngineController.Instance.Enqueue(item.Ready());
                }

            }
            else
            {
                var param = cmd.Split(' ');

                if (param.Count() == 1)
                    createItems.Enqueue(new CreateConstoleItem(param[0]));
                else if (param.Count() == 2)
                    createItems.Enqueue(new CreateTextFileItem(param[0], param[1]));
            }

        }
    }
}

결과

profile
코딩합시다.

0개의 댓글