读书人

Clyde学习札记一(Scope)

发布时间: 2012-12-27 10:17:09 作者: rapoo

Clyde学习笔记一(Scope)

Scope相关的接口、类都定义在expr这个包中,官方的说明很简单,只有一句话:expr?- expression evaluation and symbol binding,但scope这个概念却很重要,可以说是整个clyde框架的核心基石之一。那么它到底是个什么概念,又起到了什么作用呢?首先它是一个接口,在这个接口中,最重要的就是下面这个方法:

?

public <T> T get (String name, Class<T> clazz);
?

?

再看它的注释:

?

Looks up a symbol in this scope,return the mapping for the requested symbol。

?

在这里,symbol指的就是参数name,这个方法的作用就是找到name这个symbol所对应的值,并且这个值的类型是T。

?

接下来我们再来看下这个方法的实现。scope有两个默认的实现类,分别是SimpleScope和DynamicScope。

?

在SimpleScope中的实现:

?

?

    public <T> T get (String name, Class<T> clazz)    {        return ScopeUtil.get(this, name, clazz);    }

?那么具体的逻辑是写在了ScopeUtil中,在ScopeUtil.get方法中,有关键的这句:

?

?

Member member = getScoped(object.getClass()).get(name);
?

这句话的意思是在object这个类中所有用scoped annotation标注过的属性或方法中找到与name对应的那个。

相关代码如下:

?

?

        HashMap<String, Member> members = new HashMap<String, Member>();        Class<?> sclazz = clazz.getSuperclass();        if (sclazz != null) {            members.putAll(getScoped(sclazz));        }        // add all scoped fields (stripping off the leading underscore, if present)        for (Field field : clazz.getDeclaredFields()) {            if (field.isAnnotationPresent(Scoped.class)) {                field.setAccessible(true);                members.put(stripUnderscore(field.getName()), field);            }        }        // add all scoped methods        for (Method method : clazz.getDeclaredMethods()) {            if (method.isAnnotationPresent(Scoped.class)) {                method.setAccessible(true);                members.put(method.getName(), method);            }        }

?可以看到每个类都有一个对应的map,这个map中保存了所用用scoped标注过的属性和方法,并且以属性和方法名作为map的key,这个key就是我们前面所说的symbol,即那个name参数。在这里的整个方法还递归的包括了这个类所有的超类,但是要主要到这里的scoped是个annotation,和我们前面提到的scope接口不是同一回事。每个类对应的map还保存在一个全局的map中作为cache,这样就不用每次都反射查找一遍。

?

?

    /** Cached scoped members. */    protected static HashMap<Class<?>, HashMap<String, Member>> _scoped = Maps.newHashMap();
?

找到对应的属性或方法之后,ScopeUtil的get方法就返回属性的值或者执行方法的结果,在这里相关代码就不展开了。现在回过头来重新审视一下scopeUtil的get方法,那就是:

?

找到在object这个类中所有用scoped annotation标注过的属性或方法中查找属性或方法名为name的那个,并返回属性的值或方法执行的结果,返回类型为T。在simpleScope中,这个object就是它自身。

?

再来看一下dynamicScope的实现:

?

?

    public <T> T get (String name, Class<T> clazz)    {        // first try the dynamic symbols, then the reflective ones        Object value = (_symbols == null) ? null : _symbols.get(name);        return clazz.isInstance(value) ? clazz.cast(value) : ScopeUtil.get(_owner, name, clazz);    }
?

可以看到,在dynamicScope中,这个object不再是它自己,而是_owner。看一下owner的定义:

?

?

    /** The owner of this scope. */    protected Object _owner;

?这也是SimpleScope和DynamicScope最重要的一个区别。

?

现在我们已经初步了解了scope的概念,那么这个scope有什么具体的作用呢?

在scope接口中还有两个方法,addListener,removeListener。

?

?

    /**     * Adds a listener for changes in scope.  The listener will be notified when symbols are     * added or removed and whenever the scope hierarchy changes.     */    public void addListener (ScopeUpdateListener listener);    /**     * Removes a listener for changes in scope.     */    public void removeListener (ScopeUpdateListener listener);
?

?

再来看一下ScopeUpdateListener这个接口,这个接口SimpleScope和DynamicScope都默认实现了,也就是说SimpleScope和DynamicScope都可以作为监听器监听其他的scope,一旦它监听的scope被改变,scopeUpdated方法都会被调用。

?

?

/** * Used to notify objects when the scope has been updated. */public interface ScopeUpdateListener{    /**     * Called when the scope has been updated.     */    public void scopeUpdated (ScopeEvent event);}

?

下面是SimpleScope的scopeUpdate方法的实现。

?

?

    public void scopeUpdated (ScopeEvent event)    {        ScopeUtil.updateBound(this, _parentScope);    }

再看一下ScopeUtil的updateBound方法:

?

?

    /**     * Updates the {@link Bound} fields of the specified object using the provided scope.     */    public static void updateBound (Object object, Scope scope)    {        for (Field field : getBound(object.getClass())) {            String name = field.getAnnotation(Bound.class).value();            if (name.length() == 0) {                name = stripUnderscore(field.getName());            }            @SuppressWarnings("unchecked") Class<Object> type = (Class<Object>)field.getType();            try {                field.set(object, resolve(scope, name, field.get(object), type));            } catch (IllegalAccessException e) {                log.warning("Error accessing bound field.", "field", field, e);            }        }    }

?这里也是用的反射找到object类中用Bound annotation标注的属性,并且找到这个属性所bound(绑定)的symbol,resolve出这个symbol的值并赋值给这个属性。

?

再看一下ScopeUtil中resolve方法的实现。

?

?

    /**     * Attempts to resolve the identified symbol in the given scope.  If not found there,     * searches the parent of that scope, and so on.     *     * @return the mapping for the symbol, or <code>defvalue</code> if not found anywhere in the     * chain.     */    public static <T> T resolve (Scope scope, String name, T defvalue, Class<T> clazz)    {        // if the name includes a scope qualifier, look for that scope        int idx = name.indexOf(':');        if (idx != -1) {            String qualifier = name.substring(0, idx);            name = name.substring(idx + 1);            while (scope != null && !qualifier.equals(scope.getScopeName())) {                scope = scope.getParentScope();            }        }        // rise up through the scopes looking for the requested symbol        for (; scope != null; scope = scope.getParentScope()) {            T value = scope.get(name, clazz);            if (value != null) {                return value;            }        }        // no luck; return the default value        return defvalue;    }
?

这个方法就是在给定的scope中找到symbol所对应的值,在当前scope中找不到可以递归的在当前scope的parentScope中继续寻找,并且symbol还可以用“:”分隔符指定一个特定的scope。在上面的代码中我们看到了熟悉一句:

?

?

 T value = scope.get(name, clazz);
?

怎么样,到这里整个拼图中的两块已经连在了一起,并且已经隐约看到了整个scope框架它所需要表达的一个意思,那就是:

?

当一个scope有update时,所有监听该scope的其他scope所拥有的object中,任何绑定到该scope中symbol的属性值都会随着该symbol的值变化而变化。这也正是官方介绍中的symbol binding的概念所在。应该说这个表述非常的抽象,但它却是整个clyde框架的基石,框架中的其他部分有非常多的依赖于这个抽象的概念。回过头来我们再一次看到,在SimpleScope中,object还是它自身,而DynamicScope的object仍然还是它的owner。

?

?

    public void scopeUpdated (ScopeEvent event)    {        ScopeUtil.updateBound(_owner, _parentScope);        wasUpdated();    }
?

每个scope默认的监听对象都是该scope的parentScope。SimpleScope中的构造函数:

?

?

    /**     * Creates a new simple scope.     */    public SimpleScope (Scope parentScope)    {        if ((_parentScope = parentScope) != null) {            _parentScope.addListener(this);        }        ......    }

?

DynamicScope也是如此。

?

下面写一段测试代码来验证一下。

?

?

package com.meidusa.clyde.test;import com.threerings.expr.Bound;import com.threerings.expr.DynamicScope;import com.threerings.expr.Scope;import com.threerings.expr.Scoped;import com.threerings.expr.SimpleScope;public class TestScope {public static class TestScoped extends DynamicScope{public TestScoped(Scope parentScope) {super("test name", parentScope);}@Scopedprotected String scopedString = "before test";public String getScopedString() {return scopedString;}public void setScopedString(String scopedString) {this.scopedString = scopedString;}}public static class TestBounded extends SimpleScope{public TestBounded(Scope parentScope) {super(parentScope);}@Bound("scopedString")protected String boundedString;public String getBoundedString() {return boundedString;}public void setBoundedString(String boundedString) {this.boundedString = boundedString;}}public static void main(String[] args){TestScoped parent = new TestScoped(null);TestBounded child = new TestBounded(parent);System.out.println(child.getBoundedString());parent.setScopedString("after test");parent.wasUpdated();System.out.println(child.getBoundedString());}}

?

运行后输出为:

?

?

before testafter test
?

?

?

1 楼 feixf1974 2011-08-03 请问Clyde是不是一个做地图和游戏素材的工具? 2 楼 kevindude 2011-08-07 Clyde是一个基于OpenGL的3D游戏引擎

读书人网 >编程

热点推荐