设计对象可以解析自己的对象层次结构

时间:2018-01-12 19:05:28

标签: java parsing

我正在设计一个具有模拟对象层次结构的模拟器。要引导模拟器,必须从文件加载初始对象。每个对象都必须从简单的键值文本表示中解析自己。我们将不时添加新的对象类。

不幸的是,没有"静态继承"在Java中。特别是,以下方法本来很不错:

public class SimObject {
   /**
    * (unfortunately illegal) - to be implemented by subclasses,
    * returns a new object if successful, or none on failure.
    */
   static abstract SimObject buildFromAttributes(Map<String,String> attrs);

  // ... other constructors, behavior
}

这将允许使用类似下面的解析代码,通过查找正确构建它的第一个解析器来创建适当类型的SimObject

public SimObject parse(Map<String,String> attrs, Class<SimObject>[] classes) {
   for (Class<SimObject> c : classes) {
       SimObject o = c.buildFromAttributes(attrs); // illegal, I know
       if (o != null) return o;
   } 
   throw new IllegalArgumentException("Could not create valid simobject");
}

由于这是不可能的,我目前正在使用

public class SimObject {
   /** empty constructor, to call buildFromAttributes from */
   SimObject() {} 
   /**
    * returns a new object if successful, or none on failure.
    */
   abstract SimObject buildFromAttributes(Map<String,String> attrs);

  // ... other constructors, behavior
}

public SimObject parse(Map<String,String> attrs, SimObject[] instances) {
   for (SimObject i : instances) {
       SimObject o = i.buildFromAttributes(attrs);
       if (o != null) return o;
   } 
   throw new IllegalArgumentException("Could not create valid simobject");
}

哪个有效,但是buildFromAttributes应该是static似乎很难看,因为它不会访问或修改其对象的状态。

另一种选择是使用单独的对象来进行解析(称为SimObjectBuilder s,并且每个SimObject子类都有一个);但是这些对象会与他们构建的对象的内部实现非常相关,我希望尽可能简短明了。

附加要求:我希望避免使用"class"属性来实现基于反射的解析(这样就可以通过直接在好的解析器上进行调整来消除迭代可能的解析器的需要);或外部图书馆。这是一项设计练习,供学生学习OO,重点是简洁明了,而不是生产代码。

3 个答案:

答案 0 :(得分:2)

我认为这是您的要求:

1)不想定义任何新的SimObjectBuilders类型。

2)不想创建SimObject对象只是为了解析它们。

3)没有反思。

然后你可以使用lambdas并使用SimObject的每个子类中定义的静态方法包装它来构建Function类型:

    SimObject o = parse( new HashMap<>(), Arrays.asList(
    SimObject1::buildFromAttributes, SimObject2::buildFromAttributes ) );

    public SimObject parse(Map<String,String> attrs, 
    List< Function<Map<String,String>, SimObject> > factories) {
        for (Function<Map<String,String>, SimObject> f : factories) {
            SimObject o = f.apply(attrs);
            if (o != null) return o;
        } 
        throw new IllegalArgumentException("Could not create valid simobject");
     }

以下是您的子类:

public class SimObject1 extends SimObject{

    public static SimObject1 buildFromAttributes(Map<String,String> attrs){
        return new SimObject1();
    }
}

public class SimObject2 extends SimObject{

    public static SimObject2 buildFromAttributes(Map<String,String> attrs){
        return new SimObject2();
    }
}

答案 1 :(得分:1)

由于这是一项设计练习,因此将对象创建与其行为分开将是一种优秀的设计。

您可以创建与模拟对象层次结构平行的Factory对象层次结构,这样您就可以使用类似于现在的代码(用SimObjectFactory替换SimObject数组)。

除了解析代码和行为代码的更严格分离之外,您还可以为同一对象类型实现多个工厂,支持多种输入格式。

答案 2 :(得分:1)

您想要使用的实际方式违背了OOP原则:

public SimObject parse(Map<String,String> attrs, Class<SimObject>[] classes) {
   for (Class<SimObject> c : classes) {
       SimObject o = c.buildFromAttributes(attrs); // illegal, I know
       if (o != null) return o;
   } 
   throw new IllegalArgumentException("Could not create valid simobject");
}

静态方法不是为了提供多态行为而设计的,static方法与声明它们的类强烈耦合。
因此,更难切换到另一个实现,使用依赖注入机制或直接模拟方法。

关于其他选项,我总结一下:

  • 您不想使用实例方法,因为您不想创建“假”对象。

  • 您不希望将责任分开:SimObject构建和SimObject子类。

  • 您也不想使用反射。

每种语言都有其局限性 你似乎知道那些Java语言。

  

这是一项设计练习,供学生学习OO使用   重点是简单和清晰,而不是生产代码。

分隔SimObject构建和SimObject子类的方法很好,并且依赖于OOP原则:

  • 分离责任
  • 工厂模式
  • 对象协作
  • 代码的可维护性:删除或添加几个SimObject-SimObjectBuilder很容易
  • 覆盖

即使您没有指明问题是针对学生的OOP练习,也是我提出的方式。

  

但这些对象将与内部实现非常相关   他们构建的对象,我希望尽可能简短明了。

耦合是单向耦合:从构建器到要创建的对象 它很好,并且似乎期望构建特定类的实例的构建器耦合到此特定类 所以,是的,干净的代码需要编写成本,但也有利于拥有可能更容易更改的强大代码。