代码之家  ›  专栏  ›  技术社区  ›  Lehane

Java设计问题——向固定类添加功能

  •  0
  • Lehane  · 技术社区  · 15 年前

    我有一组类,所有这些类都需要以某种(个人)方式进行操作。

    通常我会创建一个 DoSomethingInterface 单用 doSomething() 方法并让每个类以适合每个类需要的方式实现该方法。但是,在这种情况下,我不能这样做,因为类定义是不可修改的(自动生成的)。

    所以,我认为我需要创建一组不同的类,每个类接受一个自动生成的类并对它们执行操作。假设我有两个自动生成的类, Class1 Class2 ,我将首先定义 Operator 接口:

    public interface Operator <TYPE>{
      public void doSomething(TYPE type);
    }
    

    然后每个类实现其中一个

    public class Class1Operator implements Operator<Class1>{
        public void doSomething(Class1 type){
          ...
          ...
        }
    }
    

    public class Class2Operator implements Operator<Class2>{
        public void doSomething(Class2 type){
          ...
          ...
        }
    }
    

    好的,到目前为止还不错。现在,假设我有一个类型的对象 第一类 ,是否有任何方法可以让其操作员不必求助于:

    public Operator getOperator(Object obj){
      if(obj instanceof Class1){
        return new Class1Operator();
      }else if(obj instanceof Class2{
        return new Class2Operator();
      }
      return null;
    }
    

    对我来说这有点像是一种坏习惯…

    我唯一能想到的另一种方法是创建一个操作符到类名的映射,如下所示:

    Map<Class, Operator> allOperators = new HashMap<Class, Operator>();
    allOperators.put(Class1.class, new Class1Operator());
    allOperators.put(Class2.class, new Class2Operator());
    

    然后返回操作员,使用:

    public Operator getOperator(Object obj){
      return allOperators.get(obj);
    }
    

    但这似乎不正确(我不确定,在从类中键入对象时是否有任何问题…)

    关于这些方法中的任何一种是否“正确”的输入?还是有更优雅的解决方案??

    谢谢

    6 回复  |  直到 15 年前
        1
  •  4
  •   Scott Stanchfield    15 年前

    您已经实现的(按类映射方法)是我在教授模式时讨论的GOF访问者模式的一个替代方案。它的效率和可扩展性,甚至在运行时也是如此。比if/else if/else硬接线方法要好得多。

    键入类的唯一问题是,如果实际实例实现的是子类型而不是您提到的类类型,那么查找映射将不起作用。

    如果您需要识别子类型,我建议使用Aaron的方法(沿着超类链走),但您也可能希望查看实现的接口。如果您只需要“精确的类匹配”,请保持getOperator简单。

    注意,getoperator中有一个bug——它应该如下所示:

    public Operator getOperator(Object obj){
        return allOperators.get(obj.getClass());
    }
    

    还有一件事…将您的地图隐藏在另一个类中,并按如下方式进行管理:

    private Map<Class<?>, Operator<?>> map = new HashMap<Class<?>, Operator<?>>();
    public <T> void register(Class<T> clazz, Operator<T> operator) {
        map.put(clazz, operator);
    }
    

    这可以防止任何人注册一个对所键入的类无效的运算符。(您可能希望使用operator作为参数来允许针对超类编写的运算符,但可能不需要这样做)

        2
  •  2
  •   Aaron Maenpaa    15 年前

    构建一个映射的一个问题是它不支持子类,除非您专门注册它们或者扩展get函数来专门查找超级类。

    也就是说,如果B继承自A,并且您已经向A.Class注册了一个运算符。除非将getOperator更改为以下类型,否则使用b.class获取运算符将失败:

    public Operator getOperator(Object obj){
      Class<?> current = obj.getClass();
      Operator op;
    
      while((op = allOperators.get(current)) == null){
        current = current.getSuperclass();
    
        if(current == null){
          /* 
           * We've walked all the way up the inheritance hierarcy
           * and haven't found a handler. 
           */
          return null;
        }
      }
    
      return op;
    }
    

    一旦有了合理的getOperator实现,将类映射到运算符似乎是一种合理的方法。

        3
  •  1
  •   Bruce    15 年前

    您可以通过class.isassignablefrom绕过子类型问题。我一直在使用这个,虽然它不是“访客”优雅,但在实践中是相当好的。

        4
  •  0
  •   willcodejavaforfood    15 年前

    是否可以创建自己的类来扩展生成的类,然后让类实现接口?

        5
  •  0
  •   jrharshath    15 年前

    您是否考虑过:

    public Interface Operator {
        public void doSomething();
    }
    
    public class Class1Operator extends Class1 implements Operator {
        ...
    }
    
    public class Class2Operator extends Class2 implements Operator {
        ...
    }
    

    但是,关于第二个问题,即让一个操作符指向一个对象,而实际上不需要执行“instanceof”mojo(我想这就是看起来不干净的地方):

    我建议,如果您不能根据您的具体需要修改类,请在它们周围编写一个包装器:

    public Interface Operator<T> {
        public void doSomething(T obj);
    }
    
    public Interface WrappedObject<T> {
        public Operator<T> getOperator();
    }
    public class WrappedClass1 extends Class1 implements WrappedObject<Class1> {
        ...
    }
    public class WrappedClass2 extends Class2 implements WrappedObject<Class2> {
        ...
    }
    public class Class1Operator implements Operator<Class1> {
        ...
    }
    public class Class2Operator implements Operator<Class2> {
        ...
    }    
    

    这能满足你的需要吗?

    围绕那些与您的需求不完全匹配,并且不能由您控制的类编写包装纸,这始终是一个很好的实践。它可以帮助您保持代码的健康,即使这些疯狂的类发生了变化。

    干杯,

    JRH。

        6
  •  0
  •   Powerlord    15 年前

    我要说,根据Java处理泛型的方式,仅仅使用接口本身是不可能的。

    在爪哇,泛型在编译时被擦除,并被替换为强制转换。

    实际上我还没有检查它在内部的工作方式,但我猜想,您的界面会变成这样:

    public interface Operator {
      public void doSomething(Object type);
    }
    

    在它被召唤的地方:

    public class Class1Operator implements Operator{
        public void doSomething(Object type){
          Class1 oType = (Class1) type;
          ...
          ...
        }
    }
    

    这仍然不是完全正确的,因为类型也会在返回后被强制转换,加上Java字节码实际上看起来不像Java,但是你可能会得到一般的想法。

    instanceof和map方法应该可以工作,即使它们有点混乱。