【问题标题】:Factory method overloading工厂方法重载
【发布时间】:2018-04-26 12:34:43
【问题描述】:

我有一个子类列表,它们都是 Super 类的实例。 我的目标是创建一个工厂,它会根据子类返回不同的结果。

public class SuperClass {
    ...
}

public class SubClass1 extends SuperClass {
    ...
}

public class SubClass2 extends SuperClass {
    ...
}

public class Factory {
    public static getInstance(SubClass1 item) {
        return new EditText();
    }

    public static getInstance(SubClass2 item) {
        return new CheckBox();
    }
}

public class Generator {
    public Generator() {
        List<SuperClass> list = getList();

        for (SuperClass item : list) {
            Factory.getInstance(item);
        }
    }

    List<SuperClass> getList() {
        ...
    }
}

new Generator();

此代码将在编译期间失败,因为它需要 getInstance(SuperClass item) 重载,但如果我添加它,那么它总是会被调用。

有没有办法在不触及 SuperClass、SubClass1、SubClass2 的情况下做到这一点?

UPD。为了澄清我想在这里归档的是原始代码:

import android.content.Context;
import android.view.View;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.RadioButton;

import com.tom_roush.pdfbox.cos.COSArray;
import com.tom_roush.pdfbox.cos.COSDictionary;
import com.tom_roush.pdfbox.cos.COSName;
import com.tom_roush.pdfbox.pdmodel.PDDocument;
import com.tom_roush.pdfbox.pdmodel.PDDocumentCatalog;
import com.tom_roush.pdfbox.pdmodel.PDPage;
import com.tom_roush.pdfbox.pdmodel.PDPageTree;
import com.tom_roush.pdfbox.pdmodel.common.PDRectangle;
import com.tom_roush.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import com.tom_roush.pdfbox.pdmodel.interactive.form.PDAcroForm;
import com.tom_roush.pdfbox.pdmodel.interactive.form.PDCheckbox;
import com.tom_roush.pdfbox.pdmodel.interactive.form.PDField;
import com.tom_roush.pdfbox.pdmodel.interactive.form.PDRadioButton;
import com.tom_roush.pdfbox.pdmodel.interactive.form.PDTextField;


public class Page {
    private View view;
    private Context context;
    private PDDocument file;

    public Page(Context _context, View _view, PDDocument _document) {
        view = _view;
        context = _context;
        document = _document;

        renderFields();
    }

    private void renderFields() {
        PDDocumentCatalog docCatalog = document.getDocumentCatalog();
        RelativeLayout ll = view.findViewById(R.id.pageFields);
        ll.removeAllViews();

        PDPageTree pageTree = docCatalog.getPages();
        PDPage page = pageTree.get(pageIndex);

        PDAcroForm acroForm = docCatalog.getAcroForm();
        List<PDField> fields = acroForm.getFields();

        for (PDField field : fields) {
            String fieldName = field.getFullyQualifiedName();
            COSDictionary fieldDict = field.getCOSObject();

            COSArray fieldAreaArray = (COSArray) fieldDict.getDictionaryObject(COSName.RECT);
            PDRectangle mediaBox = new PDRectangle(fieldAreaArray);

            int fieldColor = Color.argb(180, 220, 228, 254);
            // Factory
            View fieldView = FieldFactory.getViewFromPDField(context, field);
            RelativeLayout.LayoutParams fieldLayoutParams = new RelativeLayout.LayoutParams(
                (int) (mediaBox.getWidth() * posRatio),
                (int) (mediaBox.getHeight() * posRatio)
            );
            fieldLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
            fieldLayoutParams.leftMargin = (int) (left * posRatio);
            fieldLayoutParams.topMargin = (int) (top * posRatio);
            fieldView.setBackgroundColor(fieldColor);
            fieldView.setLayoutParams(fieldLayoutParams);

            ll.addView(fieldView, fieldLayoutParams);
        }
    }
}

...

public class FieldFactory {
    public static View getViewFromPDField(Context context, PDTextField field) {
        return new EditText(context);
    }

    public static View getViewFromPDField(Context context, PDCheckbox field) {
        return new CheckBox(context);
    }

    public static View getViewFromPDField(Context context, PDRadioButton field) {
        return new RadioButton(context);
    }
}

【问题讨论】:

  • 如果itemSubClass3 怎么办?
  • public static getInstance(...) - 这甚至可以编译吗?返回类型呢?
  • 使用泛型怎么样? stackoverflow.com/questions/34291714/…
  • Visitor Pattern 允许您以面向对象的方式执行此操作,但这涉及到 SuperClass 和所有子类。否则,这意味着在Generator() 中使用instanceof 使用一堆if 条件和强制转换来测试类型。
  • @JamesB 对不起,我没有尝试编译这个确切的代码,但我的代码是相似的。

标签: java overloading


【解决方案1】:

不幸的是,没有干净的方法可以做到这一点,因为在编译期间我们不知道要调用哪个方法。为此,您可以使用 instanceof 关键字或使用反射。

假设您的模型或多或少如下所示:

class SuperClass {
    //...
}

class SubClass1 extends SuperClass {
    //...
}

class SubClass2 extends SuperClass {
    //...
}

class Context {
    //...
}

abstract class View {
    private final Context context;

    public View(Context context) {
        this.context = context;
    }

    @Override
    public String toString() {
        return getClass().getSimpleName();
    }
}

class TextBox extends View {

    public TextBox(Context context) {
        super(context);
    }
}

class CheckBox extends View {

    public CheckBox(Context context) {
        super(context);
    }
}

你可以像下面这样实现工厂:

class FieldFactory {
    static Map<Class<? extends SuperClass>, Class<? extends View>> fieldEditorMap = new HashMap<>();

    static {
        fieldEditorMap.put(SubClass1.class, TextBox.class);
        fieldEditorMap.put(SubClass2.class, CheckBox.class);
    }

    public static View getViewFromPDField(Context context, SuperClass field) {
        Class<? extends View> editorClass = fieldEditorMap.get(field.getClass());
        try {
            return editorClass.getConstructor(Context.class).newInstance(context);
        } catch (Exception e) {
            throw new IllegalArgumentException("Can not create view for " + field.getClass().getSimpleName(), e);
        }
    }
}

示例用法:

public class Main {

    public static void main(String[] args) throws Exception {
        List<SuperClass> fields = Arrays.asList(new SubClass1(), new SubClass2(), new SubClass1());
        for (SuperClass field : fields) {
            System.out.println(FieldFactory.getViewFromPDField(new Context(), field));
        }
    }
}

上图:

TextBox
CheckBox
TextBox

当然这只是一个例子,你应该使用一些反射库或工具来更好地实现它。我们假设每个View 实现都给出了构造函数等。

【讨论】:

    【解决方案2】:

    改为将 SuperClass 抽象化并添加一个新的实例方法来满足您的需求,每个子类以其不同的行为覆盖它:

    public abstract class SuperClass {
        public abstract View getView(Context context);
    }
    public class SubClass1 extends SuperClass {
        @Override
        public View getView(Context context) {
            ...
        }
    }
    

    【讨论】:

      【解决方案3】:

      我喜欢Michał Ziober 的解决方案,并希望使用java generics 来扩展它。

      使用泛型背后的原因是拥有一个单一的、通用的实现,以后可以用于多种任务。

      我已将原始解决方案修改为通用解决方案。现在必须实例化工厂,因为它是通用的,并且实例化为我们提供了用于我们选择的特定类的特定工厂。

      我使用我选择的online java compiler 来编译和运行以下代码:

      import java.util.Map;
      import java.util.HashMap;
      import java.util.List;
      import java.util.ArrayList;
      import java.util.Arrays;
      
      public class MyClass {
      
          static class SuperClass {
              //tagging
          }
      
          static class SubClass1 extends SuperClass {
              //
          }
      
          static class SubClass2 extends SuperClass {
              //...
          }
      
          static class Context {
              //...
          }
      
          static abstract class View {
              private final Context context;
      
              public View(Context context) {
                  this.context = context;
              }
      
              @Override
              public String toString() {
                  return getClass().getSimpleName();
              }
          }
      
          static class TextBox extends View {
      
              public TextBox(Context context) {
                  super(context);
              }
          }
      
          static class CheckBox extends View {
      
              public CheckBox(Context context) {
                  super(context);
              }
          }
          static class Pair<P0,P1> {
              public P0 p0;
              public P1 p1;
              public Pair(P0 p0, P1 p1) {
                  this.p0 = p0;
                  this.p1 = p1;
              }
          }
          static class FieldFactory<T extends SuperClass, P extends View> {
              Map<Class<T>, Class<P>> fieldEditorMap = new HashMap<>();
      
              public FieldFactory(List<Pair<Class<T>, Class<P>>> boundClassMapping){
      
                  boundClassMapping.stream().forEach(pair -> fieldEditorMap.put(pair.p0,pair.p1));
              }
      
              public P getViewFromPDField(Context context, T field) {
                  Class<P> editorClass = fieldEditorMap.get(field.getClass());
                  try {
                      return editorClass.getConstructor(Context.class).newInstance(context);
                  } catch (Exception e) {
                      throw new IllegalArgumentException("Can not create view for " + field.getClass().getSimpleName(), e);
                  }
              }
          }
      
          public static void main(String args[]) {
              List<Pair<Class<? extends SuperClass>, Class<? extends View>>> mapping = new ArrayList<>();
              mapping.add(new Pair<Class<? extends SuperClass>, Class<? extends View>>(SubClass1.class, TextBox.class));
              mapping.add(new Pair<Class<? extends SuperClass>, Class<? extends View>>(SubClass2.class, CheckBox.class));
              FieldFactory ff = new FieldFactory(mapping);
              List<SuperClass> fields = Arrays.asList(new SubClass1(), new SubClass2(), new SubClass1());
              for (SuperClass field : fields) {
                  System.out.println(ff.getViewFromPDField(new Context(), field));
              }
          }
      }
      

      至于输出:

      TextBox
      CheckBox
      TextBox
      

      【讨论】:

      • 我刚刚提出了一个使用反射的想法。我不想通过添加泛型使其复杂化,但我认为这是个好主意。
      • @MichałZiober:谢谢哥们! (如果您愿意,请随时投票)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-14
      • 1970-01-01
      相关资源
      最近更新 更多