문제

나는 DDD에 대해 배우고 있으며 "가치가"가 불변해야한다는 진술을 발견했습니다. 이것은 객체 상태가 생성 된 후에 변경되어서는 안된다는 것을 의미합니다. 이것은 나에게 새로운 사고 방식이지만 많은 경우에 의미가 있습니다.

좋아, 그래서 나는 불변의 가치 객체를 만들기 시작했다.

  • 나는 그들이 전체 상태를 생성자의 매개 변수로 취하는지 확인합니다.
  • 나는 속성 세터를 추가하지 않습니다.
  • 컨텐츠를 수정하는 방법이 없는지 확인하십시오 (새 인스턴스를 반환).

그러나 이제 8 개의 다른 숫자 값을 포함 할이 값 객체를 만들고 싶습니다. 8 숫자 매개 변수를 가진 생성자를 만들면 사용하기 쉽지 않거나 오히려 숫자를 전달할 때 실수를하기가 쉽습니다. 이것은 좋은 디자인이 될 수 없습니다.

그래서 질문은 다음과 같습니다. 내 불변의 물체를 더 좋게 만드는 다른 방법이 있습니까? 나는 당신의 아이디어를 듣는 데 매우 관심이 있습니다 ..

업데이트: 누군가가 언급하기 전에 여기에서 한 가지 아이디어가 논의되었습니다.C#의 불변의 객체 패턴 - 어떻게 생각하십니까?

그래도 다른 제안이나 의견을 듣는 데 관심이 있습니다.

도움이 되었습니까?

해결책

건축업자 사용 :

public class Entity
{
   public class Builder
   {
     private int _field1;
     private int _field2;
     private int _field3;

     public Builder WithField1(int value) { _field1 = value; return this; }
     public Builder WithField2(int value) { _field2 = value; return this; }
     public Builder WithField3(int value) { _field3 = value; return this; }

     public Entity Build() { return new Entity(_field1, _field2, _field3); }
   }

   private int _field1;
   private int _field2;
   private int _field3;

   private Entity(int field1, int field2, int field3) 
   {
     // Set the fields.
   }

   public int Field1 { get { return _field1; } }
   public int Field2 { get { return _field2; } }
   public int Field3 { get { return _field3; } }

   public static Builder Build() { return new Builder(); }
}

그런 다음 다음과 같이 만듭니다.

Entity myEntity = Entity.Build()
                   .WithField1(123)
                   .WithField2(456)
                   .WithField3(789)
                  .Build()

일부 매개 변수가 선택 사항 인 경우 withxxx 메소드를 호출 할 필요가 없으며 기본값을 가질 수 있습니다.

다른 팁

현재 Args가 많은 생성자 또는 건축업자를 사용해야합니다. C# 4.0 (vs2010)에서는 이름/옵션 인수를 사용하여 C# 3.0 객체 초기화 제와 유사한 것을 달성 할 수 있습니다. 여기. 블로그의 예는 다음과 같습니다.

  Person p = new Person ( forename: "Fred", surname: "Flintstone" );

그러나 비슷한 것이 어떻게 생성자 (또는 다른 복잡한 방법)에 어떻게 적용 할 수 있는지 쉽게 알 수 있습니다. C# 3.0 객체 초기화 제 구문과 비교하십시오 (변이 가능한 유형의) :

 Person p = new Person { Forename = "Fred", Surname = "Flintstone" };

그들에게별로 말하기는별로 없습니다.

Jon Skeet 은이 주제에 대한 몇 가지 생각을 게시했습니다. 여기.

내 머리 꼭대기에서 두 개의 다른 답변이 떠 오릅니다 ...

... 첫 번째이자 아마도 가장 간단한 것은 객체 공장 (또는 건축업자)을 헬퍼로 사용하여 일을 올바르게 얻는 것입니다.

객체 초기화는 다음과 같습니다.

var factory = new ObjectFactory();
factory.Fimble = 32;
factory.Flummix = "Nearly";
var mine = factory.CreateInstance();

... 두 번째는 잠금 () 또는 freeze () 함수가있는 종래의 변호적 인 물체로 객체를 만드는 것입니다. 모든 뮤지터는 물체가 잠겨 있는지 확인하고 예외가있는 경우 예외를 던져야합니다.

객체 초기화는 다음과 같습니다.

var mine = new myImmutableObject();
mine.Fimble = 32;
mine.Flummix = "Nearly";
mine.Lock(); // Now it's immutable.

어떤 방법을 취해야하는지는 당신의 맥락에 크게 의존합니다 - 공장은 일련의 유사한 객체를 구성 할 경우 편리하다는 이점이 있지만, 쓰기 및 유지 관리 할 다른 클래스를 소개합니다. 잠금 가능한 객체는 클래스가 하나만 있다는 것을 의미하지만 다른 사용자는 예기치 않은 런타임 오류를 얻을 수 있으며 테스트가 더 어려워집니다.

아마도 당신이하고있는 일의 영역의 일부이지만, 내 제안이 무효 일 수 있지만, 8 개의 매개 변수를 논리 그룹으로 분류하려고 시도하는 것은 어떻습니까?

매개 변수의 힙을 볼 때마다 객체/방법/회의자가 더 간단해야한다고 생각합니다.

나는 복잡한 생성자가 나에게 나쁜 디자인과 같은 질문에 휩싸였다. 나는 또한 유지하기에는 너무 많은 추가 코드처럼 보이기 때문에 빌더 개념의 열렬한 팬이 아닙니다. 우리가 필요로하는 것은 아이스 사이클 불변성입니다. 즉, 객체가 부동산 세터를 사용할 수있는 곳에서 객체가 돌연변이로 시작됨을 의미합니다. 모든 속성이 설정되면 물체를 불변 상태로 동결시키는 방법이 있어야합니다. 이 전략은 불행히도 C# 언어로 기본적으로 지원되지 않습니다. 따라서 나는이 질문에 설명 된대로 불변의 물체를 만들기위한 내 자신의 패턴을 설계하게되었습니다.

C#의 불변의 객체 패턴 - 어떻게 생각하십니까?

Anders Hejlsberg는 다음 인터뷰에서 36:30부터 이러한 유형의 불변성에 대한 지원에 대해 이야기하고 있습니다.

전문가 대 전문가 : Anders Hejlsberg- C#의 미래

반사를 사용하여 물체의 모든 필드와 게으름을 초기화하여 설정 메소드/함수를 함께 연쇄시키기 위해 "세터"와 같은 메소드 (모나디 한 기능 스타일 사용)를 만들 수 있습니다.

예를 들어:

이 기본 클래스를 사용할 수 있습니다.

public class ImmutableObject<T>
{
    private readonly Func<IEnumerable<KeyValuePair<string, object>>> initContainer;

    protected ImmutableObject() {}

    protected ImmutableObject(IEnumerable<KeyValuePair<string,object>> properties)
    {
        var fields = GetType().GetFields().Where(f=> f.IsPublic);

        var fieldsAndValues =
            from fieldInfo in fields
            join keyValuePair in properties on fieldInfo.Name.ToLower() equals keyValuePair.Key.ToLower()
            select new  {fieldInfo, keyValuePair.Value};

        fieldsAndValues.ToList().ForEach(fv=> fv.fieldInfo.SetValue(this,fv.Value));

    }

    protected ImmutableObject(Func<IEnumerable<KeyValuePair<string,object>>> init)
    {
        initContainer = init;
    }

    protected T setProperty(string propertyName, object propertyValue, bool lazy = true)
    {

        Func<IEnumerable<KeyValuePair<string, object>>> mergeFunc = delegate
                                                                        {
                                                                            var propertyDict = initContainer == null ? ObjectToDictonary () : initContainer();
                                                                            return propertyDict.Select(p => p.Key == propertyName? new KeyValuePair<string, object>(propertyName, propertyValue) : p).ToList();
                                                                        };

        var containerConstructor = typeof(T).GetConstructors()
            .First( ce => ce.GetParameters().Count() == 1 && ce.GetParameters()[0].ParameterType.Name == "Func`1");

        return (T) (lazy ?  containerConstructor.Invoke(new[] {mergeFunc}) :  DictonaryToObject<T>(mergeFunc()));
    }

    private IEnumerable<KeyValuePair<string,object>> ObjectToDictonary()
    {
        var fields = GetType().GetFields().Where(f=> f.IsPublic);
        return fields.Select(f=> new KeyValuePair<string,object>(f.Name, f.GetValue(this))).ToList();
    }

    private static object DictonaryToObject<T>(IEnumerable<KeyValuePair<string,object>> objectProperties)
    {
        var mainConstructor = typeof (T).GetConstructors()
            .First(c => c.GetParameters().Count()== 1 && c.GetParameters().Any(p => p.ParameterType.Name == "IEnumerable`1") );
        return mainConstructor.Invoke(new[]{objectProperties});
    }

    public T ToObject()
    {
        var properties = initContainer == null ? ObjectToDictonary() : initContainer();
        return (T) DictonaryToObject<T>(properties);
    }
}

그렇게 구현할 수 있습니다.

public class State:ImmutableObject<State>
{
    public State(){}
    public State(IEnumerable<KeyValuePair<string,object>> properties):base(properties) {}
    public State(Func<IEnumerable<KeyValuePair<string, object>>> func):base(func) {}

    public readonly int SomeInt;
    public State someInt(int someInt)
    {
        return setProperty("SomeInt", someInt);
    }

    public readonly string SomeString;
    public State someString(string someString)
    {
        return setProperty("SomeString", someString);
    }
}

다음과 같이 사용할 수 있습니다.

//creating new empty object
var state = new State();

// Set fields, will return an empty object with the "chained methods".
var s2 = state.someInt(3).someString("a string");
// Resolves all the "chained methods" and initialize the object setting all the fields by reflection.
var s3 = s2.ToObject();
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top