[c#] C #의 팩토리 패턴 : 팩토리 클래스에서만 개체 인스턴스를 만들 수 있는지 확인하는 방법은 무엇입니까?

최근에 나는 내 코드의 일부를 보호하는 것에 대해 생각하고있다. 객체를 직접 만들 수는 없지만 팩토리 클래스의 일부 방법을 통해서만 만들 수있는 방법이 궁금합니다. “비즈니스 객체”클래스가 있고이 클래스의 모든 인스턴스가 유효한 내부 상태를 갖도록하고 싶습니다. 이를 달성하기 위해 생성자에서 객체를 생성하기 전에 몇 가지 검사를 수행해야합니다. 이 검사를 비즈니스 논리의 일부로 만들기로 결정하기 전까지는 괜찮습니다. 그렇다면 비즈니스 로직 클래스의 일부 방법을 통해서만 생성 가능하지만 직접적으로는 불가능하도록 비즈니스 객체를 어떻게 배열 할 수 있습니까? C ++의 좋은 오래된 “친구”키워드를 사용하려는 첫 번째 자연스러운 욕구는 C #에서 부족할 것입니다. 그래서 우리는 다른 옵션이 필요합니다 …

몇 가지 예를 들어 보겠습니다.

public MyBusinessObjectClass
{
    public string MyProperty { get; private set; }

    public MyBusinessObjectClass (string myProperty)
    {
        MyProperty = myProperty;
    }
}

public MyBusinessLogicClass
{
    public MyBusinessObjectClass CreateBusinessObject (string myProperty)
    {
        // Perform some check on myProperty

        if (true /* check is okay */)
            return new MyBusinessObjectClass (myProperty);

        return null;
    }
}

입력을 확인하지 않고 MyBusinessObjectClass 인스턴스를 직접 만들 수 있다는 것을 기억할 때까지는 괜찮습니다. 나는 그 기술적 가능성을 완전히 배제하고 싶습니다.

그렇다면 커뮤니티는 이것에 대해 어떻게 생각합니까?



답변

개체를 만들기 전에 일부 비즈니스 논리를 실행하려는 것 같습니다. 모든 더러운 “myProperty”검사 작업을 수행하는 “BusinessClass”내부에 정적 메서드를 만들고 생성자를 개인용으로 만드는 이유는 무엇입니까?

public BusinessClass
{
    public string MyProperty { get; private set; }

    private BusinessClass()
    {
    }

    private BusinessClass(string myProperty)
    {
        MyProperty = myProperty;
    }

    public static BusinessClass CreateObject(string myProperty)
    {
        // Perform some check on myProperty

        if (/* all ok */)
            return new BusinessClass(myProperty);

        return null;
    }
}

호출하는 것은 매우 간단합니다.

BusinessClass objBusiness = BusinessClass.CreateObject(someProperty);


답변

생성자를 비공개로 만들고 팩토리를 중첩 유형으로 만들 수 있습니다.

public class BusinessObject
{
    private BusinessObject(string property)
    {
    }

    public class Factory
    {
        public static BusinessObject CreateBusinessObject(string property)
        {
            return new BusinessObject(property);
        }
    }
}

이는 중첩 된 유형이 둘러싸는 유형의 개인 멤버에 액세스 할 수 있기 때문에 작동합니다. 조금 제한적이라는 것을 알고 있지만 도움이 되길 바랍니다.


답변

또는 정말 멋지게 만들고 싶다면 컨트롤을 반전하십시오. 클래스가 팩토리를 반환하도록하고 클래스를 생성 할 수있는 델리게이트로 팩토리를 계측하십시오.

public class BusinessObject
{
  public static BusinessObjectFactory GetFactory()
  {
    return new BusinessObjectFactory (p => new BusinessObject (p));
  }

  private BusinessObject(string property)
  {
  }
}

public class BusinessObjectFactory
{
  private Func<string, BusinessObject> _ctorCaller;

  public BusinessObjectFactory (Func<string, BusinessObject> ctorCaller)
  {
    _ctorCaller = ctorCaller;
  }

  public BusinessObject CreateBusinessObject(string myProperty)
  {
    if (...)
      return _ctorCaller (myProperty);
    else
      return null;
  }
}

🙂


답변

MyBusinessObjectClass 클래스의 생성자를 내부로 만들고 생성자와 팩토리를 자체 어셈블리로 이동할 수 있습니다. 이제 팩토리 만 클래스의 인스턴스를 생성 할 수 있습니다.


답변

Jon이 제안한 것과는 별도로 팩토리 메소드 (확인 포함)를 처음에 BusinessObject의 정적 메소드로 사용할 수도 있습니다. 그런 다음 생성자를 비공개로 설정하면 다른 모든 사람이 정적 메서드를 사용해야합니다.

public class BusinessObject
{
  public static Create (string myProperty)
  {
    if (...)
      return new BusinessObject (myProperty);
    else
      return null;
  }
}

그러나 진짜 질문은-왜이 요구 사항이 있습니까? 팩토리 나 팩토리 메서드를 클래스로 옮길 수 있습니까?


답변

수년이 지난 후이 질문을 받았으며 내가 보는 모든 답변은 불행히도 정확한 답변을 제공하는 대신 코드를 수행해야하는 방법을 알려줍니다. 당신이 찾던 실제 대답은 당신의 클래스를 private 생성자이지만 public 인스턴스화자를 갖는 것입니다. 즉, 팩토리에서만 사용할 수있는 다른 기존 인스턴스에서만 새 인스턴스를 만들 수 있습니다.

수업을위한 인터페이스 :

public interface FactoryObject
{
    FactoryObject Instantiate();
}

수업 :

public class YourClass : FactoryObject
{
    static YourClass()
    {
        Factory.RegisterType(new YourClass());
    }

    private YourClass() {}

    FactoryObject FactoryObject.Instantiate()
    {
        return new YourClass();
    }
}

그리고 마지막으로 공장 :

public static class Factory
{
    private static List<FactoryObject> knownObjects = new List<FactoryObject>();

    public static void RegisterType(FactoryObject obj)
    {
        knownObjects.Add(obj);
    }

    public static T Instantiate<T>() where T : FactoryObject
    {
        var knownObject = knownObjects.Where(x => x.GetType() == typeof(T));
        return (T)knownObject.Instantiate();
    }
}

그러면 인스턴스화를위한 추가 매개 변수가 필요하거나 생성 한 인스턴스를 전처리하기 위해이 코드를 쉽게 수정할 수 있습니다. 그리고이 코드를 사용하면 클래스 생성자가 비공개이므로 팩토리를 통해 인스턴스화를 강제 할 수 있습니다.


답변

또 다른 (경량) 옵션은 BusinessObject 클래스에서 정적 팩토리 메소드를 만들고 생성자를 개인용으로 유지하는 것입니다.

public class BusinessObject
{
    public static BusinessObject NewBusinessObject(string property)
    {
        return new BusinessObject();
    }

    private BusinessObject()
    {
    }
}