天天看點

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