今日の役に立たない一言 - Today’s Trifle! -

古い記事ではさまざまなテーマを書いていますが、2007年以降はプログラミング関連の話がほとんどです。

パッケージ間の依存関係

satoshis2004-09-16

げんぢつとおひ中。。。
プロダクトに機能追加をしようとしている。プロダクトは汎用的なパッケージgenericと、プロダクト固有のクラスを含んでいるlocalパッケージで構成している。つまり、パッケージ間の依存は、local -> generic の単方向関連。genericパッケージにはProcessorクラスとTargetクラスがあり、ProcessorはTargetを変更する。

package generic;

public class Processor {
    public Target process(Target input) {
        :
        return output;
    }
}

localパッケージにはgeneric.Targetを継承したLocalTargetクラスがある。

package local;

import generic.Target;

public class LocalTargget extends Target {
    :
}

このとき、generic.Processor#process()でTargetのValidationをできるようにしたい要望が発生したとする。
オブジェクト指向を知らない人パッケージ間の依存性に配慮できない人に変更をまかせると、いとも簡単に generic -> local の関連を追加してくれる。

package generic;

import local.LocalTarget;

public class Processor {
    public Target process(Target input) {
        if (input instanceof LocalTarget) {
            :
        }
        :
        return output;
    }
}

こうしてしまうと、genericパッケージだけを generic.jar として他の場所で利用しようとしてもLocalTargetを参照するところでNoClassDefFoundErrorが発生するため利用できない。
このような場合は、Strategyパターンを使う。genericパッケージにValidatorインタフェースを追加する。

package generic;

public interface Validator {
    public boolean validate(Target target);
}

そしてProcessorクラスでValidatorを利用できるようにする。

package generic;

public class Processor {
    private Validator validator;

    public void setValidator(Validator validator) {
        this.validator = validator;
    }

    public Target process(Target input) {
        if (validator.validate(input) {
            // 加工する
            :
        }
        return output;
    }
}

localパッケージで好きなようにValidatorを作ればよい。

package local;

import generic.Target;
import generic.Validator;

public class LocalValidator implements Validator {
    public boolean validate(Target target) {
        if (target instanceof LocalTarget) {
             // Validation
        }
        return validationResult;
    }
}

今日の教訓:
コードにプログラマの意図をいくら反映させたとしても、意図を読み取れない人には無力である