2

CFWheelsを使用して、ColdFusion でアプリケーションを開発しています。

というモデルがありVote.cfcます。Vote オブジェクトを作成、更新、または削除する前に、別のモデルから投稿オブジェクトを取得する必要があります: Post.cfc. 投票は投稿に属します。投稿には多くの票があります。

オブジェクトからのデータを使用して、複数の基準と複数の機能にまたがるオブジェクトpostが必要です。to validate the votepost オブジェクトを永続化してそれらの関数で使用できるようにする唯一の方法は、それを request スコープに格納することです。

他の人は、これは悪い習慣だと言っています。しかし、私はその理由を見つけることができませんでした。リクエスト スコープはスレッド セーフであり、この状況で使用するのは理にかなっていると思いました。

もう 1 つの方法は、それを必要とするすべての関数で post オブジェクトの新しいインスタンスをロードすることです。Wheels はキャッシングを使用しますが、これを行うとリクエスト時間が 250% 急増しました。

アップデート

ここにいくつかのサンプルがあります。最初に、コントローラーは投票オブジェクトが既に存在するかどうかを確認する処理を行います。存在する場合は削除し、存在しない場合は作成します。コントローラ機能は基本的にトグル機能です。

Votes.cfc コントローラ

   private void function toggleVote(required numeric postId, required numeric userId)
    {
    // First, we look for any existing vote
    like = model("voteLike").findOneByPostIdAndUserId(values="#arguments.postId#,#arguments.userId#");

    // If no vote exists we create one
    if (! IsObject(like))
    {
        like = model("voteLike").new(userId=arguments.userId, postId=arguments.postId);
    }
    else
    {
        like.delete()
    }           
}

モデル VoteLike.cfc

その後、検証前にモデルに登録されたコールバックが発火します。投票が属する投稿オブジェクトを取得する関数を呼び出します。関数 getPost() は、投稿をリクエスト スコープに保存します。モデル内の一連の検証関数で利用できるようになりました。

// Load post before Validation via callback in the Constructor
beforeValidation("getPost");

private function getPost()
{
    // this.postId will reference the post that the vote belongs to
    request.post = model("post").findByKey(this.postId);
}

// Validation function example
private void function validatesIsNotOwnVote()
{
    if (this.userId == request.post.userId)
    {
       // addError(message="You can't like your own post.");
    }
}

getPost() 関数に代わる方法は、スコープ呼び出し " this.post().userId" を使用して投稿オブジェクトを取得することです。たとえば、次のようになります。

private void function validatesIsNotOwnVote()
{
    if (this.userId == this.post().userId)
    {
        addError(message="can't vote on your own post")
    }
}

しかし、その後、すべての関数に対してこのスコープ指定された呼び出しを繰り返す必要がthis.post().userIdあります。これが、リクエストの速度を低下させていると思います!

4

2 に答える 2

2

OPのコメントスレッドに基づいて新しい回答で更新

基本 Vote オブジェクト フォーム VoteLike.cfc を拡張しているため、CFC はローカル スレッド セーフ変数スコープを共有します (ただし、これをアプリケーションまたはサーバー スコープに保存して、他のユーザーが操作できるようにしない限り)。これは、Variables.Post などの値を一度設定し、CFC のスタック内の任意の関数でそれを参照することを意味します。

したがって、 getPost() 関数を次のように変更します。

beforeValidation("getPost");

private function getPost(){
    Variables.Post = model("post").findByKey(this.postID);
}

これで、VoteLike.cfc のいずれかの関数内で、Variables.Post を参照できます。これは、ローカル CFC 変数スコープに Post の単一インスタンスがあり、引数や他のスコープを介してそれを渡す必要がないことを意味します。

以下の元の回答

これを処理する「最も正しい」方法は、オブジェクトを個々の関数に引数として渡すことです。または、Vote オブジェクトのプロパティとして Post を追加して、Vote オブジェクトが完全な Post オブジェクトにアクセスできるようにします。

<cffunction name="doTheValidation">
    <cfargument name="ThePost" type="Post" required="true" />
    <cfargument name="TheVote" type="Vote" required="true" />
    <!--- do stuff here --->
</cffunction>

これが悪い習慣である理由は、オブジェクトが作業を行うために、存在する場合と存在しない場合がある外部スコープにアクセスする必要があるためです。1 つのページで複数の投票または投稿を処理することにした場合は、外部スコープをいじって正しく機能させる必要があります。

オブジェクトをあちこちに渡すか、コンポジションを使用して、オブジェクトが互いに認識できるような方法でオブジェクトをつなぎ合わせる方がはるかに優れています。

アップデート

パフォーマンスの問題に関しては、コンポジションを使用してオブジェクトを結合すると、メモリ内のオブジェクトが 2 つだけになるため、不要な Post オブジェクトをインスタンス化する必要がなくなります。また、CFC を引数として関数に渡すと、参照によって CFC が渡されます。これは、メモリ内に CFC の複製を作成しないことを意味し、パフォーマンスの問題にも対処する必要があります。

コード サンプルの更新

上記の「参照による」コメントを説明するために、次のファイルを設定し、それらを単独のディレクトリに配置します。

TestObject.cfc :

<cfcomponent output="false">
    <cfproperty name="FirstName" displayname="First Name" type="string" />
    <cfproperty name="LastName" displayname="Last Name" type="string" />

</cfcomponent>

index.cfm :

<!--- Get an instance of the Object --->
<cfset MyObject = CreateObject("component", "TestObject") />

<!--- Set the initial object properties --->
<cfset MyObject.FirstName = "Dan" />
<cfset MyObject.LastName = "Short" />

<!--- Dump out the object properties before we run the function --->
<cfdump var="#MyObject#" label="Object before passing to function" />

<!--- Run a function, sending the object in as an argument, and change the object properties --->
<cfset ChangeName(MyObject) />

<!--- Dump out the object properites again, after we ran the function --->
<cfdump var="#MyObject#" label="Object after passing to function" />

<!--- Take a TestObject, and set the first name to Daniel --->
<cffunction name="ChangeName">
    <cfargument name="TheObject" type="TestObject" required="true" hint="" />
    <cfset Arguments.TheObject.FirstName = "Daniel" />
</cffunction>

index.cfm を実行すると、最初のダンプの FirstName は Dan、2 番目のダンプの FirstName は Daniel であることがわかります。これは、CFC が参照によって関数に渡されるためです。つまり、その関数内で行われた変更は、メモリ内の元のオブジェクトに適用されます。したがって、オブジェクトの再作成は行われないため、パフォーマンスは低下しません。

ダン

于 2011-02-02T18:53:36.490 に答える
1

requestページ要求に限定されたグローバル スコープ (つまり、スレッド セーフ) であり、そのページ要求の間存続します。したがって、グローバル変数が悪い習慣であるのと同じように、それは悪い習慣ですが、存続期間は短いです。あなたが説明した状況でデータを空中またはフェンスの上に投げ出すと思います。他のコードは空中から引き抜くことができます。

したがって、あなたの状況ではおそらく問題ありません。おそらくrequest、最初にデータがスコープに配置されている場所について、消費の時点で有用な参照を追加してください。つまり、毎回同じソースメソッドに戻る場合は、そのオブジェクトを作成する責任がある関数の内部にキャッシュすることを検討してください (例getVote()) そして、そのためにリクエストスコープを使用できます:

<cfparam name="request.voteCache" default="#structNew()#"/>
<cffunction name="getVote" output="false" access="public" returntype="any">
    <cfargument name="voteId" type="string" required="true"/>
    <cfif structKeyExists(request.voteCache, arguments.voteId)>
        <cfreturn request.voteCache[arguments.voteId]>
    </cfif>

    <!--- otherwise get the vote object --->
    <cfset request.voteCache[arguments.voteId] = vote>
    <cfreturn vote>
</cffunction>

欠点は、リクエスト中に他の何かがデータを変更した場合、古いキャッシュが発生することですが、実行中に変更を期待していないようです。

于 2011-02-02T18:58:52.910 に答える