DSL
★Fluent interface pattern (内部DSL)
サンプル1
interface Start {
End singleWord();
End parameterisedWord(String parameter);
Intermediate1 word1();
Intermediate2 word2();
Intermediate3 word3();
}
interface End {
void end();
}
interface Intermediate1 extends End {
End optionalWord();
}
interface Intermediate2 {
End wordChoiceA();
End wordChoiceB();
}
interface Intermediate3 extends End {
Intermediate3 word3();
}
Start start = ...
start.singleWord().end();
start.parameterisedWord("xxx").end();
start.word1().end();
start.word1().optionalWord().end();
start.word2().wordChoiceA().end();
start.word2().wordChoiceB().end();
start.word3().end();
start.word3().word3().end();
start.word3().word3().word3().end();
サンプル2
Car.Builder()
.withBody(Body.Builder()
.withColor()
.withDoorCount()
.build())
.withEngine(Engine.Builder()
.withPower()
.build())
.withWheel(Wheel.Builder()
.withSize()
.build())
.build();
★Fluent API Design
Interface based version
Mock mock = new Mock();
mock.whenConnecting()
.expectValue("a").and()
.expectHeader("header").andRespondWithValue("b");
public interface RequestExpectations {
ResponseActions expectValue(String value);
ResponseActions expectHeader(String name);
}
public interface ResponseActions {
RequestExpectations and();
void andRespondWithValue(String value);
void andRespondWithError();
}
public class Mock {
public RequestExpectations whenConnecting() {
return new MockInternal();
}
}
class MockInternal implements RequestExpectations, ResponseActions{
public RequestExpectations and() {
...
return this;
}
public void andRespondWithValue(String value) { ... }
public void andRespondWithError() { ... }
public ResponseActions expectValue(String value) {
...
return this;
}
public ResponseActions expectHeader(String name) {
...
return this;
}
}
Static factory Version
Mock mock = new Mock();
mock.whenConnecting()
.expect(value("a")).and()
.expect(header("header")).andRespond(withValue("b"));
public interface RequestExpectations {
ResponseActions expect(RequestMatcher matcher);
}
public interface ResponseActions {
RequestExpectations and();
void andRespond(ResponseCallback callback);
}
public class Mock {
public RequestExpectations whenConnecting() {
return new MockInternal();
}
}
class MockInternal implements RequestExpectations, ResponseActions{
public RequestExpectations and() {
...
return this;
}
public void andRespond(ResponseCallback callback) { ... }
public ResponseActions expect(RequestMatcher matcher) {
...
return this;
}
}
public class StaticFactory {
public static RequestMatcher value(String value) {
return new RequestMatcher() {
public void match(Object someParams) { ... }
};
}
public static RequestMatcher header(String value) {
return new RequestMatcher() {
public void match(Object someParams) { ... }
};
}
public static ResponseCallback withValue(String value) {
return new ResponseCallback() {
public void doWithResponse(Object someParams) { ... }
};
}
public static ResponseCallback withError(String value) {
return new ResponseCallback() {
public void doWithResponse(Object someParams) { ... }
};
}
}
Super static version
expect(value("a")).andExpect(header("header")).andRespond(withValue("b"));
public interface ResponseActions {
ResponseActions andExpect(RequestMatcher matcher);
void andRespond(ResponseCallback callback);
}
class MockInternal implements ResponseActions{
public ResponseActions andExpect(RequestMatcher matcher) {
...
return this;
}
public void andRespond(ResponseCallback callback) { ... }
}
public class StaticFactory {
public static ResponseActions expect(RequestMatcher matcher) {
MockInternal mock = new MockInternal();
mock.andExpect(matcher);
return mock;
}
public static RequestMatcher value(String value) {
return new RequestMatcher() {
public void match(Object someParams) { ... }
};
}
...
}
Inheritance version
public class MockTest extends MockFactory{
@Test
public void testMock() {
expect(value("a"))
.andExpect(header("header")).andRespond(withValue("b"));
}
}
あるいは
public class MockTest{
@Test
public void testMock() {
MockFactory mockFactory = new MockFactory() {
protected void configure() {
expect(value("a"))
.andExpect(header("header")).andRespond(withValue("b"));
};
};
}
}
public interface ResponseActions {
ResponseActions andExpect(RequestMatcher matcher);
void andRespond(ResponseCallback callback);
}
class MockInternal implements ResponseActions{
public ResponseActions andExpect(RequestMatcher matcher) {
...
return this;
}
public void andRespond(ResponseCallback callback) { ... }
}
public class MockFactory {
public MockFactory() {
configure();
}
protected void configure() {
}
public ResponseActions expect(RequestMatcher matcher) {
MockInternal mock = new MockInternal();
mock.andExpect(matcher);
return mock;
}
public RequestMatcher value(String value) {
return new RequestMatcher() {
public void match(Object someParams) { ... }
};
}
...
}
★実際の応用
jOOQ
create().select(
r1.ROUTINE_NAME,
r1.SPECIFIC_NAME,
decode()
.when(exists(create()
.selectOne()
.from(PARAMETERS)
.where(PARAMETERS.SPECIFIC_SCHEMA.equal(r1.SPECIFIC_SCHEMA))
.and(PARAMETERS.SPECIFIC_NAME.equal(r1.SPECIFIC_NAME))
.and(upper(PARAMETERS.PARAMETER_MODE).notEqual("IN"))),
val("void"))
.otherwise(r1.DATA_TYPE).as("data_type"),
r1.NUMERIC_PRECISION,
r1.NUMERIC_SCALE,
r1.TYPE_UDT_NAME,
decode().when(
exists(
create().selectOne()
.from(r2)
.where(r2.ROUTINE_SCHEMA.equal(getSchemaName()))
.and(r2.ROUTINE_NAME.equal(r1.ROUTINE_NAME))
.and(r2.SPECIFIC_NAME.notEqual(r1.SPECIFIC_NAME))),
create().select(count())
.from(r2)
.where(r2.ROUTINE_SCHEMA.equal(getSchemaName()))
.and(r2.ROUTINE_NAME.equal(r1.ROUTINE_NAME))
.and(r2.SPECIFIC_NAME.lessOrEqual(r1.SPECIFIC_NAME)).asField())
.as("overload"))
.from(r1)
.where(r1.ROUTINE_SCHEMA.equal(getSchemaName()))
.orderBy(r1.ROUTINE_NAME.asc())
.fetch();
SQL vs jOOQ
SELECT FIRST_NAME, LAST_NAME, COUNT(*)
FROM AUTHOR
JOIN BOOK ON AUTHOR.ID = BOOK.AUTHOR_ID
WHERE LANGUAGE = 'DE'
AND PUBLISHED > '2014-04-20'
GROUP BY FIRST_NAME, LAST_NAME
HAVING COUNT(*) > 5
ORDER BY LAST_NAME ASC NULLS FIRST
LIMIT 2
OFFSET 1
FOR UPDATE
OF FIRST_NAME, LAST_NAME
create.select(FIRST_NAME, LAST_NAME, create.count())
.from(AUTHOR)
.join(BOOK).on(Author.ID.equal(Book.AUTHOR_ID))
.where(LANGUAGE.equal("DE"))
.and(PUBLISHED.greaterThan(parseDate('2014-04-20')))
.groupBy(FIRST_NAME, LAST_NAME)
.having(create.count().greaterThan(5))
.orderBy(LAST_NAME.asc().nullsFirst())
.limit(2)
.offset(1)
.forUpdate()
.of(FIRST_NAME, LAST_NAME);
jRTF
https://code.google.com/p/jrtf/
rtf()
.header(
color( 0xff, 0, 0 ).at( 0 ),
color( 0, 0xff, 0 ).at( 1 ),
color( 0, 0, 0xff ).at( 2 ),
font( "Calibri" ).at( 0 ) )
.section(
p( font( 1, "Second paragraph" ) ),
p( color( 1, "green" ) )
)
).out( out );
Apache Camel
http://sourceforge.jp/projects/cameluserjp/wiki/FrontPage
RouteBuilder builder = new RouteBuilder() {
public void configure() {
errorHandler(deadLetterChannel("mock:error"));
from("seda:a")
.choice()
.when(header("foo").isEqualTo("xxx"))
.to("seda:b")
.when(header("foo").isEqualTo("yyy"))
.to("seda:c")
.otherwise()
.to("seda:d");
}
};