天天看点

Object System (对象系统) - Cloud_Chan

  为什么我们需要对象系统?

  你是不是遇到这样的情况,当要保存对象时,通常会有一个基类,类似:

  interface IAchive

  {

   void save(OutputStream out);

   void load(InputStream in);

  }

  然后为每一个需要保存的类写重载save跟load方法,如果当你系统比较庞大时,你就需要为每个类重载一次。

  还有我们在做GUI程序的时候,常常要把对象的属性导出到GUI界面查看或者编辑,这时你在初始化你的Property

  界面的时候是不是会为每一个要导出的类写exportToGUIComponent(Component comp)这样的方法呢;

  类似的还有很多,比如要导出接口给脚本等等。

  如果我们用上面的方法,那会让这些工作变成枯燥的体力活,那怎么样才能简化这些操作呢?

  这就是Object System存在的理由

  假如我们的对象有这种能力:

  registProperty(String propertyName, Class<?> type, String getterName, String setterName, int usage)

  把对象的属性注册到某个地方,然后要用的时候通过接口把这些要用的对象一次性拿出来,这样我们就需要把属性抽象化

  public interface IMetaProperty {

   int USAGE_SAVE_TO_DB = 1 << 1;

   int USAGE_GUI_BROSWER = 1 << 2;

   int USAGE_SCRIPT_EXPORT = 1 << 3;

   int USAGE_DEFAULT = USAGE_SAVE_TO_DB | USAGE_GUI_BROSWER | USAGE_SCRIPT_EXPORT;

   void init(String name, Object owner, Method getter, Method setter, int usage);

   void fromStream(PropertyInStream stream) throws Exception;

   void toStream(PropertyOutStream stream) throws Exception;

   String getName();

   String getDesc();

   void setDesc(String desc);

   Object getValue();

   void setValue(Object value);

   boolean isWriteable();

   boolean isReadable();

   int getUsage();

   }

   稍微解释一下,我们通过反射拿到属性的getter和setter以便我们操作属性,usage是指属性的用途,

   比如说:USAGE_SCRIPT_EXPORT说明这个属性是导出到脚本用的。

   那我们的属性被注册到什么地方去了呢?我们每个对象都会带有一个叫MetaData的类,该类封装在我们的

   顶级对象MetaObject中,该类存放所有注册进去的属性。也许你觉得一个对象一个MetaData太浪费空间了

   ,static不更好吗?当然,确实很好,这里只是讨论思想,而且由于这种方法在Java中不大好实现(其实也没多想^_^)

   , 在C++的话用个模板就搞定了(像很多c++软件系统自带的RTTI一样),这里只作简单处理

  public class MetaData {

   private Map<String, MetaProperty> propertyList = new HashMap<String, MetaProperty>();

   //...

   public void registerProperty(String name, Class<?> type, Object owner, String getterName, String setterName, int usage)

   public List<MetaProperty> getProperties(int usage)

   public MetaProperty getProperty(String name)

  }

  这样我们就通过getProperties方法把所有属性拿出来,该干什么就干什么,比如要保存对象时只要调用toStream

  方法就可以了,所有属性都一样处理。这是怎么做到的呢,如果自定义的类属性怎么办,PropertyInStream、PropertyOutStream 又是什么?

  其实关键是我们在调用registerProperty的时候为每一种Class<?> type生成特定的IMetaProperty,

  我们简单看下registerProperty的实现方法

  public void registerProperty(String name, Class<?> type, Object owner, String getterName,

   String setterName, int usage) throws SecurityException, NoSuchMethodException {

    Method _getter = null != getterName ? owner.getClass().getMethod(getterName) : null;

    Method _setter = null != setterName ? owner.getClass().getMethod(setterName, type) : null;

    IMetaProperty property = MetaDataManager.getInstance().createProperty(type, name, owner, _getter, _setter, usage);

    propertyList.put(name, property);

  }

  前面两行是初始化函数指针,创建实例的是MetaDataManager,MetaDataManager的存在就是帮助我们创建各种各样的IMetaProperty

  ,而且这些IMetaProperty都是可以定制的,这样我们的IMetaProperty就可以交给用户来扩展

  public class MetaDataManager {

   private Map<Class<?>, PropertyCreator> factory = new HashMap<Class<?>, PropertyCreator>();

   private static MetaDataManager instance = new MetaDataManager();

   private MetaDataManager() {

      // int

      registPropertyFactory(int.class, new PropertyCreator() {

       @Override

       public MetaProperty create() {

          return new IntProperty();

       }

      });

      // String

      registPropertyFactory(String.class, new PropertyCreator() {

       @Override

       public MetaProperty create() {

          return new StringProperty();

         }

      });

      //...

    }

   public static MetaDataManager getInstance() {

      return instance;

   }

   public void registPropertyFactory(Class<?> _class, PropertyCreator creator) {

      factory.put(_class, creator);

   }

   public MetaProperty createProperty(Class<?> _class, String name, Object owner, Method getter, Method setter, int usage) {

      if (null == _class || null == name)

         return null;

      PropertyCreator creator = factory.get(_class);

      if (null == creator) {

         throw new RuntimeException("Unsupport property type = " + _class.getName());

      }

      MetaProperty property = creator.create();

      if (null != property) {

         property.init(name, owner, getter, setter, usage);

      }

      return property;

   }

  }

  系统在初始化的时候我们放入了最基本的boolean、int、String等Property,如果我们要定制属性

  ,可以通过registPropertyFactory(Class<?> _class, PropertyCreator creator)把类型跟对象关联起来

  ,就像我们在构造函数里做的那样。

  我们简单看下boolean属性是怎么实现的

  public class BooleanProperty extends CommonProperty {

   @Override

   public void fromStream(PropertyInStream stream) throws Exception {

      setValue(stream.readBoolean().getValue());

   }

   @Override

   public void toStream(PropertyOutStream stream) throws Exception{

      stream.writeBoolean(getName(), (Boolean) getValue());

   }

  }

  再来介绍一下PropertyInStream,该接口是我们序列化属性时用的,你可以扩展PropertyInStream为

  BytesArrayInStream、XmlInStream或者时JsonInStream等等各种各样的存储格式

  我们看看使用该系统的例子

  class MyBaseObject extends MetaObject {

   private int z;

   boolean flag;

   long time;

   short shortNum;

   float pi;

   public MyBaseObject() {

    try {

       registerProperty("z", int.class, "getZ", "setZ", MetaProperty.USAGE_DEFAULT);

       registerProperty("flag", boolean.class, "isFlag", "setFlag", MetaProperty.USAGE_DEFAULT);

       registerProperty("time", long.class, "getTime", "setTime", MetaProperty.USAGE_DEFAULT);

       registerProperty("shortNum", short.class, "getShortNum", "setShortNum", MetaProperty.USAGE_DEFAULT);

       registerProperty("pi", float.class, "getPi", "setPi", MetaProperty.USAGE_DEFAULT);

    } catch (Exception e) {

       e.printStackTrace();

    }

   }

  //...getters && setters

 }

public class MyObject extends MyBaseObject {

   private int x;

   private int y;

   private String name;

   private byte color;

   private int[] ary;

   public MyObject() {

    try {

       registerProperty("x", int.class, "getX", "setX", MetaProperty.USAGE_SAVE_TO_DB);

       registerProperty("y", int.class, "getY", "setY", MetaProperty.USAGE_DEFAULT);

       registerProperty("name", String.class, "getName", "setName", MetaProperty.USAGE_DEFAULT);

       registerProperty("color", byte.class, "getColor", "setColor", MetaProperty.USAGE_DEFAULT);

       registerProperty("ary", int[].class, "getAry", "setAry", MetaProperty.USAGE_DEFAULT);

    } catch (Exception e) {

       e.printStackTrace();

    }

   }

   //...getters && setters

}

public class Test {

   public static void main(String[] args) {

      MyObject obj = new MyObject();

      obj.setX(10000);

      obj.setY(10000);

      obj.setZ(10000);

      obj.setName("Hello");

      obj.setFlag(true);

      obj.setPi(3.1415f);

      obj.setShortNum((short) 277);

      obj.setTime(99999999999L);

      obj.setColor((byte) 127);

      obj.setAry(new int[]{1,2,3,4,5});

      try {

         obj.getMetaData().save(new XmlOutStream(obj.getMetaData().getName(), "c:\\test.xml"));

      } catch (Exception e) {

         e.printStackTrace();

      }

   }

}

最后我们的MyObject对象被写入一个像下面那样的XML文件中

<?xml version="1.0" encoding="gb2312"?>

<MyObject>

  <property name="time" value="99999999999"/>

  <property name="flag" value="true"/>

  <property name="color" value="127"/>

  <property name="shortNum" value="277"/>

  <property name="name" value="Hello"/>

  <property name="ary length" value="5"/>

  <property name="ary[0]" value="1"/>

  <property name="ary[1]" value="2"/>

  <property name="ary[2]" value="3"/>

  <property name="ary[3]" value="4"/>

  <property name="ary[4]" value="5"/>

  <property name="pi" value="3.1415"/>

  <property name="z" value="10000"/>

  <property name="y" value="10000"/>

  <property name="x" value="10000"/>

</MyObject>

这样的系统对于数据驱动的系统是非常合适的,而且我觉得在C++中实现会更加优雅一些^_^。

当然其中还会有很多问题,比如效率等,不过写了这么多实在有点累^_^,下次再说吧。。。

思路很乱,写得更乱,见谅!O(∩_∩)O

万恶的格式!!!

Object System (对象系统) - Cloud_Chan