This is the third article in a series on design patterns.

  1. A word to sum up the design pattern of all paths: factory mode =? Policy mode =? Template method pattern

  2. Use a combination of design patterns — decorator patterns in beauty Camera

  3. Use the combined design pattern – the remote proxy pattern to chase the girl

  4. Remove unnecessary state variables with design patterns – state patterns

The last article covered a design pattern that uses combinations: the decorator pattern. It reuses types by inheritance and behavior by composition, ultimately extending the functionality of classes.

The proxy pattern in this article also uses a composite implementation approach. It is very similar to the decorator pattern, and it is interesting to compare the subtle differences between them.

Why an agent?

An agent is an object that does something for you. Why delegate it to do it for you? Because doing this is complicated and there are details you don’t need to know, delegate it to a specialized object.

Just like the real life of the visa agent, countries do not need the same materials and procedures, some of them are very complex. So let a visa agent who knows the details handle it for us (after all, we still have a whole bunch of bugs waiting for us).

In real programming, there might be several complicated things: remote object access (remote proxy), creating expensive objects (virtual proxy), caching expensive objects (cache proxy), restricting object access (secure proxy), and so on.

Local & Remote

In Java, the standard for remote and local partitioning is: “Are they running on the same memory heap?”

  • A method in which an application on one computer calls an application on another computer over the network is called a remote call, because the two applications are running in the memory heap on different computers.
  • In Android, each application runs in its own process, and each process has its own virtual machine, so they run on different heaps of the same computer’s memory. Calls across processes are also called remote calls.

Remote invocation & remote proxy

A remote call is more complex than a local call because you need to handle local and remote communication (network or cross-process calls).

The originator of the call doesn’t really need to know these details; it’s better to simply make the call and get the result it wants. So let the agents do the complicated stuff. (Of course, you can write the details of making the remote call together with the business logic that makes the call, which is what procedural code does.)

Take cross-process communication in Android for example: the application that makes the call is called the client, and the application that responds to the call is called the server. Services are defined as interfaces in a file with the suffix AIDL:

// Here is the contents of the imessage.aidl file
package test.taylor.com.taylorcode;
interface IMessage {
    // The interface generated by the system itself
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString);
    // This is the service interface we define
    int getMessageType(int index) ;
}
Copy the code

The system automatically generates an imessage. Java file for imessage. aidl:

public interface IMessage extends android.os.IInterface {
    / / pile
    public static abstract class Stub extends android.os.Binder implements test.taylor.com.taylorcode.IMessage {
        public Stub(a) {
            this.attachInterface(this, DESCRIPTOR);
        }

        // The client calls this interface to get the service
        public static test.taylor.com.taylorcode.IMessage asInterface(android.os.IBinder obj) {
            // Create proxy object (inject remote object obj)
            return new test.taylor.com.taylorcode.IMessage.Stub.Proxy(obj);
        }
        
        / / agent
        private static class Proxy implements test.taylor.com.taylorcode.IMessage {
            // Hold remote objects by combination
            private android.os.IBinder mRemote;
            // Inject the remote object
            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }
            
            // The proxy object implements the service interface
            @Override
            public int getMessageType(int index) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    // Wrap the call parameters
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(index);
                    // Make a remote call (with some natVie layer methods that eventually call the stub method implemented by the server)
                    mRemote.transact(Stub.TRANSACTION_getMessageType, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return_result; }}}}Copy the code

(The code omits a lot of extraneous details in order to focus on the idea of agents.)

The system automatically generates two key classes for cross-process communication: Stub Stub and Proxy. They are concepts that emerge in pairs in Android cross-process communication. The pile is the implementation of the service interface by the server, and the proxy is the proxy of the client to the pile. Dizzy.. Why do you have to come up with so many concepts and make it so complicated?

In order to simplify the code for cross-process communication, the details of cross-process communication are encapsulated in the proxy. The client can call the methods of the proxy class directly (the proxy and the client are in the same heap, so it is also called remote local proxy), and the proxy makes the cross-process call and returns the result to the client. The agent plays the role of masking the details of complex cross-process communication, making the client think it is calling a remote method directly.

Piles and proxies have the same type, they both implement the service interface IMessage, but piles are abstract, and the concrete implementation is on the server side. Servers typically implement piles in the Android system component Service:

public class RemoteServer extends Service {
    public static final int MESSAGE_TYPE_TEXT = 1;
    public static final int MESSAGE_TYPE_SOUND = 2;
    
    / / implementation
    private IMessage.Stub binder = new IMessage.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {}

        // Define the service content
        @Override
        public int getMessageType(int index) throws RemoteException {
            return index % 2= =0? MESSAGE_TYPE_SOUND : MESSAGE_TYPE_TEXT; }};// Return the service instance to the client
    @Override
    public IBinder onBind(Intent intent) {
        returnbinder; }}Copy the code

The client obtains the service instance by binding the service:

IMessage iMessage;
Intent intent = new Intent(this, RemoteServer.class);
ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        // Pass the service instance (pile) to asInterface(), which will create the local proxy and inject the pile
        iMessage = IMessage.Stub.asInterface(iBinder);
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        iMessage = null; }};// Bind the service
this.bindService(intent, serviceConnection, BIND_AUTO_CREATE);
Copy the code

When the service is successfully bound, onServiceConnected() is called back, the local proxy is created, and the client can request the remote service via imessage.getMessageType ().

Remote agent mode vs. decorator mode

Remote agents are implemented in exactly the same way as the decorator pattern, and it’s interesting to put the two descriptions together:

  • The decorator and the decorator are of the same type, and the decorator holds the decorator by combination
  • The agent and the agent are of the same type, and the agent holds the agent through a combination

Big head… Why divide it into two modes if it’s the same? But there are subtle differences when you compare them in combination with their intentions:

  • The decorator pattern extends the functionality of existing types and behaviors by means of inheritance + composition.
  • In remote proxy mode, inheritance + combination is used to control access to proxy objects.

It doesn’t matter if you insist on using the decorator mode line to describe the agent mode: “The agent extends its functionality by decorating the represented, making it accessible to remote objects.” It makes perfect sense, but it’s a little weird.

If you try to add a little personification, the remote agent mode and decorator mode become very distinguishable!

  • Using proxy mode is like saying, “I like you, but I can’t reach you. So I need an agent (probably your bestie).”
  • Using decorator mode is like saying, “I like another person in your type. So I need to decorate you to look like it.” (Ok, you’re not going to find a girlfriend)

subsequent

This article is intended to summarize design patterns that use composition, including decorator, agent, adapter, appearance, and state patterns.

Thousand thousand did not expect to write write become n…

Never thought of, this agent mode is written to find that if all the application scenarios are finished, the length is too long, but only in this blank. There are many variants of the proxy pattern, with subtle differences in implementation and intent, which will be examined next.