14

編集:これは複数のインターフェイスを使用して強制されるため、間違った順序で呼び出されることを心配していません.ターミナルメソッドがまったく呼び出されることを心配しています.


ビルダー パターンを使用して、システムでアクセス許可を作成しています。ビルダー パターンを選択したのは、私たちの製品ではセキュリティが非常に重要であるため (これには未成年者が関与するため、COPPAなど)、パーミッションが読みやすいことが不可欠であると感じ、読みやすさが最も重要であると感じた (つまり、流暢なスタイルを使用する) 6 つの値を持つ単一の関数ではなくビルダー パターン)。

コードは次のようになります。

 permissionManager.grantUser( userId ).permissionTo( Right.READ ).item( docId ).asOf( new Date() );

これらのメソッドは、ターミナル メソッド (つまり asOf ) がデータベースへのアクセス許可をコミットする際に、プライベート バッキング Bean を設定します。そのメソッドが呼び出されない場合、何も起こりません。時折、開発者は端末メソッドを呼び出すのを忘れることがありますが、これはコンパイラ エラーの原因にはならず、コードのすばやい読み取り/スキミングで見落としがちです。

この問題を防ぐにはどうすればよいですか? 保存する必要がある Permission オブジェクトを返したくありません。これは、ノイズが増え、パーミッション コードの読み取り、追跡、追跡、理解が難しくなるためです。

端末コマンドによってマークされるバッキングにフラグを置くことを考えました。次に、finalizeメソッドのフラグを確認し、オブジェクトが永続化せずに作成された場合はログに書き込みます。(finalize実行が保証されていないことはわかっていますが、私が考えることができる最高のものです。)

4

7 に答える 7

12

解決

この流暢なAPIパターンを構造化する良い方法はthis、各メソッドから返すのではなく、リストにあるはずのメソッドのみをサポートするMethod Object Patternを実装するインスタンスを返し、最後のメソッド呼び出しで必要な実際のオブジェクトを返すことです。Interfacenext

それがそのオブジェクトのインスタンスを取得する唯一の方法である場合は、常に最後のメソッドを呼び出す必要があります。

Q6613429.java

package com.stackoverflow;

import javax.annotation.Nonnull;
import java.util.Date;

public class Q6613429
{
    public static void main(final String[] args)
    {
        final Rights r = PermissionManager.grantUser("me").permissionTo("ALL").item("EVERYTHING").asOf(new Date());
        PermissionManager.apply(r);
    }

    public static class Rights
    {
        private String user;
        private String permission;
        private String item;
        private Date ofDate;

        private Rights() { /* intentionally blank */ }
    }

    public static class PermissionManager
    {
        public static PermissionManager.AssignPermission grantUser(@Nonnull final String user)
        {
            final Rights r = new Rights(); return new AssignPermission() {

                @Override
                public AssignItem permissionTo(@Nonnull String p) {
                    r.permission = p;
                    return new AssignItem() {
                    @Override
                    public SetDate item(String i) {
                        r.item = i;
                        return new SetDate()
                    {
                        @Override
                        public Rights asOf(Date d) {
                            r.ofDate = d;
                            return r;
                        }
                    };}
                };}
            };
        }

        public static void apply(@Nonnull final Rights r) { /* do the persistence here */ }

        public interface AssignPermission
        {
            public AssignItem permissionTo(@Nonnull final String p);
        }

        public interface AssignItem
        {
            public SetDate item(String i);
        }

        public interface SetDate
        {
            public Rights asOf(Date d);
        }
    }
}

これにより、一連の構築呼び出しが強制され、次のインターフェイスが何であるかが示され、使用可能なメソッドのみが示されるため、コードの完了に非常に便利です。

これは、中央にオプションのものがある、より完全な例です。

UrlBuilder.java

これは、オブジェクトを構築するための絶対確実なチェック例外のない方法を提供しURLます。

永続性と構造を混合することは、懸念を混合することです。

オブジェクトの作成と保存は別の懸念事項であり、混在させないでください。それ.build()が意味するものではなく.store()、その逆もありbuildAndStore()、懸念の混合がすぐにさまざまな場所でさまざまなことを行うことを指摘していることを考慮すると、必要な保証が得られます。

の完全に構築されたインスタンスのみを受け入れる別のメソッドで永続化コードを呼び出しますRights

于 2011-07-07T16:10:49.987 に答える
11

コードで本当に強制したい場合は、PMD または Findbugs のルールを作成できます。これには、コンパイル時にすでに使用可能であるという利点があります。


ランタイム: ユーザーがビルダーを正しい順序で呼び出すことのみを確認したい場合は、ステップごとに個別のインターフェースを使用します。

grantUser() は、メソッド item() を持つ IResourceSetter を返すメソッド permissionTo() を持つ ISetPermission を返します...

これらすべてのインターフェースを 1 つのビルダーに追加できますが、メソッドが次のステップのために正しいインターフェースを返すことを確認してください。

于 2011-07-07T16:01:24.133 に答える
7
public class MyClass {
    private final String first;
    private final String second;
    private final String third;

    public static class False {}
    public static class True {}

    public static class Builder<Has1,Has2,Has3> {
        private String first;
        private String second;
        private String third;

        private Builder() {}

        public static Builder<False,False,False> create() {
            return new Builder<>();
        }

        public Builder<True,Has2,Has3> setFirst(String first) {
            this.first = first;
            return (Builder<True,Has2,Has3>)this;
        }

        public Builder<Has1,True,Has3> setSecond(String second) {
            this.second = second;
            return (Builder<Has1,True,Has3>)this;
        }

        public Builder<Has1,Has2,True> setThird(String third) {
            this.third = third;
            return (Builder<Has1,Has2,True>)this;
        }
    }

    public MyClass(Builder<True,True,True> builder) {
        first = builder.first;
        second = builder.second;
        third = builder.third;
    }

    public static void test() {
        // Compile Error!
        MyClass c1 = new MyClass(MyClass.Builder.create().setFirst("1").setSecond("2"));

        // Compile Error!
        MyClass c2 = new MyClass(MyClass.Builder.create().setFirst("1").setThird("3"));

        // Works!, all params supplied.
        MyClass c3 = new MyClass(MyClass.Builder.create().setFirst("1").setSecond("2").setThird("3"));
    }
}
于 2013-07-27T09:19:36.473 に答える
2

まさに必要なことを行うステップ ビルダー パターンがあります: http://rdafbn.blogspot.co.uk/2012/07/step-builder-pattern_28.html

于 2013-01-16T09:54:01.830 に答える
0

Builder が正しく構築されたことを最初に検証する別の手順で、新しいアクセス許可を適用します。

PermissionBuilder builder = permissionManager.grantUser( userId ).permissionTo( Right.READ ).item( docId ).asOf( new Date() );
permissionManager.applyPermission(builder); // validates the PermissionBuilder (ie, was asOf actually called...whatever other business rules)
于 2011-07-07T16:46:41.310 に答える
-1

Diezelを使用してインターフェースのセット全体を生成することとは別に、強制的に「トークン」オブジェクトを取得する必要があります。

    Grant.permissionTo( permissionManager.User( userId ).permissionTo( Right.READ ).item( docId ).asOf( new Date() ) );

last/exit メソッドが正しいタイプを返すまで、ユーザーはステートメントを終了できません。Grant.permissionTo は、静的メソッド、静的にインポートされた単純なコンストラクターにすることができます。アクセス許可を実際に permissionManager に登録するために必要なすべてを取得するため、構成する必要も、構成を介して取得する必要もありません。

Folks at Guice は別のパターンを使用します。それらは、パーミッションを構成するために使用される「呼び出し可能」を定義します (Guice では、代わりにバインディングがすべてです)。

    public class MyPermissions は Permission を拡張します{

    public void configure(){
    grantUser( userId ).permissionTo( Right.READ ).item( docId ).asOf( new Date() );
    }

    }

    permissionManager.add(新しい MyPermissions() );

grantUser は保護されたメソッドです。permissionManager は、MyPermissions に完全修飾されたアクセス許可のみが含まれていることを確認できます。

単一のアクセス許可の場合、これは最初のソリューションよりも最悪ですが、一連のアクセス許可の場合はよりクリーンです。

于 2011-12-13T00:00:44.070 に答える