Design pattern notes - agent pattern

Posted by McInfo on Sun, 21 Nov 2021 01:39:07 +0100

Design pattern notes - agent pattern

Introduction to agent mode

The proxy mode is usually an intermediary system between the requester and the provider. The requester is the party who sends the request, and the provider is the party who provides the corresponding resources according to the request

The proxy server in the Web is an example. The client sends a network request to the proxy server, connects to the proxy server, evaluates the request, sends the encapsulated request to the corresponding remote server, and sends the response to the client after receiving the response. In this process, the proxy server acts as an intermediary and encapsulates the request, Privacy protection is ideal for running in a distributed architecture

The proxy mode acts as the class of the actual object interface, and the object types are diverse. For large objects in network connection, memory and files, the proxy encapsulates the wrapper or agent of the actual service object. It can provide additional functions for the wrapped object without changing the object code. Its main purpose is to provide a proxy or placeholder for the actual object, So as to control the actual access of objects, it has many adaptation scenarios

  • For a complex system, agents can represent it in a simple way to encapsulate its internal complexity
  • It can protect the actual object and improve the security of the actual object. In many cases, the client is not allowed to directly access the actual object
  • Provides local interfaces for remote objects on different servers
  • Provides a lightweight handle to a remote object that consumes a lot of memory

PS:

When the production company wants to find actors to make movies, they usually communicate directly with the Agent rather than with the actors. The Agent will determine whether the actors are free according to the actors' schedule, so as to give the negotiation results. The production company (client) does not directly find the actors (actual objects), but directly communicate with their agents (Agent agents)

class Actor(object):
    
    def __init__(self):
        self.isBusy = False
        
    def occupied(self):
        self.isBusy = True
        print(type(self).__name__, "is occupied with Current movie")
        
    def available(self):
		self.isBusy = False
        print(type(self).__name__, "is free for the movie")
        
    def getStatus(self):
        return self.isBusy
    

class Agent(object):
    
    def __init__(self):
        self.principal = None
        
    def work(self):
        self.actor = Actor()
        if self.actor.getStatus():
            self.actor.occupied()
        else:
            self.actor.available()
            
       
if __name__ == "__main__":
    r = Agent()
    r.work()
  • The proxy mode provides a proxy for other objects to realize the access control of the original object
  • Used as a layer or interface to support distributed access
  • It can increase agents and protect real components from accidental effects

UML class diagram

UML is composed of three participants: Proxy, real object RealSubject and topic Subject. The UML composition diagram is as follows:

Proxy: maintain a reference, allow the proxy to access the actual object through this reference, and provide the same interface as the Subject, so that the proxy can replace the real Subject. It is also responsible for creating and deleting the real Subject

Subject: defines the public interface of RealSubject and proxy, and the formal subject. Proxy can be used anywhere RealSubject is used

Real subject: it defines the real object represented by the Proxy

Throughout the UML diagram, you can see:

**Agent: * * class that controls access to RealSubject class, handles client requests, and is responsible for creating and deleting RealSubject

Topic / real topic: defines the public interface between real topics and agents, and real topics provide real implementation of functions

**Client: * * it accesses the Proxy class that completes the work. The Proxy is responsible for the access and control of RealSubject and responds to the request of the client

Four types of agent models

Virtual agent

If an object instantiation takes up a lot of memory, it can be represented by a placeholder first. You can't really access the object until you really request it. That is, in a Web site, if the loading of a large image requires a lot of resources, it will be represented by a placeholder before clicking it until you really click the image, It will be displayed, that is, to create the actual object

Remote agent

Provide a local representation for the actual objects located on the remote server or in the address space at different locations. For example, you want the application to establish a monitoring system, and the application involves multiple Web servers, database servers, celery task servers, cache servers, etc. If you want to monitor the CPU, memory and disk utilization of these services, you need to establish an object to monitor the context in which the application is running. At the same time, you can also execute remote commands to obtain parameter values.

Protection agent

To control the access to sensitive instance objects, the proxy will first verify the request legally when the client sends the request, so as to prevent illegal requests from causing the collapse of remote object instances. The agent will check whether the caller has the access rights required to forward the request

intelligent agent

When accessing objects, pull in other operations. For example, suppose there is a core component in the system that stores state information in one place. Usually, such components need to be called by multiple different services to complete their tasks, and may lead to the problem of sharing resources. Unlike letting the service call the core component directly, the intelligent agent is built-in and checks whether the actual object is locked before accessing to ensure that no other object changes it

example

Taking a person who goes to the store to buy water as an example, wechat now supports wechat payment, code scanning and payment. Wechat can be understood as acting as an agent, and the bank behind the deduction is the one who really performs the deduction. Wechat only helps maintain this interface and encapsulates the specific operations of the bank. Here, it is assumed that the client is You, You, During initialization, the agent will be called to instantiate the real object through make_pay to pay, and through__ del__ To give the payment results

For wechat wechat, both he and Bank have realized a do of payment_ Pay method. In this process, the Bank performs real deduction operation through the account information sent by wechat, except do_ In addition to the pay method, there are__ hasEnoughMenoy to check whether the balance is sufficient, setAccount to set the account information__ Reduce balance to deduct money through__ checkAccount to verify whether the user and user password are correct, and wechat calls do of Bank_ Pay to realize deduction operation

Python implementation

# -*- coding=utf-8 -*-

from abc import ABCMeta, abstractmethod


BankAccountData: dict = {
    "acc1":["10001", 50],
    "acc2":["10002", 100]
}

class Payment(metaclass=ABCMeta):

    @abstractmethod
    def do_pay(self, pay: int) -> bool:
        pass


class Bank(Payment):

    account: str
    password: str

    def __init__(self):
        self.account = None
        self.password = None

    def setAccount(self):
        self.account = input("Please input the account:")
        self.password = input("Please input the password:")

    def __checkAccount(self) -> bool:
        if self.account in BankAccountData:
            if self.password == BankAccountData[self.account][0]:
                return True
        return False

    def __reduceBalance(self, money: int) -> bool:
        if self.__hasEnoughMeony(money):
            BankAccountData[self.account][1] -= money
            return True
        return False

    def __hasEnoughMeony(self, money: int) -> bool:
        if self.__checkAccount():
            if money <= BankAccountData[self.account][1]:
                return True
            return False
        print("Account or Password that you input is error!")
        return False

    def do_pay(self, pay: int) -> bool:
        return self.__reduceBalance(pay)


class WetChat(Payment):

    def __init__(self):
        self.bank = Bank()

    def do_pay(self, pay: int) -> bool:
        self.bank.setAccount()
        return self.bank.do_pay(pay)


class You(object):

    def __init__(self):
        print("Let me buy the water!")
        self.wetchat = WetChat()
        self.isPurchased = None

    def make_pay_water(self):
        self.isPurchased = self.wetchat.do_pay(2)

    def __del__(self):
        if self.isPurchased:
            print("Pay successfully!")
        else:
            print("Pay failed!")


if __name__ == "__main__":
    you = You()
    you.make_pay_water()

Let me buy the water!
Please input the account:acc1
Please input the password:10001
Pay successfully!

C + + implementation

#include<iostream>
#include<map>
#include<string>

using namespace std;

map<string, pair<string, int>> bank_account_data = {{"acc1",pair<string, int>("10001", 50)},
                                                    {"acc2",pair<string, int>("10002", 100)}};


class Payment{

public:
    virtual bool do_pay(int money) = 0;
};

class Bank:public Payment{

public:
    Bank(){
        account = "";
        password = "";
    }

public:

    void setAccount(){
        cout<<"Please input the account:";
        cin>>account;
        cout<<"Please input the password:";
        cin>>password;
    }

    bool do_pay(int money) override{
        return __reduceBalance(money);
    }

private:

    bool __checkAccount(){
        if(bank_account_data.find(account) != bank_account_data.end()){
            auto res = bank_account_data[account];
            if(password == res.first){
                return true;
            }
        }
        return false;
    }

    bool __hasEnoughMoney(int money){
        if(__checkAccount()){
            if(money <= bank_account_data[account].second)
                return true;
            return false;
        }
        cout<<"Account or Password that you input is error!"<<endl;
        return false;
    }

    bool __reduceBalance(int money){
        if(__hasEnoughMoney(money)){
            bank_account_data[account].second -= money;
            return true;
        }
        return false;
    }

private:
    string account;
    string password;
};

class WetChat: public Payment{

public:
    WetChat(){
        bank = new Bank();
    }

public:
    bool do_pay(int money) override{
        bank->setAccount();
        return bank->do_pay(money);
    }

private:
    Bank *bank;
};

class You{

public:
    You(){
        cout<<"Let me but the water!"<<endl;
        wetchat = WetChat();
        isPurchased = false;
    }

    ~You(){
        if(isPurchased)
            cout<<"Pay successfully!"<<endl;
        else
            cout<<"Pay failed!"<<endl;
    }

public:

    void make_pay_water(){
        isPurchased = wetchat.do_pay(2);
    }

private:
    WetChat wetchat;
    bool isPurchased;
};

int main(int argc, char *argv[]){
    You you = You();
    you.make_pay_water();
}
Let me but the water!
Please input the account:acc1
Please input the password:10001
Pay successfully!

Advantages of agent mode

  • Agents can improve program performance by caching bulky objects or frequently accessed objects
  • Provide access privileges to real topics, so this mode will accept delegation only if appropriate permissions are provided
  • Remote code also facilitates interaction with remote servers that can be used as network and database connections

Facade mode and agent mode

Both of them are structural design patterns. The similarity is that they are real objects with an agent / facade in front of them, but there are differences in intention between the two patterns

proxy patternFacade mode
Placeholders are provided for other objects to control access to the original objectProvides an interface to a large subsystem of a class
It has the same interface as its target object and saves a reference to the objectThe communication and dependency between subsystems are minimized
Acts as a mediator between the client and the encapsulated objectProvides a single simple interface

Topics: Python C++ Design Pattern