18

たとえば、クライアントが特定の順序で関数を呼び出す必要があるクラスを設計したいとします。

hasNext();
next();

または、非常に一般的な例として、CookFoodメソッドを持つクラス:

class CookFood {
  getListOfItems();
  mixAllItems();
  heat();
}

2 番目の例では、アイテムを取得した後にのみ混合を行い、混合後にのみ加熱を行うように強制したいと考えています。関数呼び出しのシーケンスを強制する既知のパターンまたは優れたプラクティスはありますか?

4

4 に答える 4

32

Step Builder パターンに興味があるかもしれません。提示されたすべてのケースに必ずしも適切に一致するとは限りませんが、各操作は、次の操作を実行できるインターフェイスを実装する何かを返すという考えです。正しい順序で操作を実行することによってのみオブジェクトを取得できるため、正しい順序で操作を実行する必要があります。

反復 (next/hasNext) の状況では少し強制的に感じるかもしれませんが、

  • 靴下を履いて、
  • それから靴を履いて

パターン。どういうわけかCanWearSocks、次のメソッドのみを持つインターフェイスのインスタンスを取得します。

CanWearShoes putOnSocks()

を呼び出すと、次のメソッドのみを持つインスタンスputOnSocks()が取得されます。CanWearShoes

SockAndShoeWearer putOnShoes()

電話をかけるputOnShoes()と、靴下と靴を履いているものがあり、正しい順序でそれを行う必要がありました。

特に素晴らしいのは、実際には両方のケースで同じオブジェクトを使用できることですが、メソッド シグネチャはインターフェイス タイプのみを返すため、コードはインターフェイス メソッドしか使用できません (コードが卑劣で、オブジェクトを型にキャストしない限り)。タイプが違う)。

これは、反復パターンを実装するかなり不自然な例です。つまり、NextGetter の前に NextChecker を使用するようにします。

public class StepBuilderIteration {

    interface NextChecker {
        NextGetter hasNext();
    }

    interface NextGetter {
        Object next();
        NextChecker more();
    }

    static class ArrayExample {
        final static Integer[] ints = new Integer[] { 1, 2, 3, 4 };

        public static NextChecker iterate() {
            return iterate( 0 );
        }

        private static NextChecker iterate( final int i ) {
            return new NextChecker() {
                public NextGetter hasNext() {
                    if ( i < ints.length ) {
                        return new NextGetter() {
                            public Object next() {
                                return ints[i];
                            }
                            public NextChecker more() {
                                return iterate( i+1 );
                            }
                        };
                    }
                    else {
                        return null;
                    }
                }
            };
        }
    }

    public static void main(String[] args) {
        NextChecker nc = ArrayExample.iterate();
        while (nc != null) {
            NextGetter ng = nc.hasNext();
            if (ng != null) {
                System.out.println(ng.next());
                nc = ng.more();
            }
        }
    }
}

出力は次のとおりです。

1
2
3
4
于 2013-06-23T00:35:03.047 に答える
5

ソース コードに完全にアクセスでき、それを変更できる場合、ファクトリ メソッドパターンとテンプレート メソッドパターンの組み合わせを使用できない原因は何ですか。簡単な例:

public class CookFood {

    public Food MakeFood() {
        PrepareFood();
        HeatFood();
        ServeFood();
    }

    protected abstract void PrepareFood();
    protected abstract void HeatFood();
    protected abstract ServeFood();

}

コードのクライアントは、MakeFoodステップの順序を強制する呼び出しを実行できるようになりました。任意のステップをカスタマイズする場合は、CookFoodその特定のステップをサブクラス化して実装できます。もちろん、ステップPrepareFood(), HeatFood(), ServeFood()は抽象的である必要はありません。カスタマイズのためにサブクラスでオーバーライドできるデフォルトの実装を持つことができます。

于 2013-06-23T13:20:33.150 に答える
0

さまざまなアプローチが考えられますが、その 1 つをここに示します。このアプローチでは、他の関数を呼び出す前に他の関数を 1 回呼び出す必要があると見なされますが、常にそうとは限りません。必要に応じて編集できます。

  1. 関数呼び出しの状態を確認する変数を作成します。誰かが listOfItems を呼び出すたびに、isListed 変数を true に設定できます。次に、mixAllItems の isListed の値をチェックして、getListOfItems が以前に呼び出されたことを確認します。

    class CookFood {
        boolean isListed;
        boolean isMixed;
        boolean isHeated;
    
        public String getListOfItems() {
    
            // do listing and mark as listed
            isListed = true;
    
            return "something";
        }
    
        public void mixAllItems() {
            // check if listed first
            if (isListed) {
                // do mixing
    
                // mark as mixed
                isMixed = true;
            } else {
                System.out.println("You need to call getListOfItems before mixing");
                return;
            }
        }    
    
        public void heat() {
            if (isMixed) {
                // do heating
    
                // mark as mixed
                isHeated = true;
            } else {
                System.out.println("You need to call isMixed before heating");
                return;
            }
        }
    }
    
于 2013-06-23T00:37:21.333 に答える
0

必要なことを行う 1 つの方法は、関数が呼び出されるたびにフラグを設定し、依存関数が呼び出されたときにそのフラグが設定されているかどうかを確認することです。

例えば:

public void getListOfItems() {
    funcGetListOfItemsCalled = true;
    ....
}

public void mixAllItems() {
    if(funcGetListOfItemsCalled) {
        funcMixAllItemsCalled = true;
        ...
    }
}

public void mixAllItems() {
    if(funcMixAllItemsCalled ) {
        ...
    }
}
于 2013-06-23T00:37:39.257 に答える