callable 类

Dart的callable类的实例, 可以像函数一样被调用。听起来有点神秘,但一个示例即可揭开它的面纱。

// ex461.dart
class Greeter {
  String call(String name) => 'Hello, $name!'; // 1
}

void main() {
  var g = Greeter(); // 2
  print(g('World')); // 3 Output: Hello, World!
  print(g.call('Dart')); // 3a Hello, Dart!
}
  1. Greeter类中有一个名为 call 的实例方法;
  2. gGreeter的一个实例;
  3. g 当作函数来调用,实际上调用了实例方法 g.call

Greeter 定义了一个名为 call 实例方法,像这样的类即 callable 类,其实例为 callable 对象。换句话说, call 实例方法让一个类变得 callable。

除了call()实例方法外,callable类当然还能有其它的方法和字段。

// ex462.dart
class Greeter {
 String call(String name) => 'Hello, $name!'; // 1

 String hi(String name) => 'Hi, $name!'; // 2
}

void main() {
 var g = Greeter(); // 3
 print(g.hi('Auggie')); // 4 Output: Hi, Auggie!
}

此例中的callable类 Greeter 定义了一个普通的实例方法 hi,这样的方法也被称为命名函数

接下来看看callable类的应用场景。

有状态的函数

设想有一个函数,需要在多次调用之间维护其内部状态。比起使用全局变量或复杂的闭包,callable类显得更加简洁。

// ex463.dart
class Counter {
  int _count = 0; // 1
  int call() => ++_count; // 2
  void reset() => _count = 0; //3
}

void main() {
  final count = Counter();
  print(count()); // Output: 1
  print(count()); // Output: 2
  count.reset();
  print(count()); // Output: 1
}
  1. Counter类是一个计数器, 实例变量 _count 代表计数,是Counter的内部状态;
  2. call()方法每调用一次,_count 的值就加1并更新后的值;
  3. reset()方法将_count重置为0。

有状态的回调

事件处理函数或回调函数,有时需要管理与事件相关的状态,这是calllable类的另一个用武之地。

// ex464.dart
class Button {
  Function()? onClick; // 1
  void click() => onClick?.call(); // 2
}

class Callback {
  final String _context; // 3
  Callback(this._context); // 4
  void call() => print('$_context click'); // 5
}

void main() {
  var btn = Button();
  btn.onClick = Callback('Alice');
  btn.click(); // Output: Alice click

  btn.onClick = Callback('Bob');
  btn.click(); // Output: Bob click
}
  1. Button类表示图形界面上的按钮,当按钮被点击时,执行回调函数 onClick
  2. click 方法用来模拟按钮被按下,注意这里是如何调用 onClick 函数的(见call方法);
  3. Callback是一个callable类,代表回调函数,它有内部状态_context
  4. 这是Callback的构造函数;
  5. call()方法即回调函数的具体实现。

策略模式

callable类可用来实现设计模式里的策略模式

比如商场对不同等级的客户有不同的折扣。

classDiagram
  ShoppingCart o--> DiscountStrategy 

  DiscountStrategy <|-- VipDiscount
  DiscountStrategy <|-- RegularDiscount
  DiscountStrategy <|-- NoDiscount
  
  class ShoppingCart {
    - discountStrategy
    + double calculatePrice(double originalPrice)
  }
  
  class DiscountStrategy {
    <<interface>> 
    + double call(double originalPrice)
  }

// ex465.dart
abstract interface class DiscountStrategy {
  double call(double originalPrice); // 1
}

class VipDiscount implements DiscountStrategy {
  @override
  double call(double originalPrice) => originalPrice * 0.20; // 2
}

class RegularDiscount implements DiscountStrategy {
  @override
  double call(double originalPrice) => originalPrice * 0.10; // 2a
}

class NoDiscount implements DiscountStrategy {
  @override
  double call(double originalPrice) => 0.0; // 2b
}

class ShoppingCart {
  DiscountStrategy _discountStrategy; // 3

  ShoppingCart(this._discountStrategy);

  set discountStrategy(DiscountStrategy strategy) => // 3a
      _discountStrategy = strategy;

  double calculatePrice(double originalPrice) => // 4
      originalPrice - _discountStrategy(originalPrice);
}

void main() {
  const price = 100.0;
  final cart = ShoppingCart(RegularDiscount());
  print('Price with regular discount: \$${cart.calculatePrice(price)}');
  // Output: Price with regular discount: $90.0

  cart.discountStrategy = VipDiscount();
  print('Price with vip discount: \$${cart.calculatePrice(price)}');
  // Output: Price with vip discount: $80.0

  cart.discountStrategy = NoDiscount();
  print('Price with no discount: \$${cart.calculatePrice(price)}');
  // Output: Price with no discount: $100.0
}
  1. DiscountStrategy 是折扣策略的接口(interface);
  2. VipDiscountRegularDiscountNoDiscount是3个具体的折扣策略,它们都实现(implements)了DiscountStrategy ;
  3. ShoppingCart代表购物车,它聚合了一个 DiscountStrategy ;
  4. calculatePrice方法负责计算最终的价格(原价 - 折扣);