関数呼び出しを連続させる手法。 通常は、Function SequenceよりもObject Scopingを使うべき。 Function SequenceはDSLのエントリポイントとみなせる。
関数間のデータの関係は、Context Variablesを使って保持する。
Function Sequenceではグローバルな関数呼び出しを使うのが簡単だが、グローバルな関数群とstaticな解析データの2つの問題が生じる。
グローバルな関数はstatic importのような機能がないとDSLにノイズを加える。
staticなデータは特にマルチスレッドの環境で問題になる(どこから使われるているか確信できない)。
有効な解決策は代わりにObject Scopingを使うこと。
実はあまり役に立たない。Context Variablesを使うのは不恰好でコードを複雑にする。
複数の高レベルな文(statement)があり、そこからContext Variablesと単一の結果リストを求めるのであれば、それがFunction Sequenceを使用する機会。 言語のトップレベルやNested Closureの内部でのトップレベルにおいて使える。
Function SequenceはDSLのエントリポイントとみなせる。たとえ、呼び出しが1つしかなくても。
シンプルなFunction Sequenceの代替はLiteral List。
コードのインデントは必須ではない。
本当はObject Scopingを使うのがいいけど、ここではSingletonを使ってFunction Sequenceを実現する。 Singletonを使うことで、diskを保持するためのリストと2つのContext Variables(currentProcessorとcurrentDisk)を一緒に保持できる。 (これらをそれぞれstaticなフィールドに保持するよりも管理しやすくなる)
名前の衝突がいやなので、speedという関数名ではなくdiskSpeedという関数名にしている。
public class FunctionSequenceExample { public static void main(String[] args) { computer(); processor(); cores(2); processorType(i386); disk(); diskSize(150); disk(); diskSize(75); diskSpeed(7200); diskInterface(SATA); Computer computer = Builder.getComputer(); } } class Builder { private static int DEFAULT_CORES = 1; private static Builder instance; private Processor currentProcessor; private Disk currentDisk; private List<Disk> loadedDisks = new ArrayList<Disk>(); static void computer() { instance = new Builder(); } static void processor() { instance.currentProcessor = new Processor(DEFAULT_CORES, null); } static void cores(int arg) { instance.currentProcessor = new Processor(arg, instance.currentProcessor.getType()); } static void processorType(Processor.Type arg) { instance.currentProcessor = new Processor(instance.currentProcessor.getCores(), arg); } static void disk() { if (instance.currentDisk != null) { instance.loadedDisks.add(instance.currentDisk); } instance.currentDisk = new Disk(Disk.UNKNOWN_SIZE, Disk.UNKNOWN_SIZE, null); } static void diskSize(int arg) { instance.currentDisk = new Disk(arg, instance.currentDisk.getSpeed(), instance.currentDisk .getIface()); } static void diskSpeed(int arg) { instance.currentDisk = new Disk(instance.currentDisk.getSize(), arg, instance.currentDisk .getIface()); } static void diskInterface(Disk.Interface arg) { instance.currentDisk = new Disk(instance.currentDisk.getSize(), instance.currentDisk .getSpeed(), arg); } private Disk[] disks() { List<Disk> result = new ArrayList<Disk>(); result.addAll(loadedDisks); if (currentDisk != null) { result.add(currentDisk); } return result.toArray(new Disk[result.size()]); } public static Computer getComputer() { return instance.currentComputer(); } private Computer currentComputer() { return new Computer(currentProcessor, disks()); } } class Computer { private final Processor processor; private final Disk[] disks; public Computer(Processor processor, Disk[] disks) { this.processor = processor; this.disks = disks; } public Processor getProcessor() { return processor; } public Disk[] getDisks() { return disks; } } class Disk { public static final int UNKNOWN_SIZE = -1; private final int size; private final int speed; private final Interface iface; public Disk(int size, int speed, Interface iface) { this.size = size; this.speed = speed; this.iface = iface; } public int getSize() { return size; } public int getSpeed() { return speed; } public Interface getIface() { return iface; } public enum Interface { SATA, SAS } } class Processor { private final int cores; private final Type type; public Processor(int cores, Type type) { this.cores = cores; this.type = type; } public int getCores() { return cores; } public Type getType() { return type; } public enum Type { i386, i586, i686 } }
private String selectPersonSql() { BEGIN(); // Clears ThreadLocal variable SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME"); SELECT("P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON"); FROM("PERSON P"); FROM("ACCOUNT A"); INNER_JOIN("DEPARTMENT D on D.ID = P.DEPARTMENT_ID"); INNER_JOIN("COMPANY C on D.COMPANY_ID = C.ID"); WHERE("P.ID = A.ID"); WHERE("P.FIRST_NAME like ?"); OR(); WHERE("P.LAST_NAME like ?"); GROUP_BY("P.ID"); HAVING("P.LAST_NAME like ?"); OR(); HAVING("P.FIRST_NAME like ?"); ORDER_BY("P.ID"); ORDER_BY("P.FULL_NAME"); return SQL(); }