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);
		}
		
		@Scoped
		protected 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 test
after test
 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章