13

私は GWT のアクティビティと場所のフレームワークを使用してアプリケーションを構築していますが、順調に進んでいます。私を悩ませているのは、ActivityMapper実装が (1) アプリケーション内のすべてのビューを受け取る (2) 受け取った場所に基づいてアクティビティをインスタンス化するための巨大な if/else ブロックを含むことです。閲覧数が増えれば増えるほど悪くなる。

ActivityMapperのスクリーンショット

私はすでにGinを使用していますが、ここでどのように使用できるかわかりません。

からボイラープレートを削減または削除するにはどうすればよいActivityMapperですか?

4

6 に答える 6

3

素晴らしい答えはまだありません。私はコード生成スキームを念頭に置いていますが、現時点ではすべてホワイト ボードに落書きしています。ジンのユーザーにとっては、Place Scope が便利なようです。

Re: if/else カスケード、一般的なアプローチの 1 つは、Place オブジェクトに訪問者パターンを実装させることです。たとえば、あなたのアクティビティに AssistedInject がセットアップされていると仮定しましょう (そして、ずさんなフィールド インジェクションを許してください。これは単なるスケッチです)。

class BasePlace extends Place {
    <T> T acceptFilter(PlaceFilter filter);
}

interface PlaceFilter<T> {
  T filter(FooPlace place);
  T filter(BarPlace place);
  T filter(BazPlace place);
}

public class MainActivities implements ActivityMapper {
  @Inject FooFactory fooMaker;
  @Inject BarFactory barMaker;
  @Inject BazFactory bazMaker;

  public Activity getActivity(PlaceChangeEvent e) {
     return ((BasePlace)e.getPlace()).acceptFilter(
       new PlaceFilter<Activity>() {
         Activity filter(FooPlace place) {
           return fooMaker.create(place);
         }
         Activity filter(BarPlace place) {
           return barMaker.create(place);
         }
         Activity filter(BazPlace place) {
           return bazMaker.create(place);
         }
       })         
   }
}
于 2011-04-27T15:57:55.473 に答える
2

1 つの可能性は、Place クラス階層のルートで createActivity() メソッドを定義することです。Place サブクラスは、関連付けられている Activity の新しいインスタンスを返すことができます。

@Override
public Activity getActivity(Place place) {
    return ((BaseAppPlace)place).createActivity();
}

これには、その if/else ブロックが排除され、新しい Place/Activity を追加するときに変更する場所が 1 つ少なくなるという利点があります。これの欠点は、Ginjector に委譲しているだけであっても、Place クラスを Activity 作成動作で汚染することです。

于 2011-12-04T20:06:14.810 に答える
1

実際、このタスクにはカスタム定型コードを使用しています。

public class PuksaActivityMapper implements ActivityMapper {
private HashMap<String, ActivityContainer> mappings;

@Inject
private SearchResultActivityContainer searchResultContainer;
@Inject
private HelloActivityContainer helloContainer;

@Override
public Activity getActivity(Place place) {
    ActivityContainer container = getMappings().get(place.getClass().getName());

    return container.getActivity(place);
}

public HashMap<String, ActivityContainer> getMappings() {
    if (mappings == null) {
        mappings = new HashMap<String, ActivityContainer>();

        mappings.put(ShowResultsPlace.class.getName(), searchResultContainer);
        mappings.put(HelloPlace.class.getName(), helloContainer);
    }
    return mappings;
}

}

ここで、ActivityContainer は単純なファクトリ タイプです (この時点から、従来の ioc メソッドを使用できます)。

もちろん、今はマップ ルックアップ/人口を使用して 'if ブロック' を変更するだけですが、Gin マルチバインディング(魔女は現在存在しません) と組み合わせると、その仕事を行うことができます。

また、Gin の機能強化- GWT アクティビティ/場所用の汎用 GinModule も有望に見えます。

于 2011-04-27T12:25:09.630 に答える
1

私のような人々がここに着陸し、まだ答えがないための将来の参考のために。PlaceFactory の GIN とジェネレーターを使用して、次のソリューションになりました。

これが私のトークンの外観です。例: #EditUser/id:15/type:Agent

すべての Place から拡張する AbstractPlace があります。

public abstract class AbstractPlace extends Place {
    public abstract Activity getActivity();
}

例の場所:

public class EditUserPlace extends AbstractPlace {

private Long id;

private User.Type type;

//getters and setters

@Override
public Activity getActivity() {
    return App.getClientFactory().getEditUserPresenter().withPlace(this);
}
}

Defered バインディングの PlaceFactory インターフェース:

public interface PlaceFactory {
    Place fromToken(String token);
    String toToken(Place place);
}

および場所クラスを登録するための注釈

public @interface WithPlaces {
    Class<? extends Place>[] value() default {};
}

および PlaceFactoryGenerator

GWTモジュールでジェネレーターをセットアップします

<generate-with class="app.rebind.place.PlaceFactoryGenerator">
    <when-type-assignable class="app.client.common.AppPlaceFactory"/>
</generate-with>

public class PlaceFactoryGenerator extends Generator {

    private TreeLogger logger;
    private TypeOracle typeOracle;
    private JClassType interfaceType;
    private String packageName;
    private String implName;
    private Class<? extends Place>[] placeTypes;

    @Override
    public String generate(TreeLogger logger,
            GeneratorContext generatorContext, String interfaceName)
            throws UnableToCompleteException {

        this.logger = logger;
        this.typeOracle = generatorContext.getTypeOracle();
        this.interfaceType = typeOracle.findType(interfaceName);
        this.packageName = interfaceType.getPackage().getName();
        this.implName = interfaceType.getName().replace(".", "_") + "Impl";

        // TODO Trocar annotation por scan
        WithPlaces places = interfaceType.getAnnotation(WithPlaces.class);
        assert (places != null);

        Class<? extends Place>[] placeTypes = places.value();

        this.placeTypes = placeTypes;

        PrintWriter out = generatorContext.tryCreate(logger,
                packageName, implName);

        if (out != null) {
            generateOnce(generatorContext, out);
        }

        return packageName + "." + implName;
    }

    private void generateOnce(GeneratorContext generatorContext, PrintWriter out) {
        TreeLogger logger = this.logger.branch(
                TreeLogger.DEBUG,
                String.format("Generating implementation of %s",
                        interfaceType));

        ClassSourceFileComposerFactory factory = new ClassSourceFileComposerFactory(
                packageName, implName);
        factory.addImport(interfaceType.getQualifiedSourceName());
        factory.addImplementedInterface(interfaceType.getSimpleSourceName());

        factory.addImport(StringBuilder.class.getCanonicalName());
        factory.addImport(Map.class.getCanonicalName());
        factory.addImport(HashMap.class.getCanonicalName());
        factory.addImport(Place.class.getCanonicalName());

        for (Class<? extends Place> place : placeTypes)
            factory.addImport(place.getCanonicalName());

        SourceWriter sw = factory.createSourceWriter(generatorContext, out);

        sw.println("public Place fromToken(String token) {");
        sw.indent();
        sw.println("int barAt = token.indexOf('/');");
        sw.println("String placeName = token;");
        sw.println("Map<String, String> params = new HashMap<String, String>();");
        sw.println("if (barAt > 0) {");
        sw.indent();
        sw.println("placeName = token.substring(0, barAt);");
        sw.println("String[] keyValues = token.substring(barAt + 1).split(\"/\");");
        sw.println("for (String item : keyValues) {");
        sw.indent();
        sw.println("int colonAt = item.indexOf(':');");
        sw.println("if (colonAt > 0) {");
        sw.indent();
        sw.println("String key = item.substring(0, colonAt);");
        sw.println("String value = item.substring(colonAt + 1);");
        sw.println("params.put(key, value);");
        sw.outdent();
        sw.println("}");
        sw.outdent();
        sw.println("}");
        sw.outdent();
        sw.println("}\n");

        for (Class<? extends Place> placeType : placeTypes) {
            String placeTypeName = placeType.getSimpleName();

            int replaceStrPos = placeTypeName.lastIndexOf("Place");
            String placeName = placeTypeName.substring(0, replaceStrPos);

            sw.println("if (placeName.equals(\"%s\")) {", placeName);
            sw.indent();

            sw.println("%s place = new %s();", placeTypeName, placeTypeName);

            generateSetExpressions(sw, placeType);

            sw.println("return place;");

            sw.outdent();
            sw.println("}\n");

        }

        sw.println("return null;");
        sw.outdent();
        sw.println("}\n");

        sw.println("public String toToken(Place place) {");
        sw.indent();
        sw.println("StringBuilder token = new StringBuilder();\n");

        for (Class<? extends Place> placeType : placeTypes) {
            String placeTypeName = placeType.getSimpleName();
            int replaceStrPos = placeTypeName.lastIndexOf("Place");
            String placeName = placeTypeName.substring(0, replaceStrPos);

            sw.println("if (place instanceof %s) {", placeTypeName);
            sw.indent();

            sw.println("%s newPlace = (%s)place;", placeTypeName, placeTypeName);
            sw.println("token.append(\"%s\");", placeName);
            generateTokenExpressions(sw, placeType);
            sw.println("return token.toString();");

            sw.outdent();
            sw.println("}\n");
        }

        sw.println("return token.toString();");
        sw.outdent();
        sw.println("}\n");


        sw.outdent();
        sw.println("}");

        generatorContext.commit(logger, out);
    }

    private void generateTokenExpressions(SourceWriter sw,
            Class<? extends Place> placeType) {
        for (Field field : placeType.getDeclaredFields()) {
            char[] fieldName = field.getName().toCharArray();
            fieldName[0] = Character.toUpperCase(fieldName[0]); 
            String getterName = "get"  + new String(fieldName);
            sw.println("token.append(\"/%s:\");", field.getName());
            sw.println("token.append(newPlace.%s().toString());", getterName);
        }
    }

    private void generateSetExpressions(SourceWriter sw, Class<? extends Place> placeType) {
        for (Field field : placeType.getDeclaredFields()) {
            char[] fieldName = field.getName().toCharArray();
            fieldName[0] = Character.toUpperCase(fieldName[0]); 
            String setterName = "set"  + new String(fieldName);

            List<Method> methods = findMethods(placeType, setterName);

            for (Method method : methods) {
                Class<?>[] parameterTypes = method.getParameterTypes();

                if (parameterTypes.length == 0 || parameterTypes.length > 1)
                    continue;

                Class<?> parameterType = parameterTypes[0];
                String exp = "%s";

                if (parameterType == Character.class) {
                    exp = "%s.charAt(0)";
                } else if (parameterType == Boolean.class) {
                    exp = "Boolean.parseBoolean(%s)";
                } else if (parameterType == Byte.class) {
                    exp = "Byte.parseInt(%s)";
                } else if (parameterType == Short.class) {
                    exp = "Short.parseShort(%s)";
                } else if (parameterType == Integer.class) {
                    exp = "Integer.parseInt(%s)";
                } else if (parameterType == Long.class) {
                    exp = "Long.parseLong(%s)";
                } else if (parameterType == Float.class) {
                    exp = "Float.parseFloat(%s)";
                } else if (parameterType == Double.class) {
                    exp = "Double.parseDouble(%s)";
                } else if (parameterType.getSuperclass().isAssignableFrom(Enum.class)) {
                    exp = parameterType.getCanonicalName() + ".valueOf(%s)";
                } else if (parameterType != String.class){
                    continue;
                }

                String innerExp = String.format("params.get(\"%s\")", field.getName());
                String wrapperExp = String.format(exp, innerExp);
                sw.println("place.%s(%s);", setterName, wrapperExp);
            }
        }
    }

    private List<Method> findMethods(Class<? extends Place> placeType, String name) {
        Method[] methods = placeType.getMethods();
        List<Method> found = new ArrayList<Method>();

        for (Method method : methods) {
            if (method.getName().equals(name)) {
                found.add(method);
            }
        }

        return found;
    }
}

私のActivityMapperはどのように見えますか?

public class AppActivityMapper implements ActivityMapper {

    public Activity getActivity(Place place) {
        AbstractPlace abstractPlace = (AbstractPlace)place;
        return abstractPlace.getActivity();
    }
}

カスタム PlaceHistoryMapper が必要です

public class AppPlaceHistoryMapper implements PlaceHistoryMapper {

    private AppPlaceFactory placeFactory = GWT.create(AppPlaceFactory.class);

    public Place getPlace(String token) {
        return placeFactory.fromToken(token);
    }

    public String getToken(Place place) {
        return placeFactory.toToken(place);
    }
}

そして最後に PlaceFactory が生成されます。これは、プレイスクラスをアノテーションに入れるだけで満足です!

@WithPlaces(value = {
    HomePlace.class,
    EditUserPlace.class
})
public interface AppPlaceFactory extends PlaceFactory {

}
于 2012-04-08T03:16:19.610 に答える
0

まず、このために GWT の問題に関する問題を作成したので、星を付けたり、コメントしたりしてください。これが私がそれを行う方法です:

   public abstract class PlaceWithActivity extends Place {
        public Activity getActivity();
   }

次に、ActivityMapper で次のようにします。

Public Activity get Activity(Place newPlace) {
     return ((PlaceWithActivity) newPlace).getActivity();
 }

すべての場所は PlaceWithActivity を拡張する必要があります。唯一の問題は、ClassCastException のリスクがあるダウンキャストです。場所に getActivity() があった場合、ダウンキャストする必要はありませんが、そうではないので、ダウンキャストするクラスにダウンキャストする必要があります。

私が気に入らないのは、キャストを行って PlaceWithActivity クラスを作成する必要があることです。GWT が私が行っていることのサポートを追加する場合、これは必要ありません。それらに PlaceWithActivity クラスが含まれていれば、それを作成する必要はなく、ActivityManager が PlaceWithActivity クラスの getActivity() メソッドを呼び出すだけであれば、ダウン キャストする必要がないだけでなく、記述する必要さえありません。アクティビティマッパー!

于 2015-01-07T01:20:17.303 に答える