[C# 디자인 패턴] 재귀 제네릭을 사용한 Fluent Builder 패턴

참고


Fluent Builder 상속 문제

  • Fluent Builder 패턴을 이용하여 프로그래밍을 하다 보면, 상속 문제에 부딪히게 됩니다.
  • 예제를 통해 보여 드리도록 하겠습니다.
  • Employee 객체를 만들고 싶다고 가정합니다.
public class Employee
{
    public string Name { get; set; }
    public string Position { get; set; }
    public double Salary { get; set; }
    public override string ToString()
    {
        return $"Name: {Name}, Position: {Position}, Salary: {Salary}";
    }
}
  • Employee 모델 클래스를 만들었고, 여기서 Name 속성에 값을 바인딩 하는 Set 메서드를 Builder 패턴으로 만든다고 가정합니다.
public class EmployeeInfoBuilder
{
    protected Employee employee = new Employee();
    public EmployeeInfoBuilder SetName(string name)
    {
        employee.Name = name;
        return this;
    }
}
  • 이제 EmployeePositionBuilder 클래스를 생성 후, EmployeeInfoBuilder 클래스를 상속 받습니다.
public class EmployeePositionBuilder: EmployeeInfoBuilder
{
    public EmployeePositionBuilder AtPosition(string position)
    {
        employee.Position = position;
        return this;
    }
}
  • 마지막으로 Program.cs 에서 해당 빌더 클래스 객체 생성 후 메서드를 호출합니다.

  • 하지만, 위 이미지에서 보다시피 객체 생성을 할 수 없습니다.
  • 이유는 메서드가 현제 메서드를 구현하거나 상속하지 않는 SetName 유형의 인스턴스를 반환하기 때문입니다.
  • 현재 해당 문제를 해결 하려면, 파생 클래스에서 기본 클래스로 정보를 전달하는 솔루션으로 변경해야 합니다.
  • 그러기 위해서는 재귀 제네릭 접근 방식 을 사용해야 합니다.

Fluent Builder로 재귀 제네릭 구현하기

  • 따라서 EmployeeBuilder직원 개체를 인스턴스화하고 제공하는 역할을 하는 추상 클래스부터 시작하겠습니다.
public abstract class EmployeeBuilder
{
    protected Employee employee;
    public EmployeeBuilder()
    {
        employee = new Employee();
    }
    public Employee Build() => employee;
}
  • EmployeeBuilder 추상 클래스를 생성 하였다면, 다음 EmployeeInfoBuilder 클래스를 제네렉 형식으로 수정합니다.
public class EmployeeInfoBuilder<T>: EmployeeBuilder where T: EmployeeInfoBuilder<T>
{
    public T SetName(string name)
    {
        employee.Name = name;
        return (T)this;
    }
}
namespace ConsoleApp1;

public class EmployeePositionBuilder<T> : EmployeeInfoBuilder<EmployeePositionBuilder<T>> where T : EmployeePositionBuilder<T>
{
    public T AtPosition(string position)
    {
        employee.Position = position;
        return (T)this;
    }
}
  • 이렇게 함으로써 두 클래스 모두에서 상속을 가능하게 됐습니다.
  • 만약, 직원이 급여를 필요로 한다면 EmployeeSalaryBuilder 클래스를 생성하여 WithSalary 메서드를 쉽게 추가할 수 있습니다.
public class EmployeeSalaryBuilder<T>: EmployeePositionBuilder<EmployeeSalaryBuilder<T>> where T: EmployeeSalaryBuilder<T>
{
    public T WithSalary(double salary)
    {
        employee.Salary = salary;
        return (T)this;
    }
}
  • 여기까지 해서 현재 재귀 제네릭으로 Builder 클래스를 만들었습니다.
  • 하지만, 아직은 객체를 생성할 수는 없습니다.
  • 이유는 객체 생성을 할때 타입이 명확하지가 않기 때문입니다.
  • 최종적으로 Builder 객체를 빌드할 수 있는 API를 생성해야 합니다.
  • 코드는 다음과 같습니다.
public class EmployeeBuilderDirector : EmployeeSalaryBuilder<EmployeeBuilderDirector>
{
     public static EmployeeBuilderDirector NewEmployee => new EmployeeBuilderDirector()
}
  • EmployeeBuilderDirector API 클래스를 생성 후, 최종적으로 다음과 같이 Main 함수에 Builder 객체를 생성하여 사용할 수 있습니다.
class Program
{
    static void Main(string[] args)
    {
        var emp = EmployeeBuilderDirector
            .NewEmployee
            .SetName("Maks")
            .AtPosition("Software Developer")
            .WithSalary(3500)
            .Build();
        Console.WriteLine(emp);
    }
}
  • 위와 같이 재귀 제네릭 방법을 이용하여 연속적으로 상속을 받아서 최종적으로 Flunet Builder 패턴 클래스를 작성할 수 있습니다.
728x90

이 글을 공유하기

댓글

Designed by JB FACTORY