Help us understand the problem. What is going on with this article?

ES7.decorator で契約プログラミング(design by contract)

More than 5 years have passed since last update.

D言語の契約プログラミングサポートに触発された。

Dで実際どこまでできるかはあまりわかってないが、現実的にはこんな感じだろうか。

// $ babel-node --optional es7.decorators index.js
class Counter {
  constructor(initial = 0) {
    this.current = initial;
  }

  @contract
  up(i) {
    this.current += i;
    return this.current;
  }
}

function contract(tar, name, desc) {

  // 事前条件
  function before(i) {
    console.assert(i > 0, `arg for ${name} should be positive value`);
  }

  // 事後条件
  function after(result) {
    console.assert(result > 0, `result of ${name} should be positive value`);
  }

  // 不変条件
  function invariant(_this) {
    console.assert(_this.current > 0, 'current should be positive value');
  }

  let val = desc.value;
  desc.value = function(i) {

    before(i);
    invariant(this);

    let result = val.apply(this, arguments);

    invariant(this);
    after(result);

    return result;
  }
}

function main() {
  let counter = new Counter(10);
  console.log(counter.up(1));
}
main();

汎用化すると

function contract(rule) {
  return function(target, name, descriptor) {
    let rule = new Rule(target, name, descriptor);
    let original = descriptor.value;
    descriptor.value = function() {

      if (rule.before) rule.before.apply(rule, arguments);

      if (rule.invaliant) rule.invaliant(this);

      let result = original.apply(this, arguments);

      if (rule.invaliant) rule.invaliant(this);

      if (rule.after) rule.after(result);

      return result;
    }
  }
}

class Rule {
  constructor(target, name, descriptor) {
    this.target = target;
    this.name = name;
    this.descriptor = descriptor;
  }

  before(i) {
    console.log('before', i);
    console.assert(i > 0, `arg for ${this.name} should be positive value`);
  }

  after(result) {
    console.log('after', result);
    console.assert(result > 0, `result of ${this.name} should be positive value`);
  }

  invaliant(_this) {
    console.log('invaliant', _this);
    console.assert(_this.current > 0, '`current` should be positive value');
  }
}

function main() {

  class Counter {
    constructor(initial = 0) {
      this.current = initial;
    }

    @contract(Rule)
    up(i) {
      this.current += i;
      return this.current;
    }
  }

  let counter = new Counter(10);
  console.log(counter.up(2));
}

main();

D ではコンパイル時にこの辺は消す。Java も --ea を付けないと消える。
それをしたければ babel 時に @contract のデコレータを消して、
closure compiler みたいな「不使用コード削除」な minify とかをかませば消せる。

(しかし、動的で緩い型付けの JS みたいな言語では、実行時 assertion こそ残ってるベキなんじゃないかと思わなくも無い。)

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした