You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何根据调用者确定方法可见性?特权方法访问控制方案问询

特权方法访问控制的设计模式与TypeScript实现优化

通用设计模式

1. 接口分离模式(ISP)

将类的方法拆分为公共接口和特权接口,仅向授权对象暴露特权接口。以你提到的Bank和Account场景为例:

// 公共接口:所有对象都能访问
interface PublicAccount {
  get balance(): number;
  get name(): string;
}

// 特权接口:仅Bank能访问
interface PrivilegedAccount extends PublicAccount {
  sendMoneyTo(target: PublicAccount, amount: number): void;
}

class Account implements PrivilegedAccount {
  private _balance: number;
  private _name: string;

  constructor(name: string, initialBalance: number) {
    this._name = name;
    this._balance = initialBalance;
  }

  get balance(): number { return this._balance; }
  get name(): string { return this._name; }

  // 特权方法
  sendMoneyTo(target: PublicAccount, amount: number): void {
    if (this._balance >= amount) {
      this._balance -= amount;
      (target as Account)._balance += amount;
    }
  }
}

class Bank {
  private accounts: Map<string, PrivilegedAccount> = new Map();

  createAccount(name: string, initialBalance: number): PublicAccount {
    const account = new Account(name, initialBalance);
    this.accounts.set(name, account);
    // 返回公共接口,外部无法调用sendMoneyTo
    return account;
  }

  transfer(fromName: string, toName: string, amount: number): void {
    const from = this.accounts.get(fromName);
    const to = this.accounts.get(toName);
    if (from && to) {
      from.sendMoneyTo(to, amount);
    }
  }
}

外部代码只能拿到PublicAccount接口,无法直接调用sendMoneyTo,只有Bank持有PrivilegedAccount接口实例才能操作。

2. 权限令牌模式

创建一个仅授权类能生成的权限令牌,特权方法必须传入该令牌才能执行,避免简单的布尔值验证被伪造。核心是令牌的构造函数私有,仅授权类能创建:

// 权限令牌:仅Treasury能生成
class MintAuthorizationToken {
  private constructor() {}
  // 仅授权类可调用的创建方法
  static create(): MintAuthorizationToken {
    return new MintAuthorizationToken();
  }
}

class Treasury {
  // ... 其他代码
  requestLoan(amount: number, borrower: PersonOrEntity): MoneyBag {
    this.loans.set(borrower.id, amount);
    // 生成令牌并传入mint
    const token = MintAuthorizationToken.create();
    return MoneyBag.mint(amount, token);
  }
}

class MoneyBag {
  // ... 其他代码
  static mint(amount: number, token: MintAuthorizationToken): MoneyBag {
    // 验证令牌有效性(只要存在就有效,因为外部无法创建)
    if (!token) throw new Error("not authorized");
    const bag = new MoneyBag();
    bag._balance = amount;
    return bag;
  }
}

这种方式比isTreasury字段更安全,因为外部无法伪造令牌实例。

3. 模块/闭包级封装

将授权类和被访问类放在同一模块或闭包内,利用语言的作用域规则隐藏特权方法,仅向授权类暴露访问入口。比如在TypeScript中用命名空间封装:

namespace BankingSystem {
  export class MoneyBag {
    private _balance: number = 0;
    constructor() {}
    get balance(): number { return this._balance; }

    // 私有方法:仅同一命名空间内的Treasury可访问
    private setBalance(amount: number): void {
      this._balance = amount;
    }

    // ... 其他公共方法
  }

  export class Treasury {
    // ... 其他代码
    requestLoan(amount: number, borrower: PersonOrEntity): MoneyBag {
      this.loans.set(borrower.id, amount);
      const bag = new MoneyBag();
      // 直接调用私有方法设置余额
      bag.setBalance(amount);
      return bag;
    }
  }
}

// 外部只能访问export的类,无法调用MoneyBag的setBalance
const T = new BankingSystem.Treasury("USA");
const bag = T.requestLoan(100, ...);

针对你的MoneyBag示例的优化方案

方案1:移除requestor参数,改用权限令牌(推荐)

替换原有的isTreasury验证,用令牌确保只有Treasury能调用mint:

interface PersonOrEntity {
  id: string;
}

// 新增权限令牌类
class MintToken {
  private constructor() {}
  static create(): MintToken {
    return new MintToken();
  }
}

class Treasury implements PersonOrEntity {
  readonly id: string;
  private loans: Map<string, number> = new Map();

  constructor(id: string) { this.id = id; }

  requestLoan(amount: number, borrower: PersonOrEntity): MoneyBag {
    this.loans.set(borrower.id, amount);
    // 生成令牌
    const token = MintToken.create();
    return MoneyBag.mint(amount, token);
  }
}

class Person implements PersonOrEntity {
  readonly id: string;
  private bag: MoneyBag = new MoneyBag();

  constructor(id: string) { this.id = id; }
  get balance(): number { return this.bag.balance; }
  addBag(newBag: MoneyBag) { this.bag.add(newBag); }
  lend(amount: number): MoneyBag {
    if (amount > this.bag.balance) throw new Error("insufficient funds");
    return this.bag.split(amount);
  }
}

class MoneyBag {
  private _balance: number = 0;
  constructor() {}
  get balance(): number { return this._balance; }

  // 仅接受MintToken作为权限验证
  static mint(amount: number, token: MintToken): MoneyBag {
    if (!token) throw new Error("not authorized");
    const bag = new MoneyBag();
    bag._balance = amount;
    return bag;
  }

  add(bag: MoneyBag) {
    this._balance += bag.balance;
    bag._balance = 0;
  }

  split(amount: number): MoneyBag {
    if (this.balance < amount) throw new Error("Insufficient funds");
    const newBag = new MoneyBag();
    newBag._balance = amount;
    this._balance -= amount;
    return newBag;
  }
}

// 测试代码不变
let T = new Treasury("USA");
let steve = new Person("Steve");
let jane = new Person("Jane");
steve.addBag(T.requestLoan(100, steve));
jane.addBag(steve.lend(50));
console.log(steve.balance, jane.balance); // 输出 50 50

方案2:将mint逻辑内聚到Treasury,隐藏MoneyBag的私有操作

把mint的逻辑移到Treasury内部,让MoneyBag的构造函数和余额设置仅对Treasury开放:

interface PersonOrEntity {
  id: string;
}

class Treasury implements PersonOrEntity {
  readonly id: string;
  private loans: Map<string, number> = new Map();

  constructor(id: string) { this.id = id; }

  requestLoan(amount: number, borrower: PersonOrEntity): MoneyBag {
    this.loans.set(borrower.id, amount);
    // 直接创建MoneyBag并设置余额
    return MoneyBag.createWithBalance(amount);
  }
}

class Person implements PersonOrEntity {
  // 代码不变
}

class MoneyBag {
  private _balance: number = 0;
  // 私有构造函数:外部无法直接创建
  private constructor() {}

  // 仅Treasury可调用的创建方法(同一模块内可见)
  static createWithBalance(amount: number): MoneyBag {
    const bag = new MoneyBag();
    bag._balance = amount;
    return bag;
  }

  get balance(): number { return this._balance; }
  // 其他公共方法不变
}

这种方式完全隐藏了mint的实现,外部甚至不知道有这个操作,所有货币创建逻辑都由Treasury控制。


内容的提问来源于stack exchange,提问作者GGizmos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 00:08:12