AOP: Improves code encapsulation and reusability through Aspect-oriented programming (conversion and Collection)

Source: Internet
Author: User
Tags custom name
Release date: 4/8/2004 | Updated on: 5/28/2004

Dharma Shukla, Simon fell, and Chris sells

Level of difficulty 1 2 3

Download this articleCode(538kb)

Abstract: Aspect-oriented programming (AOP) is a programming paradigm invented by Xerox PARC in the 1990s S, it allows developers to better separate tasks that shouldn't be entangled with each other (such as mathematical operations and Exception Handling. The AOP method has many advantages. First, because the operation is more concise, the performance is improved. Second, it makesProgramYou can spend less time rewriting the same code. In short, AOP can provide better encapsulation for different processes and improve future interoperability.

What makes software engineers want to become hardware engineers? Since the invention of the function, programmers have spent a lot of time (and most of their boss's money) trying to design such a system: they are just a combination of models, made up of parts created by others, it is arranged in a unique shape and then covered with some pleasing colors. Functions, templates, classes, components, and so on are all attempts by software engineers to create "software integrated circuits" (simulating the electronic devices of hardware designers.

I blame this on Lego ). The cool click of the two toys (Component) is addictive, and many programmers will invent a new mechanism of encapsulation and reuse. The latest developments in this area are called Aspect-oriented programming (AOP ). The core of AOP is to arrange components (one stacked on the other), so that you can obtain the level of reuse that cannot be obtained by other component-based development methods. This arrangement is performed in the call stack between the client and the object. The result is that a specific environment is created for the object. This environment is exactly what AOP programmers are pursuing. Continue to read this article and you will understand this.

The sample code provided in this article is divided into two parts: COM and Microsoft. NET. The com part creates a basic structure that can be added to the COM object to provide the user interface to configure the class, we also provide an example implementation on the infrastructure we provide. The. NET section describes how to use the built-in. net infrastructure to complete the same job for the com version, but the code is less and more options are used. It also provides examples that suit this infrastructure. This article will describe all these codes later.

Content on this page
What is Aspect-oriented programming?
General use AOP
Design Framework
As a COM Object
Object activation
Aspect Builder
. Net
Context bound object
. Net
Aspect and context
Conclusion
RelatedArticleSee:
For background information, see:

What is Aspect-oriented programming?

Generally, an object is "bonded" by a line of code. Create this object. Create the object. Set attributes for the object (whose value is this object. Some user data is also dotted. Stir everything together. It can be executed at 450 degrees during running. Connecting multiple components in this way can lead to the following problem: to implement different methods, it takes a lot of time to write the same code. These lines of code usually have the following operations: log the activity of this method into a file for debugging, run the security check, start a transaction, open a database connection, remember to capture C ++ exceptions or Win32 structured exceptions to convert to com exceptions, and verify the parameters. Also, remember the settings when the destruction method starts after the method is executed.

This type of repetitive code often occurs because developers are trained to design the system based on the terms in the software press release. If the banking system is designed, account and customer categories are essential, they collect their unique details in one place, however, each of these methods also requires operations such as logs, security checks, and transaction management. The difference is that operations such as logs are system aspects unrelated to specific applications. Everyone needs them. Everyone writes this code. Everyone hates this.

Oh, not everyone ...... Everyone needs to use these services, and everyone hates writing repetitive code, but not everyone needs to write them. For example, COM + and. Net programmers can perform so-called attribute-based programming, also known as Declarative Programming. This technology allows programmers to use attributes to modify types or methods and declare that certain services need to be provided at runtime. For example, some services provided by COM +, including role-based security, real-time activation, distributed transaction management, and mail processing. When calling a method, a group of objects obtained between the client and the server will be placed in the runtime (for COM + programmers, it is called a "listener",. net programmers, called "message receiving"), provides services for each method without the need for component developers to write any code. This is a programming paradigm for Aspect-oriented programming (invented by Gregor kiczares, the Palo Alto Research Center at Xerox in 1990s, see the simplest form of http://www.parc.xerox.com/csl/groups/sda/publications/papers/Kiczales-ECOOP97/for-web.pdf.

In the field of AOP, the COM + listener is related to components through metadata. Metadata is used to construct this set of items during runtime, which is usually performed when an object is created. When the client calls a method, the special aspects in turn obtain the opportunity to process the call, execute the service, and finally call the method of the object. During the return process, every aspect has the opportunity to expand. In this way, you can extract the same lines of code to be written in each method of each component and put them in various aspects for the runtime to place them. This group provides context for the execution of component methods. Context provides the implementation of methods in the environment, and the operations have special significance.


Figure 1 objects securely stored in the context

For example, figure 1 shows an object securely stored in the context, which provides error propagation, transaction management, and synchronization. The program in the Win32 console application can assume that the console exists in the context, and the result of calling printf is displayed on the console. The AOP object can also assume that the transaction has been established, and calling the database will be part of the transaction. If a problem occurs when you set these services (for example, there are not enough resources to establish a transaction), the object will not be called, so you don't have to worry about it.

Back to Top

General use AOP

Although COM + provides most of the services required by AOP, to use it as a general-purpose AOP environment, it lacks the necessary important details: the ability to define custom aspects. For example, if role-based security is not suitable, role-based security cannot be implemented (as if the most dangerous person is allowed to protect their own objects ). If programmers have this capability, many com usages can be implemented using the AOP framework. Figure 2 provides a brief list of examples.

Back to Top

Design Framework

Of course, with such a framework concept, we must build it. We hope this framework has the following features:

Runtime that Concatenates the client and the object.

User-defined aspects are implemented using COM components.

Description of the metadata associated with each COM component, such as the COM + directory.

When you are ready, the client can be used to activate components.

The concept of the AOP framework is very simple. The key is listening and delegation. The method of listening is to convince the caller that the interface pointer it holds points to the object it requested. In fact, this is a pointer to the listener, you can use one of the activation techniques described later in this article to obtain the listener. The listener needs to implement the same interface as the target component, but it needs to delegate all calls through the aspect stack associated with the component. When calling a method, the listener will provide the opportunity for preprocessing and post-processing calls for each aspect, as well as the ability to spread or cancel the current call.

The AOP framework performs two different steps: component activation and method calling. In component activation, the AOP framework implements the object stack and returns a reference to the listener instead of a reference to the actual object. In a method call, when the caller calls a method call to the listener, the listener delegates the call to all registered aspects so that the [in] and [in, out] the parameter is preprocessed and the actual call is provided to the object. Then, the call return values returned by the component and the [In, out] and [out] parameters on the call Stack are passed to the party for post-processing.

Back to Top

As a COM Object

In our AOP framework, we implement the com class of the iaspect interface, as shown in figure 3. The Framework calls the iaspect: preprocess method in all specified aspects before passing the method call to the actual underlying component instance (hereinafter referred to as the principal. It passes the identity of the principal, the IID of the interface, the method name, The vtbl slot of the method, and the enumerator of the [in] and [In, out] parameters to the corresponding aspect. If a failed hresult is returned from preprocess, the framework does not provide the call to the delegated user. In fact, the call is canceled.

After preprocessing is successful, the Framework provides the actual call to the delegate. The framework will call the iaspect: postprocess method to pass all the parameters required by the hresult and postprocess Methods returned by the delegate, whether or not the delegate returns the hresult, however, this time the enumerator is built on the [out], [In, out], and [out, retval] parameters.

Figure 4 shows how to write call tracing. It can trace the parameters provided by all callers passing to the delegate method.

Now that we have a framework for calling and a usable aspect, we need a mechanism to concatenate them. This operation is performed when the object is activated.

Back to Top

Object activation

Although we need to stack up any number of aspects between the client and the object, the client should be able to create an object and call its method, just as in the case of no listening. Unfortunately, if COM does not adopt some fancy technical means (this is exactly what Microsoft Transaction Service must implement before it is integrated into the com infrastructure and renamed as COM + ), it does not support any extended code injected into its master activation API cocreateinstance. However, COM does provide a fully extended activation API: GetObject in Visual Basic (cogetobject for C ++ programmers ). We use a custom name object to construct the AOP activation code based on this API.

A com name object is a piece of code that converts any string (called the display name) to a COM Object. This means you can create a new one or find one from the file, even download from the moon. Our AOP name object obtains metadata (describes the aspects associated with the classes discussed here), creates instances of this class, constructs the aspect stack, and concatenates them through the AOP listener, then, return the listener to the client. The following is an example:

 
Private sub form_load () set myfoo = GetObject ("aoactivator: C: \ aopfoo. xml") myfoo. dosomethingfooish end sub

Note that the client does not need any special operations to use components except to obtain the foo instance. Although the aopfoo. xml file associates any number of aspects with the specific instance of Foo, it implements the same interface, and more importantly, it has the same semantics.

Implementing a custom com name object is a magical technique in a sense, mainly involving the internal knowledge of previous Ole details. Fortunately, most of the implementation content is lazy, and COMCommunityA long time ago, the basic implementation of the name object was written into an ATL class called ccommoniker. (You can access the http://www.sellsbrothers.com/tools to get this com name object framework .) To use this framework, we really need to care about parsedisplayname (this is a boring method for analyzing custom display name syntax) and bindtoobject (part of the name object, this name object is used to activate the COM Object indicated by the display name provided by the client) (see figure 5 ).

Note that the code in Figure 5 does not show the most difficult part-creating and initializing the listener. The difficulty lies not in the listener itself, but in what the listener must do. Remember that to make full use of functions in a universal AOP framework, you must be able to respond to the QueryInterface method using a set of interfaces identical to any encapsulated component. The returned interface must be able to obtain the call stack provided by the client of each method, pass it to all aspects, and always pass it to the component itself to keep the parameters complete-no matter how many, and the type. This is a very difficult task, involving a large number of _ declspec (naked) and ASM thunk.

Fortunately, because the com community is very mature, we were able to stand on the shoulders of giants using the universal delegate (UD), a COM component created by Keith Brown to execute this task. Keith has written in MSJ in two parts to describe his UD: "Building a lightweight com interception framework, Part I: the universal delegator", and Part II: "the guts of the UD ". We use the UD of Keith to implement the AOP framework, which reduces the "magic" part of the bindtoobject implementation, as shown in figure 6.

To wrap the target component for the client, perform the following four steps:

1. Use the CLSID of the component to create the actual component. The CLSID is passed to the name object originally in the metadata XML file.

2. A delegatorhook object is created to listen for the QueryInterface call to the object. The hook is responsible for routing method calls to every aspect.

3. Create a UD object to retrieve the idelegatorfactory interface.

4. Call createdelegator using idelegatorfactory to pass the interface of the actual object, the delegate Hook, the IID (riidresult) of the interface requested by the source caller, And the pointer to the interface pointer (ppvresult ). The delegate returns a pointer to the listener, which can call the delegate hook of each call.


Figure 7 com aop Structure

Result 7 is displayed. The client can use the listener as the actual interface pointer of the target component. After the call, they are routed along the path to the target component.

Back to Top

Aspect Builder

To activate components and link all aspects correctly, our AOP name object relies on an XML file to describe components and associated aspects. The format is very simple. It only contains the CLSID of the component and the CLSID of the aspect component. The example in Figure 8 encapsulates Microsoft FlexGrid Control in two aspects. To simplify the task of creating an AOP metadata instance, we have created an aspect Builder (as shown in Figure 9 ).


Figure 9 aspect Builder

The aspect builder will enumerate all aspects registered on the machine and display them in the List View on the left with a cloud map. The client area of aspect builder contains the graphical representation of the component. You can double-click it (or use the corresponding menu item) and specify the progid of the component. After selecting a component, you can drag and drop the aspect to the client area and add the aspect to the AOP metadata of the component.

To generate the XML format required for the AOP name object, select the "compile" menu item in the "Tools" menu, and the metadata will be displayed in the bottom pane. You can write scripts in the verify aspects pane to verify that the metadata is correct. You can save compiled XML instances on disks or use aspect builder to reload them.

Back to Top

. Net

Although the aspect builder greatly simplifies the work, the aspect metadata is stored separately from the component, which makes it inconvenient to program AOP in COM. Unfortunately, the metadata of COM lacks many necessary functions in terms of scalability, which is why we feel that metadata and classes need to be stored separately first. However, as a clear successor to com,. Net does not have this problem. . Net metadata is completely scalable, so it has all the necessary foundations and can be directly associated with the class itself through attributes. For example, given a custom. Net attribute, we can easily associate the Call trace attribute with the. NET method:

 
Public class bar {[calltracingattribute ("in bar ctor")] Public Bar () {} [calltracingattribute ("in bar. calculate Method ")] public int calculate (int x, int y) {return X + Y ;}}

Note that the square brackets contain the calltracingattribute and the string output when accessing the method. This is the property syntax that associates the custom metadata with the two bar methods. Like the AOP framework in COM, attributes in. NET are classified based on components in. net .. The Custom Attributes in. NET are implemented using classes derived from attributes, as shown below:

 
Using system; using system. reflection; [attributeusage (attributetargets. classmembers, allowmultiple = false)] public class calltracingattribute: attribute {public calltracingattribute (string s) {console. writeline (s );}}

Our attribute classes also have attributes that modify their behavior. In this case, we require that this attribute be associated only with the method, instead of the assembly, class, or field, and each method can have only one trace attribute associated with it.

After we associate attributes with methods, we are half done. To provide the AOP function, you also need to access the call stack before and after the environment necessary for each method to establish the execution component. This requires a listener and the context on which the component depends. In COM, we require the client to activate the component using the AOP name object to implement this task. Fortunately,. Net has built-in hooks, so the client does not have to do any special work.

Back to Top

Context bound object

The key to listening in. Net (the same as in COM) lies in the provision of context for COM components. In the COM + and custom AOP frameworks, by stacking between the client and the object, context is provided for the component before the method is executed. In. net, context is provided for any class derived from system. contextboundobject:

Public class liketolivealone: contextboundobject {...}

When the liketolivealone class instance is activated, a separate context is automatically created during. Net runtime for its survival, and a listener is created, from which we can suspend our own aspects .. Net Listener is a combination of two objects-transparent proxy and real proxy. The behavior of the transparent proxy is the same as that of the target object, and the same as that of the com aop listener. It serializes the call stack into an object called a message and then delivers the message to the real proxy. The real proxy receives the message and sends it to the first message for processing. The first message receiving processes the message, sends it to the next message receiving in the message receiving stack between the client and the object, and then processes the message. The next message is also received, and so on, until it reaches the receiving of the stack builder, which deserializes the message back to the call stack, calls the object, serializes the outbound parameters and return values, and return to the previous message reception. This call chain 10 is shown.


Figure 10 listener

To participate in this message receiving chain, we first need to derive attributes from contextattribute (not just attribute), and provide the so-called context attributes to update the attributes to participate in the context binding object:

{[Attributeusage (attributetargets. class)] public class calltracingattribute: contextattribute {public calltracingattribute (): Base ("calltrace") {} public override void getpropertiesfornewcontext (iconstructioncallmessage CCM) {ccm. contextproperties. add (New calltracingproperty ());}??? }

When this object is activated, the getpropertiesfornewcontext method is called for each context attribute. In this way, we can add our context attributes to the attribute list associated with the new context created for the object. Context attribute allows us to associate a message receipt with an object in the message receipt chain. The property class implements icontextobject and icontextobjectsink as the factory for receiving messages:

 
Public class calltracingproperty: icontextproperty, icontributeobjectsink {public imessagesink getobjectsink (descrialbyrefobject o, imessagesink next) {return New calltracingaspect (next );}??? }

The process of creating attributes for the proxy is shown in 11, where context attributes are created first, and then message receipt is created.


Figure 11. Net messagesink Creation

Back to Top

. Net

When everything is correctly appended, each call will go to the imessagesink implementation. Syncprocessmessage allows us to pre-process and post-process messages, as shown in Figure 12. Finally, you want to use calltracingattribute to declare your preference for the context binding class associated with call tracing:

Finally, you want to use calltracingattribute to declare your preference for the context binding class associated with call tracing:

 
{[AOP. Experiments. calltracingattribute ()] public class traceme: contextboundobject {public int returnfive (string s) {return 5 ;}}

Note that context attributes are associated with classes rather than each method .. Net context structure will automatically notify us of each method, so our call trace attributes have all the required information, which avoids the previous processing of common attributes, it is difficult to manually associate attributes with each method. When the client class instantiates the class and calls a method, the aspect is activated:

 
Public class client {public static void main () {traceme = new traceme (); traceme. returnfive ("stringarg ");}}

During runtime, the client and the object-oriented objects output the following content:

 
Preprocessing: traceme. returnfive (S = stringarg) postprocessing: traceme. returnfive (returned [5])
Back to Top

Aspect and context

So far, we have not yet achieved the expected AOP ideal in this simple aspect. Although it can indeed be used for separate preprocessing and post-processing of method calls, what is really interesting is the impact of the aspect on method execution itself. For example, in the aspect of COM + transactions, all resource providers used in the object method are involved in the same transaction. In this way, only the transactions provided by COM + transactions can be aborted. Therefore, COM + adds the com call context, which provides a gathering point for all components that are interested in accessing the current transaction. Similarly,. NET provides an extensible call context that allows methods to participate. For example, you can place the object in the. NET context so that the object (which is encapsulated in call tracing) can add information to the trace message stream, as shown below:

 
Internal class calltracingaspect: imessagesink {public static string contextname {get {return "calltrace" ;}} private void preprocess (iMessage MSG ){??? // Set us up in the call context call. logicalcallcontext. setdata (contextname, this );}??? }

Once the aspect is added to the call context, the method can be extracted again and involved in the trace:

[Calltracingattribute ()] public class traceme: contextboundobject {public int returnfive (string s) {object OBJ = callcontext. getdata (calltracingaspect. contextname); calltracingaspect aspect = (calltracingaspect) OBJ; aspect. trace ("Inside methodcall"); return 5 ;}

By providing a method to increase the call context,. Net allows you to set a real environment for the object. In our example, it is similar to the transactional aspect of COM + that allows an object to add a trace statement to a stream without having to know the stream destination, how to create a stream, and when to destroy the stream:

 
Preprocessing: traceme. returnfive (S = stringarg) During: traceme. returnfive: Inside methodcall postprocessing: traceme. returnfive (returned [5])
Back to Top

Conclusion

With Aspect-Oriented Programming, developers can encapsulate the use of public services across components in the same way as encapsulated components themselves. By using metadata and listeners, you can place any service between the client and the object. Such operations are semi-seamless in COM and seamless in. net. This article describes how to access the call stack when calling methods. They provide an added context for objects to survive. Although it is not mature compared with structured or object-oriented programming, local support for AOP in. NET provides a valuable tool for us to pursue software dreams like Lego toys.

Back to Top

For more information, see:

Keith Brown's series of articles on the universal delegate:

Building a lightweight com interception framework, Part 1: the universal delegator

Building a lightweight com interception framework, Part II: the guts of the UD

Back to Top

For background information, see:

Http://portal.acm.org/portal.cfm

Http://www.aosd.net

Generative programming by Krzysztof Czarnecki and Ulrich eisenecker (Addison-Wesley, February 2000)

Aspectj-a JAVA Implementation of AOP

Dharma ShuklaIs the development lead of Microsoft BizTalk Server team. Dharma is currently engaged in the development of next-generation enterprise tools. You can send an email to the dharmas@microsoft.com to contact him.

Simon fellHe is an outstanding engineer at provada and used XML,. net, and COM to develop distributed systems. Simon is open to pocketsoapSource codeThe author of the soap toolkit and created a com tracehook with Chris sells. He is currently engaged in the development of soap binding for Avian carrier. To contact Simon, you can send an email to the http://www.pocketsoap.com.

Chris sellsAn independent consultant who specializes in distributed applications in. NET and COM and teaches at developmentors. He has created several books, including ATL internals, which should be updated this year. He is writing a book on Windows forms, which will be published in 2002. Email can be sent to http://www.sellsbrothers.comhttp: // www.sellsbrothers.com to contact Chris.

From msdn magazine in March 2002.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.