パッケージ間の依存関係
げんぢつとおひ中。。。
プロダクトに機能追加をしようとしている。プロダクトは汎用的なパッケージ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; } }
今日の教訓:
コードにプログラマの意図をいくら反映させたとしても、意図を読み取れない人には無力である