2010-05-18

動的言語は引数チェックが面倒

例えばPHPで

  1. PHPのImage関数のラッパークラスをつくる必要がある
  2. imageline()関数をラップして線を引くメソッドをつくる必要がある
  3. 線を引くメソッドはdrawLine(int $x1, int $y1, int $x2, int $y2, int $color)としたい
  4. drawLine(Point $p1, Point $p2, int $color)ともできるようにしたい (Pointはxとyをフィールドとして持つクラス)

と考えたとき、もし仮にPHPにオーバーロード(PHP: オーバーロード - Manualじゃなく一般的な意味でのオーバーロード)があればdrawLine()は

class Image
{
  protected $id; // 画像リソースID

// コンストラクタ等省略...

  public function drawLine($x1, $y1, $x2, $y2, $color)
  {
    return imageline($this->id, $x1, $y1, $x2, $y2, $color);
  }
  public function drawLine($p1, $p2, $color)
  {
    return imageline($this->id, $p1->x, $p1->y, $p2->x, $p2->y, $color);
  }
}

というような形で上手く収まるんですが、PHPのような動的言語はオーバーロードができないので引数の数や型を自力で調べて適切に例外を出したり…というとても面倒な事になります。

引数の数をチェックするだけにしても

class Image
{
  protected $id; // 画像リソースID

// コンストラクタ等省略...

  public function drawLine()
  {
    // 引数の数をチェックし、
    // 数が5ならdrawLine(int $x1, int $y1, int $x2, int $y2, int $color)、
    // 数が3ならdrawLine(Point $p1, Point $p2, int $color)とみなします。
    $args = func_get_args();
    switch (count($args)) {
    case 5:
      list($x1, $y1, $x2, $y2, $color) = $args;
      break;
    case 3:
      list($p1, $p2, $color) = $args;
      list($x1, $y1, $x2, $y2) = array($p1->x, $p1->y, $p2->x, $p2->y);
      break;
    default:
      // argument exception...
    }

    return imageline($this->id, $x1, $y1, $x2, $y2, $color);
  }
}

のようにどうにもすっきりしません。これが例えば

  1. 線を引くメソッドはdrawLine(int $x1, int $y1, int $x2, int $y2)としたい
  2. drawLine(int $x1, int $y1, int $x2, int $y2, int $color)ともできるようにしたい

だけであれば、PHPにはデフォルト引数があるのでそれを使って

class Image
{
  protected $id; // 画像リソースID

// コンストラクタ等省略...

  public function drawLine($x1, $y1, $x2, $y2, $color=0x000000)
  {
    return imageline($this->id, $x1, $y1, $x2, $y2, $color);
  }
}

とするだけでいいんですが…。こういう場合は静的言語の方が親切ですよね。

追記(6月1日)

もとの話とはそれるんですが、GIFはインデックスカラーなので上のコードのように$colorにRGB値をそのまま入れてもだめで、ちゃんとimagecolorallocate()等で割り当てしたインデックス値を入れないといけませんでした。サボったらだめですね。