2

特定のプロセスをモデル化しようとしていますが、状態パターンが適切である可能性があると考えています。Stateが私のニーズに合うかどうか、そしてそれを永続化メカニズムとどのように組み合わせる必要があるかについて、フィードバックをお寄せください。

Pagesなどの多数のオブジェクトを持つCMSがあります。これらのオブジェクト(Pagesの例を使用しますが、ほとんどのオブジェクトに当てはまります)は、いくつかの状態の1つになります。3つの例は次のとおりです。未公開公開済みリワーク

未公開の場合、編集可能です。公開されると、編集できなくなりますが、リワーク状態に移行できます。リワーク状態では、それらは再び編集可能であり、再公開できます。

明らかに、これらのページが編集可能かどうかの決定は、UIではなくモデル自体で行う必要があります。そのため、Stateパターンが頭に浮かびました。ただし、オブジェクトのプロパティに値が割り当てられないようにするにはどうすればよいですか?各プロパティセッターをチェックするのは悪い考えのようです。

if (!CurrentState.ReadOnly)

これをどのように機能させるかについてのアイデアはありますか?これにはもっと良いパターンがありますか?

4

2 に答える 2

3

ウィキペディアのJavaの例を使用すると、構造にはContextがあり、基本Stateで定義されたメソッドを呼び出します。これは、具体的な状態がオーバーライドします。

状態図

あなたの場合、コンテキストはページのようなものです。一部の州では、このedit()方法は単に何もしません。コンテキスト上の一部のアクションは、暗黙的に状態変更を実行する場合があります。クライアントコードで、現在の状態をテストする必要はありません。

于 2010-01-07T22:03:14.083 に答える
1

アップデート:

私は実際に今朝、あなたの特定のケースで機能し、保守がはるかに簡単になる方法を考えました。ここでは元の2つのポイントを残しますが、代わりに最後のオプションをお勧めしますので、「より良い方法」のセクションにスキップしてください。

  1. ThrowIfReadOnly缶に書かれていることを行うメソッドを作成します。これにより、繰り返しがわずかに少なくなり、ネストが回避されます。

  2. インターフェイスを使用します。必要なIPage機能を実装するを持ち、すべてのパブリックメソッドにを返しIPage、次に、との2つの実装を持ちEditablePageますReadOnlyPageReadOnlyPage誰かがそれを変更しようとすると、ただ例外をスローします。また、インターフェイスに1IsReadOnlyつまたは複数のStateプロパティを配置して、IPageコンシューマーが例外をキャッチせずに実際にステータスを確認できるようにします。

オプション(2)は、多かれ少なかれどのようIListReadOnlyCollection<T>連携するかです。これにより、すべてのメソッドの最初にチェックを行う手間が省けます(したがって、検証を忘れるリスクがなくなります)が、2つのクラスを維持する必要があります。

-より良い方法-

適切な技術仕様は、この問題を明確にするのに大いに役立ちます。ここに実際にあるのは次のとおりです。

  • 一連の任意の書き込み」アクション。
  • 状態に応じて、各アクションの結果は同じです。
  • アクションが実行される(未公開/リワーク)か、失敗/操作なし(読み取り専用)のいずれかです。

本当に抽象化する必要があるのは、アクション自体ではなく、アクションの実行です。したがって、ここで少し機能的に優れていると役立ちます。

public enum PublishingState
{
    Unpublished,
    Published,
    Reworking
}

public delegate void Action();

public class PublishingStateMachine
{
    public PublishingState State { get; set; }

    public PublishingStateMachine(PublishingState initialState)
    {
        State = initialState;
    }

    public void Write(Action action)
    {
        switch (State)
        {
            case PublishingState.Unpublished:
            case PublishingState.Reworking:
                action();
                break;
            default:
                throw new InvalidOperationException("The operation is invalid " +
                    "because the object is in a read-only state.");
        }
    }
}

これで、クラス自体を作成するのはほとんど簡単になります。

public class Page
{
    private PublishingStateMachine sm = new
        PublishingStateMachine(PublishingState.Unpublished);

    private string title;
    private string category;

    // Snip other methods/properties
    // ...

    public string Title
    {
        get { return title; }
        set { sm.Write(() => title = value; }
    }

    public string Category
    {
        get { return category; }
        set { sm.Write(() => category = value; }
    }

    public PublishingState State
    {
        get { return sm.State; }
        set { sm.State = value; }
    }
}

これは多かれ少なかれStateパターンを実装するだけでなく、異なる状態に対して別々のクラスや別々のコードパスを維持する必要もありません。たとえば、を操作なしに変えたい場合は、メソッドからステートメントをInvalidOperationException削除するだけです。または、そのような状態を追加する場合は、1行追加するだけです。throwWriteReviewingcase

これは、状態遷移や、状態に応じて異なることを行う非常に複雑なアクション(単に「成功」​​または「失敗」以外)を処理しませんが、それが必要なようには思えません。したがって、これにより、使用する追加のコードをほとんど必要としないドロップイン状態の実装が得られます。

もちろん、依存性注入/ AOPのオプションはまだありますが、そのアプローチには明らかに多くのオーバーヘッドがあり、私はおそらくそれをそれほど単純なものには使用しないでしょう。

于 2010-01-07T22:00:36.000 に答える