`

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
 

 

 

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

相关推荐

    无参数Zig Zag,A-la Clyde Lee 模式 - MetaTrader 5脚本.zip

    无参数Zig Zag,基于"a-la Clyde Lee 模式"。

    Clyde:Clyde 是一款兼容 Arduino 的开源灯,具有很多个性,您可以适应、玩耍并真正称其为您自己的灯

    克莱德Clyde 是一盏个性十足的灯,您可以适应、玩耍并真正称其为您自己的灯。 Clyde 是开源的并且与 Arduino 兼容。 您可以通过在主控制器板上插入个性模块来改变克莱德对环境的React。 您还可以通过使用标准 ...

    clyde-开源

    通过HTTP维护的轻量级代码,文档或文件保留应用程序。 用户可以创建新的库,子类别和上/下加载文件。 易于使用是其最大的优势。

    clyde:对话语言和游戏工具

    克莱德-对话语言克莱德(Clyde)是一种用于编写游戏对话的语言。 它支持通过变量和事件进行分支对话,翻译和与游戏的接口。 它在很大程度上受到启发,但它着重于对话而不是叙述。 您可以与在线编辑器一起玩。 这是一...

    clyde

    获得一个工作流,以查看您站点的输出(本地使用或Jekyll)。 开发 该版本使用版本3.3.1构建,但也应该支持较新的版本。 使用安装依赖项: $ bundle install 通过Bundler运行jekyll命令,以确保您使用的版本正确...

    frc333-2018-clyde:FRC团队333-2018克莱德代码

    【标题】"frc333-2018-clyde:FRC团队333-2018克莱德代码"所代表的是一个与FIRST Robotics Competition ...这个项目为学习和研究FRC机器人编程提供了一个实际的案例,有助于理解在竞赛中如何使用Java进行机器人控制。

    吃豆人(pacman in c#)用 C#编写的

    用C#语言编写吃豆人游戏是一个有趣的编程挑战,可以学习到游戏开发的基础知识,包括图形界面、对象交互和算法设计。 标题中的“吃豆人(Pacman in C#)”指的是使用Microsoft的C#编程语言来实现吃豆人游戏的源代码...

    customs英语海关实用PPT课件.pptx

    首先,"Customs"指的是海关,这是一个负责监管货物、人员和货币进出国家的政府机构。海关官员(Customs officer)负责执行海关法规,检查行李和货物,确保符合进出口规定。 "Declare"意为申报,"Declaration"则是...

    clyde-kun:我的GitHub个人资料的配置文件

    我是一位有抱负的软件开发者和机器人工程师。 编程语言 :globe_with_meridians: 当前使用和学习 有兴趣学习 | | | | | | | --- | --- | --- | --- | --- | --- | --- || 工具 :hammer_and_wrench: 当前使用和学习 有...

    经典吃豆人游戏与源代码.zip

    迷宫中含有许多点点和名为Blinky,Pinky,Inky和Clyde的幽灵。目标是吃掉迷宫中的所有点点。幽灵会在迷宫中四处游走,试图消灭吃豆人。玩家通过箭头键控制游戏角色。四个幽灵会在迷宫中寻找吃豆人,如果任何一个幽灵...

    简单的吃豆人小游戏+源码

    "简单的吃豆人小游戏+源码" 提供了一个简化版的吃豆人游戏,让你有机会重温经典,甚至可以学习和修改游戏源码,进一步理解游戏开发的过程。 吃豆人的基本玩法: 吃豆人游戏的目标是控制一个小黄色的圆形角色——...

    Clyde Project:可持续能源概念、方法、模型和应用-开源

    克莱德项目将开发一个电子上下文模型,用于可持续能源概念的知识建模,以及关于可持续、可再生能源系统的实用方法、模型和应用的知识展示。

    clyde:用于制作网络3D游戏的Java软件包的集合

    克莱德图书馆 克莱德图书馆提供了用于创建面向动作的联网3D游戏的各种功能。 它的软件包包括: config-配置管理和用于配置操作的工具 delta-在Narya流之上添加了对delta编码对象的支持 编辑器-注释控制的反射对象...

    经典吃豆人游戏(C# 源代码)+使用说明+可做毕设

    迷宫中包含各种点和名为Blinky、Pinky、Inky和Clyde的幽灵。目标是吃掉迷宫中的所有点。幽灵会在迷宫中四处跑动并试图杀死吃豆人。游戏的控制方式是使用箭头键。四个幽灵在迷宫中游荡,寻找吃豆人。如果任何一个幽灵...

    The Book of Dads

    - **育儿的挑战与乐趣**:无论是夜间的守护还是日常生活的琐碎,父亲们都在不断学习如何更好地陪伴孩子成长。 - **成长的反思**:许多文章都提到了时间的流逝以及对未来的期待,反映出父亲对于孩子成长过程中所经历...

    软件测试-实验7.docx

    在软件测试领域,冒烟测试是一种初步的系统测试,旨在验证软件的基本功能是否能正常运行,以决定是否可以继续进行更深入的测试。在"软件测试-实验7.docx"中,实验主要围绕Jpacman游戏进行了冒烟测试的实践,探讨了...

    跨平台翻拍PacMan(南梦宫,1980年),带有原始幽.zip

    “PacMan-master”这个名字暗示这是一个开源项目,可能在GitHub等代码托管平台上发布,供开发者学习和改进。开源游戏项目通常包含许可文件、源代码、构建脚本和资源文件,以便其他人可以自由地查看、修改和分发。 *...

    Mrs-Pac-Man:仅使用Javascript,CSS和HTML重新制作经典的《吃豆人》经典游戏

    眨眼和漆黑会收到一个随机的方向,并会继续朝那个方向移动,直到撞到墙,然后他们会收到一个新的方向。 当不易受到攻击时,Pinky和Clyde将计算吃豆人及其潜在动作之间的距离,并选择最短的距离。 如果卡住,Pinky和...

    谷歌吃豆子游戏的

    1. **游戏机制**:Pac-Man是一款迷宫追逐游戏,玩家控制黄色的Pac-Man角色,通过吃掉迷宫中的所有豆子来得分,同时避开鬼魂(Blinky、Pinky、Inky和Clyde)。 2. **游戏规则**:Pac-Man可以吃掉特殊的“能量豆”,...

    吃豆人.rar

    Clyde(橙鬼)在早期版本中较为随机,后期则更接近吃豆人。了解这些鬼魂的行为模式是提高游戏技能的关键。 除了街机版,吃豆人还衍生出了许多版本和变体,包括家用游戏机、掌上游戏机、PC、手机应用以及网络版本。...

Global site tag (gtag.js) - Google Analytics