Dynamic agents with Delphi

xiaoxiao2021-03-06  43

I didn't think that Delphi didn't use Delphi to achieve dynamic agents last weekend, but I can't put this idea in these few days. This is really a wonderful idea. Moreover, after careful reading VCL's implementation of SOAP, at least 90% of the grasp can achieve such a dynamic agent.

So what is the use of dynamic agents?

This should first start from GoF's Proxy mode.

Assume that there is such an interface and its implementation:

Now, if you are the user of this interface, this interface and its implementation provide a:

Foo: IFOO;

Give you, where foo points to an instance of TFOOIMPL. Now you have the definition of IFOO, and this foo instance - note that you don't have TFOOIMPL definitions and implementation code. What if you want to add transaction functions for all ifoo.dosth (assuming that Dosth is implemented as an update of the database), what should I do?

GOF's Proxy mode is one of the solutions:

If you want to implement a new iFoo interface to implement - TSTATICPROXY. Among them, an attribute FIMPL records an instance of TFOOIMPL. Dosth and bar are then implemented in TStaticProxy, and the BAR function that does not need to be changed directly to the FIMPL process, and transaction can be added in Dosth implementation. TSTATICPROXY code is approximately as follows:

TSTATICPROXY = Class (TinterFaceDObject, Iifoo)

Private

FIMPL: IFOO;

public

Constructor Create (AIMPL: IFOO);

Function dosth (...): xxx;

Function bar (...): xxx;

END;

Constructor TStaticProxy.create (AIMPL: IFOO);

Begin

FIMPL: = AIMPL;

END;

Function TStaticProxy.dosth (...): xxx;

Begin

Begintransaction;

Result: = dimpl.dosth (...);

Endtransaction;

END;

Function TStaticProxy.bar (...): xxx;

Begin

Result: = fimpl.bar (...);

END;

Then, in all where you need to use the Foo object, you can change the new Newfoo object, as follows:

VAR

Newfoo: ifoo;

Begin

NEWFOO: = TSTATICPROXY.CREATE (FOO) As IFOO;

... // After you can use Newfoo as Foo as Foo.

END;

It can be seen that we pass a proxy class to proxy all the operations of the IFOO interface, which is equivalent to inserting its own code from IFOO to TFOOIMPL, to some extent, this is the so-called "cross cut" of AOP. Of course, if you can have TFooImpl code, it is simple, as long as you are as follows:

Delicate a TNEWFOOIMPL from TFOOIMPL, then in Override, you can replace the FOO reference in the Override of TFOOIMPL, then create a TNEWFOOIMPL.

But the problem is that you must use TFOOIMPL code and can change the provided Foo instance, but this is not done many times - unless it is not using Delphi, it is like Python's dynamic language ^ o ^ . For example, component containers, such as remote instances, etc. There is also a "virtual agent" (that is, when you create a fiMPL cost, just create a proxy class when you create, then instance of the FIMPL is created when you really need it), but the above static agent is still very troublesome. First of all, if the member functions of the IFOO must be added to them by one by one; secondly, if there are many interfaces in the application that require a proxy, they must write such a dedicated agent class; third, When you need to change the agent function, you need to modify all proxy classes ...

In particular, when the component container or a universal remote agent, the static agent is not used in the case where the interface to be implemented is not determined.

So we need "dynamic agents". I am watching Gigix published in the article "Dynamic Agent" on this year "Programmer", although he said that Java is proposed in JDK1.3, in java.lang.Reflect Proxy. But this makes me alert, and I found that it can also achieve such a dynamic agent in Delphi.

A typical dynamic agent is as follows:

In this way, we only need to add an instance of an IinvocationHandler interface to the function, as the TfooInvHandler in the figure. Then dynamically create an instance of TDynamicProxy supporting the IFO interface - it is a dynamic agent, only need to pass the corresponding parameters: The interface to be implemented and the corresponding INVHANDLER instance, do not need to write a proxy for each interface. Of course, as mentioned in GIGIX, this can be implemented with template, but the problem is that template is in the end is a dynamic technology of compile, and it is still necessary to run when the component container needs to run, it still cannot be implemented . Finally, InvHandler calls the specific implementation foo through the RTTI.

Its usage is as follows:

TfooInvHandler = Class (TinterFaceDObject, IinvocationHandler)

Private

FIMPL: IFOO;

public

Constructor Create (AIMPL: IFOO);

Function Invoke (IID, MethodName, Args []): XXX;

END;

Constructor tfooinvhandler.create (AIMPL: IFO);

Begin

FIMPL: = AIMPL;

END;

Function TfooInvHandler.Invoke (IID, MethodName, Args []): XXX

Begin

IF (IID = ifoo) and (MethodName = 'dosth') THEN

Begin

Begintransaction;

Result: = DOINVOKE (Fimpl, IID, MethodName, Args []);

Endtransaction;

End

Else

Result: = DOINVOKE (Fimpl, IID, MethodName, Args []);

END;

VAR

Handler: IinvocationHandler;

Newfoo: ifoo;

Begin

Handler: = tfooinvhandler.create (foo);

NEWFOO: = TDYNAMICPROXY.CREATE (TypeInfo (ifoo), Handler) AS ifoo; ... //, you can use Newfoo as used as Foo.

END;

Note: I didn't think about how I didn't want to define it, so this code is just a problem. In addition, DOINVOKE is called FIMPL through RTTI.

As can be seen from the code, TDynamicProxy dynamically generates a proxy for the IFOO interface via the parameter ifoo, and inserts a processing interface IinvocationHandler via the Handler parameter, and the call to the IFOCATIONHandler interface is transferred to the IINVOCATIONHANDLER interface. Processing by the TFOOINVHANDLER class. Here, it is possible to dynamically determine if the transaction is required to be cut, which method is required to be cut. Which of the interface is required.

With such a dynamic agent, it is also convenient to sequise in InvocationHandler, such as security check, log, etc. In this case, it is not a problem with Delphi to implement AOP.

Now I am facing: How to define this IinvocationHandler.

In fact, the most important problem here is the issue of the passage of parameters. The interface can be represented by IID, the method can be used by the method name, but the parameter varies too much: First, the number is uncertain, there can be any multiple parameters; the second is the type uncertainty; third is the issue of the value parameters and reference parameters. As the example of the example is a simple way, it is to record with an unordered Variant array that can solve the first two problems, but the third question is more troublesome. Is it necessary to use a tuple to return value? Too much trouble.

In the VCL's SOAP implementation is recorded through a TinVContext, but such a developer of Handler has to face the internal complexity of TinVContext, ease of use is too bad.

This is what I still can't determine the achievement. -_- |||

Raptor Feb.03-05

转载请注明原文地址:https://www.9cbs.com/read-60785.html

New Post(0)