如何确定 ColdFusion 对象所在的上下文?

11

所以,假设我有一个组件实例:

foo.cfc


(提示:此文本涉及 IT 技术)
<cfcomponent>
  <cffunction name="locateMe">
    <cfreturn "I don't know where I live!">
  </cffunction>
</cfcomponent>

还有另一个组件,fooParent.cfc:

<cfcomponent>
  <cfset this.foo = createObject("component", "foo")>
</cfcomponent>

假设我以几种不同的方式创建了“foo”的实例:

<cfset myStruct = {}>
<cfset myStruct.foo = createObject("component", "foo")>

<cfset myFoo = createObject("component", "foo")>

<cfset myFooParent = createObject("component", "fooParent")>

<cfoutput>
#myStruct.foo.locateMe()#<br>
#myFoo.locateMe()#<br>
#myFooParent.foo.locateMe()#<br>
</cfoutput>

正如预期的那样,这将输出:
I don't know where I live!
I don't know where I live!
I don't know where I live!

我想知道的是,在foo.cfc中可能做些什么来告诉我关于它被调用的上下文信息(无论何种信息!)。由于所有东西最终都存在于某种作用域中,而且所有作用域都是一种对象,我的意思是我真的很想知道如何在给定的实例化对象内部确定包含对象。最终,构建foo.cfc的一些方式可以使输出类似于我上面的示例片段:
I live within a "class coldfusion.runtime.Struct" instance!
I live within a "class coldfusion.runtime.VariableScope" instance!
I live within a "component cfjunk.fooParent" instance!

每个值可能是通过检查传递给getMetaData实际包含对象引用的结果来确定的。

更新 如评论中Micah所建议的,我已经添加了“Java”标签,因为我怀疑他可能是正确的,解决方案可能在使用Java进行内省方面。

更新

不要把这看成一个纯学术讨论,让我解释一下为什么我需要这个。

我正在使用CFWheels ORM和包含来获取对我的数据的引用,如下所示:

var user = model("User").findOne(where="id=123", include="AuthSource", returnAs="object");

这将返回一个对象,我可以像这样引用它:
user.id // property of the "User" model
user.reset() // method on the "User" model
user.AuthSource.id // property of the "AuthSource" model
user.AuthSource.authenticate(password) // method on the "AuthSource" model

现在,在我的“AuthSource.authenticate”方法中,我想了解包含我的“User”对象。否则,我将不得不像这样调用函数:
user.AuthSource.authenticate(user, password) // note the redundancy in the use of "user"

我应该能够依赖于通过用户对象调用AuthSource模型上的方法并从该方法内部实际读取该对象的事实。


好问题。我想知道JAVA是否有一些内省功能来确定这种情况。也许您可以通过添加Java标签来扩大您的问题。 - Micah
为什么对这个问题进行随机的负评?好奇怪啊... - Jake Feasel
3个回答

4

我已经很久没有写ColdFusion了,所以请原谅我的伪代码。在这种情况下,通常做法是在实例化子类时,父类向子类发送自己的副本。这在许多面向对象设计模式中都有使用,其中两个对象需要双向通信,而不仅仅是父对象调用子对象的方法。

因此,您的子类应该定义为以下内容:

<cfcomponent>
  <cffunction name="init">
     <cfargument name="parentParam" required="yes" type="object">
     <cfset this.parent = parentParam >
      <cfreturn this> 
   </cffuncton>
  <cffunction name="locateMe">
    <cfreturn "I belong to #this.parent.className# !">
  </cffunction>
 <cffunction name="doOtherStuff">
    <cfreturn "I do stuff with my parent: #this.parent.className# !">
  </cffunction>
</cfcomponent>

然后当你使用它时...

<cfset myParent.child = createObject("component", "Object").init(myParent) />
#myparent.locateMe()#
#myparent.doOtherStuff()#

"init"方法是构造函数中必需的参数,parentParam,以便子类始终有对其父级的引用。然后,您的所有方法都可以使用this.parent来处理它。在我的代码示例中,我使用#this.parent.className#,但不知道ColdFusion对象是否具有此属性。可能您可以使用反射或元编程等技术来实现相同的功能。

请注意:据我了解,ColdFusion不支持内置构造函数,因此我向您展示的是该网站的社区标准最佳实践:

http://www.iknowkungfoo.com/blog/index.cfm/2007/8/22/Object-Oriented-Coldfusion--1--Intro-to-Objectcfc

抱歉,顺便说一下,你正在使用ColdFusion...


是的,手动跟踪包含对象的引用是我的第一个想法。然而,我希望还有其他解决方案。我会等一下看看是否有人提出不同的方法。谢谢,CF 真的不错! - Jake Feasel
这种方法的一个不幸结果是,您对象的cfdump变成了无限循环。 :( - Jake Feasel
啊,这很有道理,因为每个对象都有一个指向另一个对象的引用。 - fregas
所以,如果你想避免在子类中引用父类,我认为你必须使用Coldfusion的反射版本,并查看是否有一种方法可以找到所有对子对象的引用。即使是在我更熟悉的语言中,我也不确定该如何做到这一点。我知道Coldfusion并不那么糟糕,我也是从它开始的。比PHP好多了! :) - fregas
我认为这里的解决方案可能是我会考虑的方式。我认为主要问题在于,在组件内部,您不应该访问组件外部的数据,这排除了直接访问变量的可能性。据我所知,唯一的方法是按照此处详细说明的方式传递父作用域。顺便问一下,您为什么需要这样做? - Simon at My School Portal
@Simonatmso.net 我已经更新了我的问题,并用更真实的例子解释了我为什么需要这个。 - Jake Feasel

0

我已删除所有以前的内容,因为它似乎没有帮助。根据您最近的评论,以下是一个建议,我认为可能更符合您的目标。

// Object
<cfcomponent displayname="Object">
    <cffunction name="init">
        <cfargument name="username" type="string">
        <cfscript>
            variables.username = arguments.username;
            variables.authSource = CreateObject('component','AuthSource').init();
        </cfscript>
        <cfreturn this>
    </cffunction>
    <cffunction name="authenticate">
        <cfargument name="password" type="string">
        <cfreturn variables.authSource.authenticate(variables.username,arguments.password)>
    </cffunction>
 </cfcomponent>

<cfcomponent displayname="AuthSource">
    <cffunction name="init">
        <cfreturn this>
    </cffunction>
    <cffunction name="authenticate">
        <cfargument name="username" type="string">
        <cfargument name="password" type="string">

            .... DO STUFF ...

        <cfreturn ...>
    </cffunction>
 </cfcomponent>

 <cfscript>
    objUser = CreateObject('component','Object').init('SomeUserName');
    // Authenticate
    objUser.authenticate('SomePassword');
 </cfscript>

这样,AuthSource就不需要知道父对象的信息,但是同时进行身份验证的人也不需要再次传递用户名。对象(父对象)有一个包装方法用于身份验证,其中添加了用户名。

这对您是否有进一步帮助?


我很感谢您的回复,但基本上您的答案是“尝试做你所要求的事情不是一个好主意”。您可能是正确的,但这并不是问题的答案。就像我如何改变我的真实例子-我知道我可以改变它使其工作,但这并不是问题的关键。也许这仍然是一个有点学术性的问题。 - Jake Feasel
我可以看到你所追求的用途 - 我目前正在进行的项目中也可以利用相同的方法。我解决这个问题的方式是将父对象传递给子对象,但正如之前提到的那样,这会产生循环引用,在序列化时会出现问题,并且需要定义和取消定义父对象引用的方法。我可以更新上面的示例来说明,尽管它最终会与@fregas发布的示例非常相似。 - Simon at My School Portal
我真的不担心我需要做什么来解决我最后一个例子中的问题 - 那很容易。这更是一个技术问题,关于我是否应该解决它,或者是否有某种方法可以从对象内部找到给定对象的上下文。最好将重点放在第一个例子上(foo.locateMe()等...)。 - Jake Feasel
@Jake - 从技术角度来看,这是一个有趣的问题。但我认为即使在Java中也不可能轻松地推导出父对象。(编辑:) 据我所知,没有任何固有的东西可以识别对象的父级。除非你注入一个,否则没有直接的链接或引用,正如其他人所提到的那样。例如,在javax.swing中的树节点中,父关系是明确管理的。如果没有直接引用,您将不得不从上往下查找父容器。即通过上下文迭代所有变量,在所有作用域中进行操作。(续) - Leigh
然后敲门,询问“我的引用住在这里吗?”显然,这比将对象作为参数传递进去的方式更加荒谬。是的,后者会感觉有些冗余。但是试图“猜测”父级感觉就像是错误的方法(如果可能的话)。 - Leigh

0

实际上,调用堆栈与我所询问的不同。在我的例子中,尽管所有对foo.locateMe()的三次调用都包含在不同的对象中,但它们都源自调用堆栈中相同的位置。这是因为调用堆栈是指执行路径(函数调用链),而不是内存中对象的组织形式。 - Jake Feasel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接