API Experiment: Generische Datenmodelle mit getyptem Zugriff

Thomas Darimont

Erfahrenes Mitglied
Hallo,

hier mal ein kleines API Experiment zur Demonstration eines generischen Models mit getyptem Zugriff.

Das Model ist im Prinzip eine Ansammlung von Properties (mit Name, Typ und Wert), wobei die Werte innerhalb einer Map mit dem vollen Property-Namen (scope.Name) als Key und dem entsprechenden Wert abgelegt werden.

Die Model Properties werden hierbei als Enum implements IProperty spezifiziert und per @Property Annotation mit einem Typ versehen um mit möglichst wenig Code den Namen / Scope und Typ einer Property zu definieren.

Java:
package de.tutorials.model;

import static de.tutorials.model.GenericModelExample.BaseProperties.Id;
import static de.tutorials.model.GenericModelExample.BaseProperties.Name;
import static de.tutorials.model.GenericModelExample.BaseProperties.Quantity;
import static de.tutorials.model.GenericModelExample.BaseProperties.SomeObject;

import java.io.Serializable;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class GenericModelExample {

  static interface IProperty {
    String name();
  }

  @Retention(RetentionPolicy.RUNTIME)
  @Target(ElementType.FIELD)
  static @interface Property {
    Class<?> value() default Object.class;
  }

  static enum BaseProperties implements IProperty {
    @Property(String.class) Id, 
    @Property(String.class) Name, 
    @Property(Integer.class) Quantity,
    @Property(Double.class) Turnover,
    @Property SomeObject;
  }
  
  static enum OtherProperties implements IProperty{
    @Property(List.class) Values;
  }


  public static void main(String[] args) {
    System.out.println(Id);
    System.out.println(getPropertyName(Id));
    System.out.println(getPropertyType(Id));

    IModel model = new Model();
    model.set(Id, "1234");
    model.set(Name, "Bubu");
    model.set(Quantity, 4711);

    if (model.has(Quantity)) {
      Integer qty0 = model.get(Quantity);
      System.out.println(qty0);
    }

    Number qty1 = model.get(BaseProperties.Quantity, Number.class);
    System.out.println(qty1);

    if (model.has("BaseProperties.Name")) {
      System.out.println(model.get("BaseProperties.Name"));
    }

    model.set("BaseProperties.Name", "Baba");
    System.out.println(model.get(Name));

    model.set(SomeObject, Arrays.asList(1, 2, 3, 4));
    System.out.println(model.get(SomeObject));
    
    model.set(OtherProperties.Values, Arrays.asList(6,7,8));
    model.get(OtherProperties.Values);
    
    System.out.println(model);
    
    System.out.println("All base properties:");
    for(IProperty property : BaseProperties.values()){
      System.out.println(property + " -> " + model.get(property));
    }
  }

  static interface IModel extends Serializable {
    boolean has(String propertyName);


    boolean has(IProperty property);


    <TTarget> TTarget get(String propertyName);


    <TTarget> TTarget get(String propertyName, Class<TTarget> target);


    <TTarget> TTarget get(IProperty property);


    <TTarget> TTarget get(IProperty property, Class<TTarget> target);


    IModel set(IProperty property, Object value);


    IModel set(String propertyName, Object value);
  }

  static class Model implements IModel {
    Map<String, Object> properties = new TreeMap<String, Object>();


    public <TTarget> TTarget get(IProperty property) {
      return get(property, null);
    }


    public <TTarget> TTarget get(IProperty property, Class<TTarget> target) {
      String propertyName = getPropertyName(property);
      return get(propertyName, target);
    }


    public <TTarget> TTarget get(String propertyName) {
      return get(propertyName, null);
    }


    public <TTarget> TTarget get(String propertyName, Class<TTarget> target) {
      Object value = properties.get(propertyName);
      if (target != null && value != null) {
        checkArgument(target.isAssignableFrom(value.getClass()),
          "Value of Property %s (%s) cannot be converted to: (%s)", propertyName, value.getClass(), target);
      }
      return (TTarget) value;
    }


    public IModel set(IProperty property, Object value) {
      String propertyName = getPropertyName(property);
      Class<?> propertyType = getPropertyType(property);
      if (propertyType != null && value != null) {
        checkArgument(propertyType.isAssignableFrom(value.getClass()),
          "Property %s (%s) is not compatible with: %s (%s)", propertyName, propertyType, value, value.getClass());
      }
      return set(propertyName, value);
    }


    public IModel set(String propertyName, Object value) {
      properties.put(propertyName, value);
      return this;
    }


    @Override
    public boolean has(IProperty property) {
      String propertyName = getPropertyName(property);
      return has(propertyName);
    }


    @Override
    public boolean has(String propertyName) {
      return this.properties.containsKey(propertyName);
    }


    public String toString() {
      return properties.toString();
    }
  }


  static String getPropertyName(IProperty property) {
    String name = property.getClass().getSimpleName() + "." + property.name();
    return name;
  }


  static Class<?> getPropertyType(IProperty property) {
    Class<?> type = null;
    try {
      Field field = property.getClass().getField(property.name());
      Property propertyAnnotation = field.getAnnotation(Property.class);
      if (propertyAnnotation != null) {
        type = propertyAnnotation.value();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return type;
  }


  static void checkArgument(boolean invariant, String message, Object... params) {
    if (!invariant) {
      throw new IllegalArgumentException(String.format(message, params));
    }
  }
}

Ausgabe:
Code:
Id
BaseProperties.Id
class java.lang.String
4711
4711
Bubu
Baba
[1, 2, 3, 4]
{BaseProperties.Id=1234, BaseProperties.Name=Baba, BaseProperties.Quantity=4711, BaseProperties.SomeObject=[1, 2, 3, 4], OtherProperties.Values=[6, 7, 8]}
All base properties
Id -> 1234
Name -> Baba
Quantity -> 4711
Turnover -> null
SomeObject -> [1, 2, 3, 4]

Gruß Tom
 
Zurück