博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Object System (对象系统)

Posted on 2010-06-29 20:53  Cloud_Chan  阅读(1976)  评论(5编辑  收藏  举报

 

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

  你是不是遇到这样的情况,当要保存对象时,通常会有一个基类,类似:
  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

万恶的格式!!!