1

質問を強調するために、私は特別に不変の単語と突然変異で遊んだ。

状態遷移をモデル化し、関連データを収集する抽象化をプロパティ設計したいと思います(おそらく、この2つの要件は単一責任に違反していますか?)。

私は次のようなものをスケッチします:

enum StateTypeEnum
{
    State1,
    State2,
    State3
}

class State
{
    private readonly StateTypeEnum _stateType;
    private readonly IEnumerable<string> _stateData;

    public State(StateTypeEnum stateType)
    {
        _stateType = stateType;
        _stateData = new List<string>(new string[] { });
    }

    public State(StateTypeEnum stateType, IEnumerable<string> stateData)
    {
        _stateType = stateType;
        _stateData = new List<string>(stateData);
    }


    public State ChangeState(StateTypeEnum stateType)
    {
        return new State(stateType, _stateData);
    }

    public State ChangeState(StateTypeEnum stateType, IEnumerable<string> stateData)
    {
        return new State(stateType, _stateData.Concat(stateData));
    }

    public State ChangeState(IEnumerable<string> stateData)
    {
        return new State(_stateType, _stateData.Concat(stateData));
    }

    public IReadOnlyList<string> StateData
    {
        get
        {
            return new ReadOnlyCollection<string>(_stateData.ToList());
        }
    }

    public StateTypeEnum CurrentStateType
    {
        get
        {
            return _stateType;
        }
    }
}

そしてそれを次のように使用します:

var state1 = new State(StateTypeEnum.State1);
var state2 = state1.ChangeState(StateTypeEnum.State3);
var state3 = state2.ChangeState(new[] { "a", "b", "c" });
var state4 = state3.ChangeState(StateTypeEnum.State1, new[] { "d", "e" });
var state5 = state4.ChangeState(StateTypeEnum.State3);

Debug.Assert(
    state5.CurrentStateType == StateTypeEnum.State3);
Debug.Assert(
    state5.StateData.SequenceEqual(new[] { "a", "b", "c", "d", "e" }));

前の質問から、 .NET不変コレクションを使用できることがわかりました。

しかし、これはここでの私の質問の中心ではありません。そのような設計が、関連データの遷移をモデル化する不変のデータ構造の正しい(または許容可能な)実装につながるかどうかを知りたいです。

代替実装:

列挙を削除して、Stateのサブタイプを定義できますか?

class State {

  public StateTransition1 ChangeToTransition1() {
    return new StateTransition1( ... );
  }

  public StateTransition2 ChangeToTransition2(IEnumerable data) {
    return new StateTransition2( ... );
  }
}

このように、私は、transition1が明確に定義された特定の意味であり、transition2がそれ自体である(たとえば、特定の関連データが含まれている)ことを明確に示しています。

次に、コンシューマーコードは、フローを制御するために列挙型ではなくサブタイプタイプをクエリできます。

状態変更方法を適切に因数分解する:

状態はコンストラクターを使用して再作成されるため、拡張メソッドとしての因数分解( `` ChangeToXXXX`)変更状態メソッドが、より保守可能で正式な正しい設計につながる可能性があるのではないかと思います。

4

2 に答える 2

1

Stateが不変であるという保証が必要な場合は、クラスを封印Stateすることを検討する必要があります。これにより、その唯一無二のクラスによって公開されたメソッドを介した (新しい不変状態への) 状態遷移のみが許可され、enumこれまでのように s が維持されます。 . この場合、拡張メソッドを使用することは、実装を封印したままにするため、機能的に同等です。

それを「OOP スタイル」にリファクタリングしたいが、これによって変更可能な実装が可能になるかどうかを気にしない場合は、「Typecode をサブクラスに置き換える」リファクタリングを使用して、次のようなものにすることができます。

interface IState
{
    IEnumerable<string> Data { get; }
}

class State1 : IState 
{  ...  }

class State2 : IState 
{  ...  }

class State3 : IState 
{  ...  }

この場合、IStateインターフェイスに拡張メソッドを含めることができますが、遷移ロジックを実際の状態から完全に分離する必要があることを意味します。

または、IStateインターフェースにメソッドを実装させ、GetNextState(someInput)次にどの状態にするかを各状態に決定させることができます。

または、メンバーを持つ抽象基本クラスを使用しinternal abstractて、他のアセンブリがそれらから派生するのを防ぐことができます。

追加の背景がないと、このケースのどれがアプリケーションに最適かを正確に判断するのは困難です。たとえば、これらの列挙型は遷移に関する決定に関与していないように見えるため、これらの列挙型の目的が不明です。

于 2013-03-06T12:29:11.363 に答える
-2

そんなパターンが参考になるかも

于 2013-03-06T10:52:08.370 に答える