跳至主要內容

代理模式

约 718 字

代理模式

核心思想

代理模式允许控制对另一对象的访问。

典型用例

保护代理

控制对原始对象的访问,用于不同权限的访问控制。保护代理根据访问者的权限决定是否允许访问真实对象。

在这个例子中,保护代理根据用户角色来决定是否允许访问。这样的设计可以用来在不同层级的应用程序中实现权限控制,而不必修改真实对象的代码。

// npm run code src/code/design-pattern/proxy/protection-agent.ts

export {};

// 定义真实对象和代理的共同接口
interface SensitiveOperation {
    performOperation(): void;
}

// 创建真实对象类
class SensitiveObject implements SensitiveOperation {
    performOperation(): void {
        console.log("Performing a sensitive operation.");
    }
}

// 创建保护代理类
class ProtectedProxy implements SensitiveOperation {
    private sensitiveObject: SensitiveObject;
    private hasAccess: boolean;

    constructor(userRole: string) {
        this.sensitiveObject = new SensitiveObject();
        this.hasAccess = userRole === "admin"; // 假设只有管理员有权限
    }

    performOperation(): void {
        if (this.hasAccess) {
            this.sensitiveObject.performOperation();
        } else {
            console.log("Access denied. You do not have permission to perform this operation.");
        }
    }
}

const adminProxy = new ProtectedProxy("admin");
adminProxy.performOperation(); // 输出: Performing a sensitive operation.

const userProxy = new ProtectedProxy("user");
userProxy.performOperation(); // 输出: Access denied. You do not have permission to perform this operation.

远程代理

代理模式可以为远程对象提供一个本地的代理,以便可以像访问本地对象一样访问远程对象。例如,在分布式对象通信中,远程代理可以隐藏网络相关的复杂性。

// npm run code src/code/design-pattern/proxy/remote-agent.ts

export {};

interface IRemoteService {
    fetchData(): Promise<string>;
}

class RemoteService implements IRemoteService {
    async fetchData(): Promise<string> {
        // 模拟网络延迟
        await new Promise(resolve => setTimeout(resolve, 1000));
        return "Data from remote service";
    }
}

class RemoteServiceProxy implements IRemoteService {
    private service: RemoteService;

    constructor() {
        this.service = new RemoteService();
    }

    async fetchData(): Promise<string> {
        console.log("Proxy: Delegating fetching data to remote service...");
        const data = await this.service.fetchData();
        console.log("Proxy: Data fetched from remote service.");
        return data;
    }
}

async function clientCode(service: IRemoteService) {
    const data = await service.fetchData();
    console.log(`Client: Received data: ${data}`);
}

const proxy = new RemoteServiceProxy();
clientCode(proxy);

虚拟代理

使用虚拟代理可以用于延迟创建开销较大的对象。当对象的创建成本很高时,虚拟代理先代表真实对象存在,直到真正需要对象时才创建它。例如,在图像加载中,直到图像真正需要显示在屏幕上时才加载。

// npm run code src/code/design-pattern/proxy/virtual-agent.ts

export {};

interface ExpensiveObject {
    performOperation(): void;
}

class ConcreteExpensiveObject implements ExpensiveObject {
    constructor() {
        console.log("ConcreteExpensiveObject: Initialized (heavy operation)");
    }

    performOperation(): void {
        console.log("ConcreteExpensiveObject: Performing operation");
    }
}

class VirtualProxyExpensiveObject implements ExpensiveObject {
    private expensiveObject: ConcreteExpensiveObject | null = null;

    performOperation(): void {
        if (!this.expensiveObject) {
            console.log("VirtualProxyExpensiveObject: Lazily initializing ConcreteExpensiveObject");
            this.expensiveObject = new ConcreteExpensiveObject();
        }
        this.expensiveObject.performOperation();
    }
}

function clientCode(expensiveObject: ExpensiveObject) {
    console.log("Client: Executing operation.");
    expensiveObject.performOperation();
}

const virtualProxy = new VirtualProxyExpensiveObject();
clientCode(virtualProxy); // 只有在这里调用时,ConcreteExpensiveObject 才会被创建

智能引用代理

智能引用代理通常需要创建一个代理类,这个类在访问实际对象时执行额外的操作,这种代理可以用来跟踪对象的引用次数、预加载资源、进行权限检查,或者执行其他在直接访问对象之前需要的操作。

// npm run code src/code/design-pattern/proxy/intelligent-reference-agent.ts

export {};

interface Subject {
    performAction(): void;
}

class RealSubject implements Subject {
    performAction(): void {
        console.log("RealSubject: Performing action");
    }
}

class SmartReferenceProxy implements Subject {
    private realSubject: RealSubject;
    private accessCount: number = 0;

    constructor(realSubject: RealSubject) {
        this.realSubject = realSubject;
    }

    performAction(): void {
        this.accessCount++;
        console.log(`SmartReferenceProxy: Access count is ${this.accessCount}`);
        
        // 在调用真实对象之前可以执行一些额外的操作
        console.log("SmartReferenceProxy: Additional actions before calling RealSubject");
        
        this.realSubject.performAction();

        // 在调用真实对象之后也可以执行一些额外的操作
        console.log("SmartReferenceProxy: Additional actions after calling RealSubject");
    }
}

function clientCode(subject: Subject) {
    subject.performAction();
}

const realSubject = new RealSubject();
const proxy = new SmartReferenceProxy(realSubject);
clientCode(proxy); // 第一次调用
clientCode(proxy); // 第二次调用

缓存代理

当一个操作的结果被请求多次时,缓存代理可以直接从缓存中返回结果,而不是重新执行昂贵的操作。缓存代理可以为昂贵的操作结果提供临时存储,以便多个客户端可以共享这些结果,从而减少计算或网络延迟。

// npm run code src/code/design-pattern/proxy/cache-proxy.ts

export {};

interface IService {
    fetchData(param: string): Promise<string>;
}

class Service implements IService {
    async fetchData(param: string): Promise<string> {
        console.log(`Service: Fetching data for ${param} (expensive operation)`);
        // 模拟网络请求或其他昂贵操作
        await new Promise(resolve => setTimeout(resolve, 1000));
        return `Data for ${param}`;
    }
}

class CachingProxy implements IService {
    private service: Service;
    private cache: Map<string, string>;

    constructor(service: Service) {
        this.service = service;
        this.cache = new Map();
    }

    async fetchData(param: string): Promise<string> {
        if (this.cache.has(param)) {
            console.log(`CachingProxy: Returning cached data for ${param}`);
            return this.cache.get(param)!;
        }

        const data = await this.service.fetchData(param);
        this.cache.set(param, data);
        console.log(`CachingProxy: Cache updated for ${param}`);
        return data;
    }
}

async function clientCode(service: IService) {
    console.log(await service.fetchData("param1"));
    // 第二次调用相同参数时,将从缓存中获取数据
    console.log(await service.fetchData("param1"));
}

const service = new Service();
const proxy = new CachingProxy(service);
clientCode(proxy);

日志记录代理

日志代理可以记录一个对象的所有操作。通过代理模式,可以在执行操作的同时,自动记录每次操作的详细信息,例如,记录对数据库的查询。

// npm run code src/code/design-pattern/proxy/logging-agent.ts

export {};

interface IService {
    performTask(action: string): void;
}

class Service implements IService {
    performTask(action: string): void {
        console.log(`Service: Performing task '${action}'`);
    }
}

class LoggingProxy implements IService {
    private service: Service;
    private log: string[] = [];

    constructor(service: Service) {
        this.service = service;
    }

    performTask(action: string): void {
        const logMessage = `LoggingProxy: Task '${action}' performed at ${new Date().toISOString()}`;
        this.log.push(logMessage);
        console.log(logMessage);

        this.service.performTask(action);
    }

    getLog(): string[] {
        return this.log;
    }
}

function clientCode(service: IService) {
    service.performTask("Task1");
    service.performTask("Task2");
}

const realService = new Service();
const proxy = new LoggingProxy(realService);
clientCode(proxy);

console.log("Logged Operations:");
console.log(proxy.getLog().join("\n"));

防火墙代理

防火墙代理可以决定哪些网络请求是安全的,哪些应该被拒绝。防火墙代理充当客户端和资源之间的中介,可以基于预定义的规则允许或拒绝对资源的访问,防止不安全的或不受信任的请求访问关键资源。

// npm run code src/code/design-pattern/proxy/firewall-agent.ts

export {};

interface INetworkService {
    fetchData(request: string): Promise<string>;
}

class NetworkService implements INetworkService {
    async fetchData(request: string): Promise<string> {
        console.log(`NetworkService: Fetching data for request: ${request}`);
        // 模拟网络请求
        await new Promise(resolve => setTimeout(resolve, 1000));
        return `Data for ${request}`;
    }
}

class FirewallProxy implements INetworkService {
    private networkService: NetworkService;
    private bannedRequests: Set<string>;

    constructor(networkService: NetworkService) {
        this.networkService = networkService;
        this.bannedRequests = new Set(["badRequest", "unauthorizedRequest"]);
    }

    async fetchData(request: string): Promise<string> {
        if (this.bannedRequests.has(request)) {
            console.log(`FirewallProxy: Access denied for request: ${request}`);
            throw new Error("Request blocked by firewall");
        }

        return await this.networkService.fetchData(request);
    }
}

async function clientCode(service: INetworkService) {
    try {
        console.log(await service.fetchData("goodRequest"));
        console.log(await service.fetchData("badRequest"));
    } catch (error) {
        console.error(`Error: ${(error as Error).message}`);
    }
}

const service = new NetworkService();
const firewallProxy = new FirewallProxy(service);
clientCode(firewallProxy);

上次编辑于: