API Design Done Right: Some Guidelines Which Every Programmer Should Probably Know
API Design Done Right: Some Guidelines Which Every Programmer Should Probably Know
Confidential
Usually write-once,
read/learn many times
by many different people
http://www.flickr.com/photos/bamshad/1469713774/sizes/s/in/photostream/
Good API
Intuitive
Easy to learn from simple examples
Self explanatory
Effort minimizing
Levels of abstraction
JDBC API
public int countBigCustomers() {
Connection connection = null;
try {
connection = DriverManager.getConnection
("jdbc:h2:mem:");
PreparedStatement statement = connection
.prepareStatement("SELECT COUNT(*)
FROM CUSTOMERS WHERE REVENUE > ?");
// Do indexes start from 0 or 1?
statement.setLong(1, 1000000L);
ResultSet resultSet = statement.executeQuery();
resultSet.next();
// Do indexes start from 0 or 1?
int result = resultSet.getInt(1);
return result;
} catch (SQLException e) {
// Checked exception - ouch!
throw new RuntimeException(e);
} finally {
try {
if (connection != null && !
connection.isClosed()) {
connection.close();
}
} catch (SQLException e) {
// Checked exception - aargh
throw new RuntimeException(e);
}
}
}
Spring API
Levels of abstraction
@Before
public void setUp() {
// API as an user interface - how user of the API would
an order
order = new OrderBuilder(CustomerId.FANBOY_CUSTOMER)
.with(ProductId.JPHONE_6, 4)
.with(ProductId.CONSTELLATION_TABLET, 2)
.build();
}
@Test
public void orderHasTwoRows() {
assertThat(order.orderRows().size(), is(2));
}
Simplicity
Object initialization
An object should be in valid, usable state
immediately.
BAD!!!
BETTER
Object initialization
Use builders or factories for more complex
initializations
Copyright Reaktor 2011
Monday, September 12, 2011
// default visibility
// we can only create objects with valid state
Order(CustomerId customerId, List<OrderRow> orderRows) {
this.customerId = customerId;
this.orderRows = Collections.unmodifiableList(orderRows);
}
Object state
It should be impossible to get an object in an
illegal state.
Object should be in a valid state between
any method calls.
Fail fast if the method call would leave the
object in an invalid state.
Object state
Prefer immutability
public class Order {
Atomic operations
// User must explicitly begin transaction...
c.beginTransaction();
// ...do their stuff...
c.doSomething();
// ...and remember to commit...
c.commit();
// ...but what if something fails in between?
c.doInTransaction(new Callback() {
@Override
public void execute(Component c) {
// Method doInTransaction() takes
// care of transaction management
// and error handling
c.doSomething();
}
});
BAD!!!
BETTER
Joda-Time
public DateTime twoWeeksFrom(DateTime beginning) {
return beginning.plusWeeks(2);
}
Thank you!
Jari Mkel <jari.makela@reaktor.fi>