SlideShare une entreprise Scribd logo
1  sur  92
Télécharger pour lire hors ligne
Let's make a contract:
the art of designing a
Java API
by Mario Fusco
mario.fusco@gmail.com
@mariofusco
What is an API?
What is an API?
An API is what a
developer uses to
achieve some task
What is an API?
What is an API?
An API is a contract between
its implementors and its users
And why should I care?
We are all API designers
Our software doesn't work in
isolation, but becomes
useful only when it interacts
with other software written
by other developers
Basic Principles
●
Intuitive
●
Understandable
●
Learnable
●
Discoverable
●
Consistent
●
Self-defensive
●
Concise
●
Easy to use
●
Minimal
●
Orthogonal
●
Idiomatic
●
Flexible
●
Evolvable
●
Well documented
●
Right level of abstraction
●
Correct use of the type system
●
Limited number of entry-points
●
Respect the principle of least astonishment
Be ready for changes
Be ready for changes
Software being 'done' is
like lawn being 'mowed'
– Jim Benson
Change is the only constant in software
Add features sparingly and carefully
so that they won’t become obstacles
for the evolution of your API
If in doubt, leave it out
A feature that only takes a few hours to
be implemented can
➢
create hundreds of hours of support
and maintenance in future
➢
bloat your software and confuse
your users
➢
become a burden and prevent
future improvements
“it’s easy to build” is NOT a good
enough reason to add it to your product
Best Practices
&
Practical Hints
Write meaningful Javadocs
Write meaningful Javadocs
Write meaningful Javadocs
Write meaningful Javadocs
Convenience methods
Use overloading judiciously
and sparingly
Convenience methods
Use overloading judiciously
and sparingly
Primitives often cause
methods proliferation
{
Convenience methods
Use overloading judiciously
and sparingly
Are these all
necessary?
Primitives often cause
methods proliferation
{
Convenience methods
public interface StockOrder {
void sell(String symbol, double price, int quantity);
void buy(String symbol, int quantity, double price);
void buy(String symbol, int quantity, double price, double commission);
void buy(String symbol, int quantity, double minPrice, double maxPrice, double commission);
}
What’s wrong with this?
Convenience methods
public interface StockOrder {
void sell(String symbol, double price, int quantity);
void buy(String symbol, int quantity, double price);
void buy(String symbol, int quantity, double price, double commission);
void buy(String symbol, int quantity, double minPrice, double maxPrice, double commission);
}
Too many overloads
What’s wrong with this?
Convenience methods
public interface StockOrder {
void sell(String symbol, double price, int quantity);
void buy(String symbol, int quantity, double price);
void buy(String symbol, int quantity, double price, double commission);
void buy(String symbol, int quantity, double minPrice, double maxPrice, double commission);
}
Too many overloads
Inconsistent argument order
What’s wrong with this?
Convenience methods
Long arguments lists (especially of same type)
public interface StockOrder {
void sell(String symbol, double price, int quantity);
void buy(String symbol, int quantity, double price);
void buy(String symbol, int quantity, double price, double commission);
void buy(String symbol, int quantity, double minPrice, double maxPrice, double commission);
}
Too many overloads
Inconsistent argument order
What’s wrong with this?
Convenience methods
Long arguments lists (especially of same type)
public interface StockOrder {
void sell(String symbol, double price, int quantity);
void buy(String symbol, int quantity, double price);
void buy(String symbol, int quantity, double price, double commission);
void buy(String symbol, int quantity, double minPrice, double maxPrice, double commission);
}
public interface StockOrder {
void sell(String symbol, int quantity, Price price);
void buy(String symbol, int quantity, Price price);
}
Too many overloads
Inconsistent argument order
What’s wrong with this?
How to do better
Consider static
factories
public interface Price {
static Price price( double price ) {
if (price < 0) return Malformed.INSTANCE;
return new Fixed(price);
}
static Price price( double minPrice, double maxPrice ) {
if (minPrice > maxPrice) return Malformed.INSTANCE;
return new Range(minPrice, maxPrice);
}
class Fixed implements Price {
private final double price;
private Fixed( double price ) {
this.price = price;
}
}
class Range implements Price {
private final double minPrice;
private final double maxPrice;
private Range( double minPrice, double maxPrice ) {
this.minPrice = minPrice;
this.maxPrice = maxPrice;
}
}
enum Malformed implements Price { INSTANCE }
}
➢
nicer syntax for users
(no need of new
keyword)
Consider static
factories
public interface Price {
static Price price( double price ) {
if (price < 0) return Malformed.INSTANCE;
return new Fixed(price);
}
static Price price( double minPrice, double maxPrice ) {
if (minPrice > maxPrice) return Malformed.INSTANCE;
return new Range(minPrice, maxPrice);
}
class Fixed implements Price {
private final double price;
private Fixed( double price ) {
this.price = price;
}
}
class Range implements Price {
private final double minPrice;
private final double maxPrice;
private Range( double minPrice, double maxPrice ) {
this.minPrice = minPrice;
this.maxPrice = maxPrice;
}
}
enum Malformed implements Price { INSTANCE }
}
➢
nicer syntax for users
(no need of new
keyword)
➢
can return different
subclasses
Consider static
factories
public interface Price {
static Price price( double price ) {
if (price < 0) return Malformed.INSTANCE;
return new Fixed(price);
}
static Price price( double minPrice, double maxPrice ) {
if (minPrice > maxPrice) return Malformed.INSTANCE;
return new Range(minPrice, maxPrice);
}
class Fixed implements Price {
private final double price;
private Fixed( double price ) {
this.price = price;
}
}
class Range implements Price {
private final double minPrice;
private final double maxPrice;
private Range( double minPrice, double maxPrice ) {
this.minPrice = minPrice;
this.maxPrice = maxPrice;
}
}
enum Malformed implements Price { INSTANCE }
}
➢
nicer syntax for users
(no need of new
keyword)
➢
can return different
subclasses
➢
can check
preconditions and
edge cases returning
different
implementations
accordingly
Promote fluent API
public interface Price {
Price withCommission(double commission);
Price gross();
}
public interface Price {
void setCommission(double commission);
void setGross();
}
Promote fluent API
public interface Price {
Price withCommission(double commission);
Price gross();
}
stockOrder.buy( "IBM", 100, price(150.0).withCommission(0.7).gross() );
public interface Price {
void setCommission(double commission);
void setGross();
}
Price price = price(150.0);
price.setCommission(0.7);
price.setGross();
stockOrder.buy( "IBM", 100, price );
Promote fluent API
public interface Price {
Price withCommission(double commission);
Price gross();
}
stockOrder.buy( "IBM", 100, price(150.0).withCommission(0.7).gross() );
public interface Price {
void setCommission(double commission);
void setGross();
}
Price price = price(150.0);
price.setCommission(0.7);
price.setGross();
stockOrder.buy( "IBM", 100, price );
Concatenate multiple invocations
Use result directly
Promote fluent API
Streams are a very
nice and convenient
example of fluent API
Promote fluent API
Streams are a very
nice and convenient
example of fluent API
Promote fluent API
Name consistency???
Streams are a very
nice and convenient
example of fluent API
Use the weakest possible type
public String concatenate( ArrayList<String> strings ) {
StringBuilder sb = new StringBuilder();
for (String s : strings) {
sb.append( s );
}
return sb.toString();
}
Use the weakest possible type
public String concatenate( ArrayList<String> strings ) {
StringBuilder sb = new StringBuilder();
for (String s : strings) {
sb.append( s );
}
return sb.toString();
}
Do I care of the actual
List implementation?
Use the weakest possible type
public String concatenate( List<String> strings ) {
StringBuilder sb = new StringBuilder();
for (String s : strings) {
sb.append( s );
}
return sb.toString();
}
Use the weakest possible type
public String concatenate( List<String> strings ) {
StringBuilder sb = new StringBuilder();
for (String s : strings) {
sb.append( s );
}
return sb.toString();
}
Do I care of the
elements’ order?
Use the weakest possible type
public String concatenate( Collection<String> strings ) {
StringBuilder sb = new StringBuilder();
for (String s : strings) {
sb.append( s );
}
return sb.toString();
}
Use the weakest possible type
public String concatenate( Collection<String> strings ) {
StringBuilder sb = new StringBuilder();
for (String s : strings) {
sb.append( s );
}
return sb.toString();
}
Do I care of the
Collection’s size?
Use the weakest possible type
public String concatenate( Iterable<String> strings ) {
StringBuilder sb = new StringBuilder();
for (String s : strings) {
sb.append( s );
}
return sb.toString();
}
Using the weakest possible type...
public String concatenate( Iterable<String> strings ) {
StringBuilder sb = new StringBuilder();
for (String s : strings) {
sb.append( s );
}
return sb.toString();
}
… enlarges the applicability of your method, avoiding to restrict your client
to a particular implementation or forcing it to perform an unnecessary and
potentially expensive copy operation if the input data exists in other forms
Use the weakest possible type
also for returned value
public List<Address> getFamilyAddresses( Person person ) {
List<Address> addresses = new ArrayList<>();
addresses.add(person.getAddress());
for (Person sibling : person.getSiblings()) {
addresses.add(sibling.getAddress());
}
return addresses;
}
Use the weakest possible type
also for returned value
public List<Address> getFamilyAddresses( Person person ) {
List<Address> addresses = new ArrayList<>();
addresses.add(person.getAddress());
for (Person sibling : person.getSiblings()) {
addresses.add(sibling.getAddress());
}
return addresses;
}
Is the order of this List
meaningful for client?
Use the weakest possible type
also for returned value
public List<Address> getFamilyAddresses( Person person ) {
List<Address> addresses = new ArrayList<>();
addresses.add(person.getAddress());
for (Person sibling : person.getSiblings()) {
addresses.add(sibling.getAddress());
}
return addresses;
}
Is the order of this List
meaningful for client?
… and shouldn’t we maybe return only
the distinct addresses?
Yeah, that will be easy let’s do this!
Use the weakest possible type
also for returned value
public List<Address> getFamilyAddresses( Person person ) {
Set<Address> addresses = new HashSet<>();
addresses.add(person.getAddress());
for (Person sibling : person.getSiblings()) {
addresses.add(sibling.getAddress());
}
return addresses;
}
It should be enough to
change this List into a Set
Use the weakest possible type
also for returned value
public List<Address> getFamilyAddresses( Person person ) {
Set<Address> addresses = new HashSet<>();
addresses.add(person.getAddress());
for (Person sibling : person.getSiblings()) {
addresses.add(sibling.getAddress());
}
return addresses;
}
It should be enough to
change this List into a Set
But this doesn’t
compile :(
Use the weakest possible type
also for returned value
public List<Address> getFamilyAddresses( Person person ) {
Set<Address> addresses = new HashSet<>();
addresses.add(person.getAddress());
for (Person sibling : person.getSiblings()) {
addresses.add(sibling.getAddress());
}
return addresses;
}
It should be enough to
change this List into a Set
But this doesn’t
compile :(
and I cannot change the returned type to
avoid breaking backward compatibility :(((
Use the weakest possible type
also for returned value
public List<Address> getFamilyAddresses( Person person ) {
Set<Address> addresses = new HashSet<>();
addresses.add(person.getAddress());
for (Person sibling : person.getSiblings()) {
addresses.add(sibling.getAddress());
}
return new ArrayList<>( addresses );
}
I’m obliged to uselessly create an expensive
copy of data before returning them
Use the weakest possible type
also for returned value
public Collection<Address> getFamilyAddresses( Person person ) {
List<Address> addresses = new ArrayList<>();
addresses.add(person.getAddress());
for (Person sibling : person.getSiblings()) {
addresses.add(sibling.getAddress());
}
return addresses;
}
Returning a more generic type (if this is acceptable
for your client) provides better flexibility in future
Support lambdas
public interface Listener {
void beforeEvent(Event e);
void afterEvent(Event e);
}
class EventProducer {
public void registerListener(Listener listener) {
// register listener
}
}
public interface Listener {
void beforeEvent(Event e);
void afterEvent(Event e);
}
public interface Listener {
void beforeEvent(Event e);
void afterEvent(Event e);
}
EventProducer producer = new EventProducer();
producer.registerListener( new Listener() {
@Override
public void beforeEvent( Event e ) {
// ignore
}
@Override
public void afterEvent( Event e ) {
System.out.println(e);
}
} );
Support lambdas
class EventProducer {
public void registerBefore(BeforeListener before) {
// register listener
}
public void registerAfter(AfterListener after) {
// register listener
}
}
@FunctionalInterface
interface BeforeListener {
void beforeEvent( Event e );
}
@FunctionalInterface
interface AfterListener {
void afterEvent( Event e );
}
EventProducer producer = new EventProducer();
producer.registerAfter( System.out::println );
Taking functional interfaces as
argument of your API enables
clients to use lambdas
Support lambdas
class EventProducer {
public void registerBefore(Consumer<Event> before) {
// register listener
}
public void registerAfter(Consumer<Event> after) {
// register listener
}
}
@FunctionalInterface
interface BeforeListener {
void beforeEvent( Event e );
}
@FunctionalInterface
interface AfterListener {
void afterEvent( Event e );
}
EventProducer producer = new EventProducer();
producer.registerAfter( System.out::println );
Taking functional interfaces as
argument of your API enables
clients to use lambdas
In many cases you don’t need
to define your own functional
interfaces and use Java’s one
public void writeList( Writer writer, Collection<String> strings ) {
strings.stream().forEach( writer::write );
}
Writer writer = new StringWriter();
List<String> strings = asList("one", "two", "three");
writeList( writer, strings );
Avoid checked exceptions
Avoid checked exceptions
public void writeList( Writer writer, Collection<String> strings ) {
strings.stream().forEach( writer::write );
}
Writer writer = new StringWriter();
List<String> strings = asList("one", "two", "three");
writeList( writer, strings );
public void writeList( Writer writer, Collection<String> strings ) {
strings.stream().forEach( str -> {
try {
writer.write( str );
} catch (IOException e) {
throw new RuntimeException( e );
}
} );
}
Avoid checked exceptions
Useful error
management
or
Wasteful
bloatware
?
Stay in control (loan pattern)
public byte[] readFile(String filename) throws IOException {
FileInputStream file = new FileInputStream( filename );
byte[] buffer = new byte[4096];
ByteArrayOutputStream out = new ByteArrayOutputStream( buffer.length );
int n = 0;
while ( (n = file.read( buffer )) > 0 ) {
out.write( buffer, 0, n );
}
return out.toByteArray();
}
Stay in control (loan pattern)
public byte[] readFile(String filename) throws IOException {
FileInputStream file = new FileInputStream( filename );
byte[] buffer = new byte[4096];
ByteArrayOutputStream out = new ByteArrayOutputStream( buffer.length );
int n = 0;
while ( (n = file.read( buffer )) > 0 ) {
out.write( buffer, 0, n );
}
return out.toByteArray();
}
File descriptor leak
Stay in control (loan pattern)
public byte[] readFile(String filename) throws IOException {
FileInputStream file = new FileInputStream( filename );
try {
byte[] buffer = new byte[4096];
ByteArrayOutputStream out = new ByteArrayOutputStream( buffer.length );
int n = 0;
while ( (n = file.read( buffer )) > 0 ) {
out.write( buffer, 0, n );
}
return out.toByteArray();
} finally {
file.close();
}
}
Stay in control (loan pattern)
public byte[] readFile(String filename) throws IOException {
FileInputStream file = new FileInputStream( filename );
try {
byte[] buffer = new byte[4096];
ByteArrayOutputStream out = new ByteArrayOutputStream( buffer.length );
int n = 0;
while ( (n = file.read( buffer )) > 0 ) {
out.write( buffer, 0, n );
}
return out.toByteArray();
} finally {
file.close();
}
}
We can do better using
try-with-resource
Stay in control (loan pattern)
public byte[] readFile(String filename) throws IOException {
try ( FileInputStream file = new FileInputStream( filename ) ) {
byte[] buffer = new byte[4096];
ByteArrayOutputStream out = new ByteArrayOutputStream( buffer.length );
int n = 0;
while ( (n = file.read( buffer )) > 0 ) {
out.write( buffer, 0, n );
}
return out.toByteArray();
}
}
Stay in control (loan pattern)
public byte[] readFile(String filename) throws IOException {
try ( FileInputStream file = new FileInputStream( filename ) ) {
byte[] buffer = new byte[4096];
ByteArrayOutputStream out = new ByteArrayOutputStream( buffer.length );
int n = 0;
while ( (n = file.read( buffer )) > 0 ) {
out.write( buffer, 0, n );
}
return out.toByteArray();
}
}
Better, but we’re
still transferring
to our users the
burden to use
our API correctly
Stay in control (loan pattern)
public byte[] readFile(String filename) throws IOException {
try ( FileInputStream file = new FileInputStream( filename ) ) {
byte[] buffer = new byte[4096];
ByteArrayOutputStream out = new ByteArrayOutputStream( buffer.length );
int n = 0;
while ( (n = file.read( buffer )) > 0 ) {
out.write( buffer, 0, n );
}
return out.toByteArray();
}
}
Better, but we’re
still transferring
to our users the
burden to use
our API correctly
That’s a leaky abstraction!
Stay in control (loan pattern)
public static <T> T withFile( String filename,
ThrowingFunction<FileInputStream, T> consumer ) throws IOException {
try ( FileInputStream file = new FileInputStream( filename ) ) {
return consumer.apply( file );
}
}
@FunctionalInterface
public interface ThrowingFunction<T, R> {
R apply(T t) throws IOException;
}
Yeah, checked
exceptions
suck :(
Stay in control (loan pattern)
public byte[] readFile(String filename) throws IOException {
return withFile( filename, file -> {
byte[] buffer = new byte[4096];
ByteArrayOutputStream out = new ByteArrayOutputStream( buffer.length );
int n = 0;
while ( (n = file.read( buffer )) > 0 ) {
out.write( buffer, 0, n );
}
return out.toByteArray();
});
}
Now the
responsibility of
avoiding the leak
is encapsulated
in our API
If clients are
forced to use this
API no leak is
possible at all!
Break apart large interfaces into
smaller versions
public interface RuleEngineServices {
Resource newUrlResource( URL url );
Resource newByteArrayResource( byte[] bytes );
Resource newFileSystemResource( File file );
Resource newInputStreamResource( InputStream stream );
Resource newClassPathResource( String path );
void addModule( Module kModule );
Module getModule( ReleaseId releaseId );
Module removeModule( ReleaseId releaseId );
Command newInsert( Object object );
Command newModify( FactHandle factHandle );
Command newDelete( FactHandle factHandle );
Command newFireAllRules( int max );
RuntimeLogger newFileLogger( RuntimeEventManager session, String fileName, int maxEventsInMemory );
RuntimeLogger newThreadedFileLogger( RuntimeEventManager session, String fileName, int interval );
RuntimeLogger newConsoleLogger( RuntimeEventManager session );
}
Break apart large interfaces into
smaller versions
public interface RuleEngineServices {
Resources getResources();
Repository getRepository();
Loggers getLoggers();
Commands getCommands();
}
public interface Resources {
Resource newUrlResource( URL url );
Resource newByteArrayResource( byte[] bytes );
Resource newFileSystemResource( File file );
Resource newInputStreamResource( InputStream stream );
Resource newClassPathResource( String path );
}
public interface Repository {
void addModule( Module module );
Module getModule( ReleaseId releaseId );
Module removeModule( ReleaseId releaseId );
}
public interface Commands {
Command newInsert( Object object );
Command newModify( FactHandle factHandle );
Command newDelete( FactHandle factHandle );
Command newFireAllRules( int max );
}
Break apart large interfaces into
smaller versions
public interface RuleEngineServices {
Resources getResources();
Repository getRepository();
Loggers getLoggers();
Commands getCommands();
}
public interface Resources {
Resource newUrlResource( URL url );
Resource newByteArrayResource( byte[] bytes );
Resource newFileSystemResource( File file );
Resource newInputStreamResource( InputStream stream );
Resource newClassPathResource( String path );
}
public interface Repository {
void addModule( Module module );
Module getModule( ReleaseId releaseId );
Module removeModule( ReleaseId releaseId );
}
public interface Commands {
Command newInsert( Object object );
Command newModify( FactHandle factHandle );
Command newDelete( FactHandle factHandle );
Command newFireAllRules( int max );
}
Divide et Impera
Be defensive with your data
public class Person {
private List<Person> siblings;
public List<Person> getSiblings() {
return siblings;
}
}
What’s the problem here?
public class Person {
private List<Person> siblings;
public List<Person> getSiblings() {
return siblings;
}
}
person.getSiblings().add(randomPerson);
What’s the problem here?
Be defensive with your data
public class Person {
private List<Person> siblings;
public List<Person> getSiblings() {
return siblings;
}
}
public class Person {
private List<Person> siblings;
public List<Person> getSiblings() {
return Collections.unmodifiableList( siblings );
}
}
If necessary return
unmodifiable objects to avoid
that a client could compromise
the consistency of your data.
person.getSiblings().add(randomPerson);
What’s the problem here?
Be defensive with your data
Return empty Collections or Optionals
public class Person {
private Car car;
private List<Person> siblings;
public Car getCar() {
return car;
}
public List<Person> getSiblings() {
return siblings;
}
}
What’s the problem here?
Return empty Collections or Optionals
public class Person {
private Car car;
private List<Person> siblings;
public Car getCar() {
return car;
}
public List<Person> getSiblings() {
return siblings;
}
}
What’s the problem here?
for (Person sibling : person.getSiblings()) { ... }
NPE!!!
Return empty Collections or Optionals
public class Person {
private Car car;
private List<Person> siblings;
public Car getCar() {
return car;
}
public List<Person> getSiblings() {
return siblings;
}
}
public class Person {
private Car car;
private List<Person> siblings;
public Optional<Car> getCar() {
return Optional.ofNullable(car);
}
public List<Person> getSiblings() {
return siblings == null ?
Collections.emptyList() :
Collections.unmodifiableList( siblings );
}
}
What’s the problem here?
for (Person sibling : person.getSiblings()) { ... }
NPE!!!
contacts.getPhoneNumber( ... );
Prefer enums to
boolean parameters
public interface EmployeeContacts {
String getPhoneNumber(boolean mobile);
}
contacts.getPhoneNumber( ... );
Prefer enums to
boolean parameters
public interface EmployeeContacts {
String getPhoneNumber(boolean mobile);
}
Should I use true or false here?
contacts.getPhoneNumber( ... );
Prefer enums to
boolean parameters
public interface EmployeeContacts {
String getPhoneNumber(boolean mobile);
}
Should I use true or false here?
What if I may need to add a third
type of phone number in future?
public interface EmployeeContacts {
String getPhoneNumber(PhoneType type);
enum PhoneType {
HOME, MOBILE, OFFICE;
}
}
Prefer enums to
boolean parameters
contacts.getPhoneNumber(PhoneType.HOME);
Use meaningful
return types
public interface EmployeesRegistry {
enum PhoneType {
HOME, MOBILE, OFFICE;
}
Map<String, Map<PhoneType, List<String>>> getEmployeesPhoneNumbers();
}
Use meaningful
return types
public interface EmployeesRegistry {
enum PhoneType {
HOME, MOBILE, OFFICE;
}
Map<String, Map<PhoneType, List<String>>> getEmployeesPhoneNumbers();
}
Employee name
Employee’s
phone numbers
grouped by type
List of phone numbers
of a give type for a
given employee
Primitive
obsession
Use meaningful
return types
public interface EmployeesRegistry {
enum PhoneType {
HOME, MOBILE, OFFICE;
}
PhoneBook getPhoneBook();
}
public class PhoneBook {
private Map<String, EmployeeContacts> contacts;
public EmployeeContacts getEmployeeContacts(String name) {
return Optional.ofNullable( contacts.get(name) )
.orElse( EmptyContacts.INSTANCE );
}
}
public class EmployeeContacts {
private Map<PhoneType, List<String>> numbers;
public List<String> getNumbers(PhoneType type) {
return Optional.ofNullable( numbers.get(type) )
.orElse( emptyList() );
}
public static EmptyContacts INSTANCE = new EmptyContacts();
static class EmptyContacts extends EmployeeContacts {
@Override
public List<String> getNumbers(PhoneType type) {
return emptyList();
}
}
}
Optional – the mother of all bikeshedding
Optional – the mother of all bikeshedding
Principle of least
astonishment???
"If a necessary feature has a high
astonishment factor, it may be
necessary to redesign the feature."
- Cowlishaw, M. F. (1984). "The
design of the REXX language"
Optional – the mother of all bikeshedding
Principle of least
astonishment???
Wrong default
Optional – the mother of all bikeshedding
Principle of least
astonishment???
Wrong default
This could be removed if
the other was correctly
implemented
API design is an
iterative process
and there could
be different points
of view ...
… that could be
driven by the fact
that different
people may
weigh possible
use cases
differently...
… or even see
use cases to
which you didn’t
think at all
Also a good API
has many
different
characteristics ...
… and they
could be
conflicting so you
may need to
trade off one to
privilege another
What should
always drive the
final decision is
the intent of the
API … but even
there it could be
hard to find an
agreement
●
Write lots of tests and examples against your API
●
Discuss it with colleagues and end users
●
Iterates multiple times to eliminate
➢
Unclear intentions
➢
Duplicated or redundant code
➢
Leaky abstraction
API design is an
iterative process
●
Write lots of tests and examples against your API
●
Discuss it with colleagues and end users
●
Iterates multiple times to eliminate
➢
Unclear intentions
➢
Duplicated or redundant code
➢
Leaky abstraction
Practice Dogfeeding
API design is an
iterative process
And that’s all
what you were
getting wrong :)
… questions?
Mario Fusco
Red Hat – Principal Software Engineer
mario.fusco@gmail.com
twitter: @mariofusco

Contenu connexe

Tendances

Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...Philip Schwarz
 
Railway Oriented Programming
Railway Oriented ProgrammingRailway Oriented Programming
Railway Oriented ProgrammingScott Wlaschin
 
Anatomy of a Spring Boot App with Clean Architecture - Spring I/O 2023
Anatomy of a Spring Boot App with Clean Architecture - Spring I/O 2023Anatomy of a Spring Boot App with Clean Architecture - Spring I/O 2023
Anatomy of a Spring Boot App with Clean Architecture - Spring I/O 2023Steve Pember
 
Pipeline oriented programming
Pipeline oriented programmingPipeline oriented programming
Pipeline oriented programmingScott Wlaschin
 
Domain Modeling in a Functional World
Domain Modeling in a Functional WorldDomain Modeling in a Functional World
Domain Modeling in a Functional WorldDebasish Ghosh
 
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...Mario Fusco
 
Everything as Code with Terraform
Everything as Code with TerraformEverything as Code with Terraform
Everything as Code with TerraformAll Things Open
 
Inversion of Control and Dependency Injection
Inversion of Control and Dependency InjectionInversion of Control and Dependency Injection
Inversion of Control and Dependency InjectionDinesh Sharma
 
If You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are WrongIf You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are WrongMario Fusco
 
PHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object CalisthenicsPHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object CalisthenicsGuilherme Blanco
 
Introduction to SOLID Principles
Introduction to SOLID PrinciplesIntroduction to SOLID Principles
Introduction to SOLID PrinciplesGanesh Samarthyam
 
Angular & RXJS: examples and use cases
Angular & RXJS: examples and use casesAngular & RXJS: examples and use cases
Angular & RXJS: examples and use casesFabio Biondi
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modelingMario Fusco
 
The Future of Java: Records, Sealed Classes and Pattern Matching
The Future of Java: Records, Sealed Classes and Pattern MatchingThe Future of Java: Records, Sealed Classes and Pattern Matching
The Future of Java: Records, Sealed Classes and Pattern MatchingJosé Paumard
 
Threading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
Threading Made Easy! A Busy Developer’s Guide to Kotlin CoroutinesThreading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
Threading Made Easy! A Busy Developer’s Guide to Kotlin CoroutinesLauren Yew
 
Functional Patterns with Java8 @Bucharest Java User Group
Functional Patterns with Java8 @Bucharest Java User GroupFunctional Patterns with Java8 @Bucharest Java User Group
Functional Patterns with Java8 @Bucharest Java User GroupVictor Rentea
 

Tendances (20)

Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
 
Railway Oriented Programming
Railway Oriented ProgrammingRailway Oriented Programming
Railway Oriented Programming
 
Anatomy of a Spring Boot App with Clean Architecture - Spring I/O 2023
Anatomy of a Spring Boot App with Clean Architecture - Spring I/O 2023Anatomy of a Spring Boot App with Clean Architecture - Spring I/O 2023
Anatomy of a Spring Boot App with Clean Architecture - Spring I/O 2023
 
Pipeline oriented programming
Pipeline oriented programmingPipeline oriented programming
Pipeline oriented programming
 
Domain Modeling in a Functional World
Domain Modeling in a Functional WorldDomain Modeling in a Functional World
Domain Modeling in a Functional World
 
Rxjs ppt
Rxjs pptRxjs ppt
Rxjs ppt
 
Monadic Java
Monadic JavaMonadic Java
Monadic Java
 
Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...Laziness, trampolines, monoids and other functional amenities: this is not yo...
Laziness, trampolines, monoids and other functional amenities: this is not yo...
 
Everything as Code with Terraform
Everything as Code with TerraformEverything as Code with Terraform
Everything as Code with Terraform
 
Inversion of Control and Dependency Injection
Inversion of Control and Dependency InjectionInversion of Control and Dependency Injection
Inversion of Control and Dependency Injection
 
If You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are WrongIf You Think You Can Stay Away from Functional Programming, You Are Wrong
If You Think You Can Stay Away from Functional Programming, You Are Wrong
 
PHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object CalisthenicsPHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object Calisthenics
 
Introduction to SOLID Principles
Introduction to SOLID PrinciplesIntroduction to SOLID Principles
Introduction to SOLID Principles
 
Angular & RXJS: examples and use cases
Angular & RXJS: examples and use casesAngular & RXJS: examples and use cases
Angular & RXJS: examples and use cases
 
Java 8 Lambda and Streams
Java 8 Lambda and StreamsJava 8 Lambda and Streams
Java 8 Lambda and Streams
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
 
The Future of Java: Records, Sealed Classes and Pattern Matching
The Future of Java: Records, Sealed Classes and Pattern MatchingThe Future of Java: Records, Sealed Classes and Pattern Matching
The Future of Java: Records, Sealed Classes and Pattern Matching
 
Threading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
Threading Made Easy! A Busy Developer’s Guide to Kotlin CoroutinesThreading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
Threading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
 
Express JS
Express JSExpress JS
Express JS
 
Functional Patterns with Java8 @Bucharest Java User Group
Functional Patterns with Java8 @Bucharest Java User GroupFunctional Patterns with Java8 @Bucharest Java User Group
Functional Patterns with Java8 @Bucharest Java User Group
 

Similaire à Let's make a contract: the art of designing a Java API

Let's make a contract: The art of designing a Java API | DevNation Tech Talk
Let's make a contract: The art of designing a Java API | DevNation Tech TalkLet's make a contract: The art of designing a Java API | DevNation Tech Talk
Let's make a contract: The art of designing a Java API | DevNation Tech TalkRed Hat Developers
 
TypeScript Presentation - Jason Haffey
TypeScript Presentation - Jason HaffeyTypeScript Presentation - Jason Haffey
TypeScript Presentation - Jason HaffeyRalph Johnson
 
Domänenspezifische Sprachen mit Xtext
Domänenspezifische Sprachen mit XtextDomänenspezifische Sprachen mit Xtext
Domänenspezifische Sprachen mit XtextDr. Jan Köhnlein
 
What's new in PHP 8.0?
What's new in PHP 8.0?What's new in PHP 8.0?
What's new in PHP 8.0?Nikita Popov
 
Nikita Popov "What’s new in PHP 8.0?"
Nikita Popov "What’s new in PHP 8.0?"Nikita Popov "What’s new in PHP 8.0?"
Nikita Popov "What’s new in PHP 8.0?"Fwdays
 
Effective Java - Enum and Annotations
Effective Java - Enum and AnnotationsEffective Java - Enum and Annotations
Effective Java - Enum and AnnotationsRoshan Deniyage
 
SeriesTester.classpathSeriesTester.project SeriesT.docx
SeriesTester.classpathSeriesTester.project  SeriesT.docxSeriesTester.classpathSeriesTester.project  SeriesT.docx
SeriesTester.classpathSeriesTester.project SeriesT.docxlesleyryder69361
 
Is your C# optimized
Is your C# optimizedIs your C# optimized
Is your C# optimizedWoody Pewitt
 
An introduction to functional programming with Swift
An introduction to functional programming with SwiftAn introduction to functional programming with Swift
An introduction to functional programming with SwiftFatih Nayebi, Ph.D.
 
TypeScript by Howard
TypeScript by HowardTypeScript by Howard
TypeScript by HowardLearningTech
 
Howard type script
Howard   type scriptHoward   type script
Howard type scriptLearningTech
 
Type script by Howard
Type script by HowardType script by Howard
Type script by HowardLearningTech
 
Transaction is a monad
Transaction is a  monadTransaction is a  monad
Transaction is a monadJarek Ratajski
 
FP in Java - Project Lambda and beyond
FP in Java - Project Lambda and beyondFP in Java - Project Lambda and beyond
FP in Java - Project Lambda and beyondMario Fusco
 
Back-2-Basics: .NET Coding Standards For The Real World (2011)
Back-2-Basics: .NET Coding Standards For The Real World (2011)Back-2-Basics: .NET Coding Standards For The Real World (2011)
Back-2-Basics: .NET Coding Standards For The Real World (2011)David McCarter
 
Functional Programming In Java
Functional Programming In JavaFunctional Programming In Java
Functional Programming In JavaAndrei Solntsev
 
Java Static Factory Methods
Java Static Factory MethodsJava Static Factory Methods
Java Static Factory MethodsYe Win
 

Similaire à Let's make a contract: the art of designing a Java API (20)

Let's make a contract: The art of designing a Java API | DevNation Tech Talk
Let's make a contract: The art of designing a Java API | DevNation Tech TalkLet's make a contract: The art of designing a Java API | DevNation Tech Talk
Let's make a contract: The art of designing a Java API | DevNation Tech Talk
 
Functional Programming with C#
Functional Programming with C#Functional Programming with C#
Functional Programming with C#
 
TypeScript Presentation - Jason Haffey
TypeScript Presentation - Jason HaffeyTypeScript Presentation - Jason Haffey
TypeScript Presentation - Jason Haffey
 
Domänenspezifische Sprachen mit Xtext
Domänenspezifische Sprachen mit XtextDomänenspezifische Sprachen mit Xtext
Domänenspezifische Sprachen mit Xtext
 
What's new in PHP 8.0?
What's new in PHP 8.0?What's new in PHP 8.0?
What's new in PHP 8.0?
 
Nikita Popov "What’s new in PHP 8.0?"
Nikita Popov "What’s new in PHP 8.0?"Nikita Popov "What’s new in PHP 8.0?"
Nikita Popov "What’s new in PHP 8.0?"
 
Effective Java - Enum and Annotations
Effective Java - Enum and AnnotationsEffective Java - Enum and Annotations
Effective Java - Enum and Annotations
 
SeriesTester.classpathSeriesTester.project SeriesT.docx
SeriesTester.classpathSeriesTester.project  SeriesT.docxSeriesTester.classpathSeriesTester.project  SeriesT.docx
SeriesTester.classpathSeriesTester.project SeriesT.docx
 
Solid principles
Solid principlesSolid principles
Solid principles
 
Legacy is Good
Legacy is GoodLegacy is Good
Legacy is Good
 
Is your C# optimized
Is your C# optimizedIs your C# optimized
Is your C# optimized
 
An introduction to functional programming with Swift
An introduction to functional programming with SwiftAn introduction to functional programming with Swift
An introduction to functional programming with Swift
 
TypeScript by Howard
TypeScript by HowardTypeScript by Howard
TypeScript by Howard
 
Howard type script
Howard   type scriptHoward   type script
Howard type script
 
Type script by Howard
Type script by HowardType script by Howard
Type script by Howard
 
Transaction is a monad
Transaction is a  monadTransaction is a  monad
Transaction is a monad
 
FP in Java - Project Lambda and beyond
FP in Java - Project Lambda and beyondFP in Java - Project Lambda and beyond
FP in Java - Project Lambda and beyond
 
Back-2-Basics: .NET Coding Standards For The Real World (2011)
Back-2-Basics: .NET Coding Standards For The Real World (2011)Back-2-Basics: .NET Coding Standards For The Real World (2011)
Back-2-Basics: .NET Coding Standards For The Real World (2011)
 
Functional Programming In Java
Functional Programming In JavaFunctional Programming In Java
Functional Programming In Java
 
Java Static Factory Methods
Java Static Factory MethodsJava Static Factory Methods
Java Static Factory Methods
 

Plus de Mario Fusco

Kogito: cloud native business automation
Kogito: cloud native business automationKogito: cloud native business automation
Kogito: cloud native business automationMario Fusco
 
How and why I turned my old Java projects into a first-class serverless compo...
How and why I turned my old Java projects into a first-class serverless compo...How and why I turned my old Java projects into a first-class serverless compo...
How and why I turned my old Java projects into a first-class serverless compo...Mario Fusco
 
Drools 6 deep dive
Drools 6 deep diveDrools 6 deep dive
Drools 6 deep diveMario Fusco
 
OOP and FP - Become a Better Programmer
OOP and FP - Become a Better ProgrammerOOP and FP - Become a Better Programmer
OOP and FP - Become a Better ProgrammerMario Fusco
 
Reactive Programming for a demanding world: building event-driven and respons...
Reactive Programming for a demanding world: building event-driven and respons...Reactive Programming for a demanding world: building event-driven and respons...
Reactive Programming for a demanding world: building event-driven and respons...Mario Fusco
 
Comparing different concurrency models on the JVM
Comparing different concurrency models on the JVMComparing different concurrency models on the JVM
Comparing different concurrency models on the JVMMario Fusco
 
Why we cannot ignore Functional Programming
Why we cannot ignore Functional ProgrammingWhy we cannot ignore Functional Programming
Why we cannot ignore Functional ProgrammingMario Fusco
 
Real world DSL - making technical and business people speaking the same language
Real world DSL - making technical and business people speaking the same languageReal world DSL - making technical and business people speaking the same language
Real world DSL - making technical and business people speaking the same languageMario Fusco
 
Introducing Drools
Introducing DroolsIntroducing Drools
Introducing DroolsMario Fusco
 
Java 7, 8 & 9 - Moving the language forward
Java 7, 8 & 9 - Moving the language forwardJava 7, 8 & 9 - Moving the language forward
Java 7, 8 & 9 - Moving the language forwardMario Fusco
 
Swiss army knife Spring
Swiss army knife SpringSwiss army knife Spring
Swiss army knife SpringMario Fusco
 
No more loops with lambdaj
No more loops with lambdajNo more loops with lambdaj
No more loops with lambdajMario Fusco
 
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STMConcurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STMMario Fusco
 
Scala - where objects and functions meet
Scala - where objects and functions meetScala - where objects and functions meet
Scala - where objects and functions meetMario Fusco
 

Plus de Mario Fusco (16)

Kogito: cloud native business automation
Kogito: cloud native business automationKogito: cloud native business automation
Kogito: cloud native business automation
 
How and why I turned my old Java projects into a first-class serverless compo...
How and why I turned my old Java projects into a first-class serverless compo...How and why I turned my old Java projects into a first-class serverless compo...
How and why I turned my old Java projects into a first-class serverless compo...
 
OOP and FP
OOP and FPOOP and FP
OOP and FP
 
Drools 6 deep dive
Drools 6 deep diveDrools 6 deep dive
Drools 6 deep dive
 
OOP and FP - Become a Better Programmer
OOP and FP - Become a Better ProgrammerOOP and FP - Become a Better Programmer
OOP and FP - Become a Better Programmer
 
Reactive Programming for a demanding world: building event-driven and respons...
Reactive Programming for a demanding world: building event-driven and respons...Reactive Programming for a demanding world: building event-driven and respons...
Reactive Programming for a demanding world: building event-driven and respons...
 
Comparing different concurrency models on the JVM
Comparing different concurrency models on the JVMComparing different concurrency models on the JVM
Comparing different concurrency models on the JVM
 
Why we cannot ignore Functional Programming
Why we cannot ignore Functional ProgrammingWhy we cannot ignore Functional Programming
Why we cannot ignore Functional Programming
 
Real world DSL - making technical and business people speaking the same language
Real world DSL - making technical and business people speaking the same languageReal world DSL - making technical and business people speaking the same language
Real world DSL - making technical and business people speaking the same language
 
Introducing Drools
Introducing DroolsIntroducing Drools
Introducing Drools
 
Java 7, 8 & 9 - Moving the language forward
Java 7, 8 & 9 - Moving the language forwardJava 7, 8 & 9 - Moving the language forward
Java 7, 8 & 9 - Moving the language forward
 
Hammurabi
HammurabiHammurabi
Hammurabi
 
Swiss army knife Spring
Swiss army knife SpringSwiss army knife Spring
Swiss army knife Spring
 
No more loops with lambdaj
No more loops with lambdajNo more loops with lambdaj
No more loops with lambdaj
 
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STMConcurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
 
Scala - where objects and functions meet
Scala - where objects and functions meetScala - where objects and functions meet
Scala - where objects and functions meet
 

Dernier

Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odishasmiwainfosol
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Velvetech LLC
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024StefanoLambiase
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfMarharyta Nedzelska
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentationvaddepallysandeep122
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsAhmed Mohamed
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio, Inc.
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationBradBedford3
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWave PLM
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanyChristoph Pohl
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfStefano Stabellini
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...OnePlan Solutions
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Natan Silnitsky
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...Technogeeks
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprisepreethippts
 

Dernier (20)

Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort ServiceHot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...
 
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
Dealing with Cultural Dispersion — Stefano Lambiase — ICSE-SEIS 2024
 
2.pdf Ejercicios de programación competitiva
2.pdf Ejercicios de programación competitiva2.pdf Ejercicios de programación competitiva
2.pdf Ejercicios de programación competitiva
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdf
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentation
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML Diagrams
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion Application
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need It
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
 
Xen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdfXen Safety Embedded OSS Summit April 2024 v4.pdf
Xen Safety Embedded OSS Summit April 2024 v4.pdf
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
 
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
Taming Distributed Systems: Key Insights from Wix's Large-Scale Experience - ...
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprise
 

Let's make a contract: the art of designing a Java API

  • 1. Let's make a contract: the art of designing a Java API by Mario Fusco mario.fusco@gmail.com @mariofusco
  • 2. What is an API?
  • 3. What is an API?
  • 4. An API is what a developer uses to achieve some task What is an API?
  • 5. What is an API? An API is a contract between its implementors and its users
  • 6. And why should I care? We are all API designers Our software doesn't work in isolation, but becomes useful only when it interacts with other software written by other developers
  • 7. Basic Principles ● Intuitive ● Understandable ● Learnable ● Discoverable ● Consistent ● Self-defensive ● Concise ● Easy to use ● Minimal ● Orthogonal ● Idiomatic ● Flexible ● Evolvable ● Well documented ● Right level of abstraction ● Correct use of the type system ● Limited number of entry-points ● Respect the principle of least astonishment
  • 8. Be ready for changes
  • 9. Be ready for changes Software being 'done' is like lawn being 'mowed' – Jim Benson Change is the only constant in software Add features sparingly and carefully so that they won’t become obstacles for the evolution of your API
  • 10. If in doubt, leave it out A feature that only takes a few hours to be implemented can ➢ create hundreds of hours of support and maintenance in future ➢ bloat your software and confuse your users ➢ become a burden and prevent future improvements “it’s easy to build” is NOT a good enough reason to add it to your product
  • 16. Convenience methods Use overloading judiciously and sparingly
  • 17. Convenience methods Use overloading judiciously and sparingly Primitives often cause methods proliferation {
  • 18. Convenience methods Use overloading judiciously and sparingly Are these all necessary? Primitives often cause methods proliferation {
  • 19. Convenience methods public interface StockOrder { void sell(String symbol, double price, int quantity); void buy(String symbol, int quantity, double price); void buy(String symbol, int quantity, double price, double commission); void buy(String symbol, int quantity, double minPrice, double maxPrice, double commission); } What’s wrong with this?
  • 20. Convenience methods public interface StockOrder { void sell(String symbol, double price, int quantity); void buy(String symbol, int quantity, double price); void buy(String symbol, int quantity, double price, double commission); void buy(String symbol, int quantity, double minPrice, double maxPrice, double commission); } Too many overloads What’s wrong with this?
  • 21. Convenience methods public interface StockOrder { void sell(String symbol, double price, int quantity); void buy(String symbol, int quantity, double price); void buy(String symbol, int quantity, double price, double commission); void buy(String symbol, int quantity, double minPrice, double maxPrice, double commission); } Too many overloads Inconsistent argument order What’s wrong with this?
  • 22. Convenience methods Long arguments lists (especially of same type) public interface StockOrder { void sell(String symbol, double price, int quantity); void buy(String symbol, int quantity, double price); void buy(String symbol, int quantity, double price, double commission); void buy(String symbol, int quantity, double minPrice, double maxPrice, double commission); } Too many overloads Inconsistent argument order What’s wrong with this?
  • 23. Convenience methods Long arguments lists (especially of same type) public interface StockOrder { void sell(String symbol, double price, int quantity); void buy(String symbol, int quantity, double price); void buy(String symbol, int quantity, double price, double commission); void buy(String symbol, int quantity, double minPrice, double maxPrice, double commission); } public interface StockOrder { void sell(String symbol, int quantity, Price price); void buy(String symbol, int quantity, Price price); } Too many overloads Inconsistent argument order What’s wrong with this? How to do better
  • 24. Consider static factories public interface Price { static Price price( double price ) { if (price < 0) return Malformed.INSTANCE; return new Fixed(price); } static Price price( double minPrice, double maxPrice ) { if (minPrice > maxPrice) return Malformed.INSTANCE; return new Range(minPrice, maxPrice); } class Fixed implements Price { private final double price; private Fixed( double price ) { this.price = price; } } class Range implements Price { private final double minPrice; private final double maxPrice; private Range( double minPrice, double maxPrice ) { this.minPrice = minPrice; this.maxPrice = maxPrice; } } enum Malformed implements Price { INSTANCE } } ➢ nicer syntax for users (no need of new keyword)
  • 25. Consider static factories public interface Price { static Price price( double price ) { if (price < 0) return Malformed.INSTANCE; return new Fixed(price); } static Price price( double minPrice, double maxPrice ) { if (minPrice > maxPrice) return Malformed.INSTANCE; return new Range(minPrice, maxPrice); } class Fixed implements Price { private final double price; private Fixed( double price ) { this.price = price; } } class Range implements Price { private final double minPrice; private final double maxPrice; private Range( double minPrice, double maxPrice ) { this.minPrice = minPrice; this.maxPrice = maxPrice; } } enum Malformed implements Price { INSTANCE } } ➢ nicer syntax for users (no need of new keyword) ➢ can return different subclasses
  • 26. Consider static factories public interface Price { static Price price( double price ) { if (price < 0) return Malformed.INSTANCE; return new Fixed(price); } static Price price( double minPrice, double maxPrice ) { if (minPrice > maxPrice) return Malformed.INSTANCE; return new Range(minPrice, maxPrice); } class Fixed implements Price { private final double price; private Fixed( double price ) { this.price = price; } } class Range implements Price { private final double minPrice; private final double maxPrice; private Range( double minPrice, double maxPrice ) { this.minPrice = minPrice; this.maxPrice = maxPrice; } } enum Malformed implements Price { INSTANCE } } ➢ nicer syntax for users (no need of new keyword) ➢ can return different subclasses ➢ can check preconditions and edge cases returning different implementations accordingly
  • 27. Promote fluent API public interface Price { Price withCommission(double commission); Price gross(); } public interface Price { void setCommission(double commission); void setGross(); }
  • 28. Promote fluent API public interface Price { Price withCommission(double commission); Price gross(); } stockOrder.buy( "IBM", 100, price(150.0).withCommission(0.7).gross() ); public interface Price { void setCommission(double commission); void setGross(); } Price price = price(150.0); price.setCommission(0.7); price.setGross(); stockOrder.buy( "IBM", 100, price );
  • 29. Promote fluent API public interface Price { Price withCommission(double commission); Price gross(); } stockOrder.buy( "IBM", 100, price(150.0).withCommission(0.7).gross() ); public interface Price { void setCommission(double commission); void setGross(); } Price price = price(150.0); price.setCommission(0.7); price.setGross(); stockOrder.buy( "IBM", 100, price ); Concatenate multiple invocations Use result directly
  • 30. Promote fluent API Streams are a very nice and convenient example of fluent API
  • 31. Promote fluent API Streams are a very nice and convenient example of fluent API
  • 32. Promote fluent API Name consistency??? Streams are a very nice and convenient example of fluent API
  • 33. Use the weakest possible type public String concatenate( ArrayList<String> strings ) { StringBuilder sb = new StringBuilder(); for (String s : strings) { sb.append( s ); } return sb.toString(); }
  • 34. Use the weakest possible type public String concatenate( ArrayList<String> strings ) { StringBuilder sb = new StringBuilder(); for (String s : strings) { sb.append( s ); } return sb.toString(); } Do I care of the actual List implementation?
  • 35. Use the weakest possible type public String concatenate( List<String> strings ) { StringBuilder sb = new StringBuilder(); for (String s : strings) { sb.append( s ); } return sb.toString(); }
  • 36. Use the weakest possible type public String concatenate( List<String> strings ) { StringBuilder sb = new StringBuilder(); for (String s : strings) { sb.append( s ); } return sb.toString(); } Do I care of the elements’ order?
  • 37. Use the weakest possible type public String concatenate( Collection<String> strings ) { StringBuilder sb = new StringBuilder(); for (String s : strings) { sb.append( s ); } return sb.toString(); }
  • 38. Use the weakest possible type public String concatenate( Collection<String> strings ) { StringBuilder sb = new StringBuilder(); for (String s : strings) { sb.append( s ); } return sb.toString(); } Do I care of the Collection’s size?
  • 39. Use the weakest possible type public String concatenate( Iterable<String> strings ) { StringBuilder sb = new StringBuilder(); for (String s : strings) { sb.append( s ); } return sb.toString(); }
  • 40. Using the weakest possible type... public String concatenate( Iterable<String> strings ) { StringBuilder sb = new StringBuilder(); for (String s : strings) { sb.append( s ); } return sb.toString(); } … enlarges the applicability of your method, avoiding to restrict your client to a particular implementation or forcing it to perform an unnecessary and potentially expensive copy operation if the input data exists in other forms
  • 41. Use the weakest possible type also for returned value public List<Address> getFamilyAddresses( Person person ) { List<Address> addresses = new ArrayList<>(); addresses.add(person.getAddress()); for (Person sibling : person.getSiblings()) { addresses.add(sibling.getAddress()); } return addresses; }
  • 42. Use the weakest possible type also for returned value public List<Address> getFamilyAddresses( Person person ) { List<Address> addresses = new ArrayList<>(); addresses.add(person.getAddress()); for (Person sibling : person.getSiblings()) { addresses.add(sibling.getAddress()); } return addresses; } Is the order of this List meaningful for client?
  • 43. Use the weakest possible type also for returned value public List<Address> getFamilyAddresses( Person person ) { List<Address> addresses = new ArrayList<>(); addresses.add(person.getAddress()); for (Person sibling : person.getSiblings()) { addresses.add(sibling.getAddress()); } return addresses; } Is the order of this List meaningful for client? … and shouldn’t we maybe return only the distinct addresses? Yeah, that will be easy let’s do this!
  • 44. Use the weakest possible type also for returned value public List<Address> getFamilyAddresses( Person person ) { Set<Address> addresses = new HashSet<>(); addresses.add(person.getAddress()); for (Person sibling : person.getSiblings()) { addresses.add(sibling.getAddress()); } return addresses; } It should be enough to change this List into a Set
  • 45. Use the weakest possible type also for returned value public List<Address> getFamilyAddresses( Person person ) { Set<Address> addresses = new HashSet<>(); addresses.add(person.getAddress()); for (Person sibling : person.getSiblings()) { addresses.add(sibling.getAddress()); } return addresses; } It should be enough to change this List into a Set But this doesn’t compile :(
  • 46. Use the weakest possible type also for returned value public List<Address> getFamilyAddresses( Person person ) { Set<Address> addresses = new HashSet<>(); addresses.add(person.getAddress()); for (Person sibling : person.getSiblings()) { addresses.add(sibling.getAddress()); } return addresses; } It should be enough to change this List into a Set But this doesn’t compile :( and I cannot change the returned type to avoid breaking backward compatibility :(((
  • 47. Use the weakest possible type also for returned value public List<Address> getFamilyAddresses( Person person ) { Set<Address> addresses = new HashSet<>(); addresses.add(person.getAddress()); for (Person sibling : person.getSiblings()) { addresses.add(sibling.getAddress()); } return new ArrayList<>( addresses ); } I’m obliged to uselessly create an expensive copy of data before returning them
  • 48. Use the weakest possible type also for returned value public Collection<Address> getFamilyAddresses( Person person ) { List<Address> addresses = new ArrayList<>(); addresses.add(person.getAddress()); for (Person sibling : person.getSiblings()) { addresses.add(sibling.getAddress()); } return addresses; } Returning a more generic type (if this is acceptable for your client) provides better flexibility in future
  • 49. Support lambdas public interface Listener { void beforeEvent(Event e); void afterEvent(Event e); } class EventProducer { public void registerListener(Listener listener) { // register listener } } public interface Listener { void beforeEvent(Event e); void afterEvent(Event e); } public interface Listener { void beforeEvent(Event e); void afterEvent(Event e); } EventProducer producer = new EventProducer(); producer.registerListener( new Listener() { @Override public void beforeEvent( Event e ) { // ignore } @Override public void afterEvent( Event e ) { System.out.println(e); } } );
  • 50. Support lambdas class EventProducer { public void registerBefore(BeforeListener before) { // register listener } public void registerAfter(AfterListener after) { // register listener } } @FunctionalInterface interface BeforeListener { void beforeEvent( Event e ); } @FunctionalInterface interface AfterListener { void afterEvent( Event e ); } EventProducer producer = new EventProducer(); producer.registerAfter( System.out::println ); Taking functional interfaces as argument of your API enables clients to use lambdas
  • 51. Support lambdas class EventProducer { public void registerBefore(Consumer<Event> before) { // register listener } public void registerAfter(Consumer<Event> after) { // register listener } } @FunctionalInterface interface BeforeListener { void beforeEvent( Event e ); } @FunctionalInterface interface AfterListener { void afterEvent( Event e ); } EventProducer producer = new EventProducer(); producer.registerAfter( System.out::println ); Taking functional interfaces as argument of your API enables clients to use lambdas In many cases you don’t need to define your own functional interfaces and use Java’s one
  • 52. public void writeList( Writer writer, Collection<String> strings ) { strings.stream().forEach( writer::write ); } Writer writer = new StringWriter(); List<String> strings = asList("one", "two", "three"); writeList( writer, strings ); Avoid checked exceptions
  • 53. Avoid checked exceptions public void writeList( Writer writer, Collection<String> strings ) { strings.stream().forEach( writer::write ); } Writer writer = new StringWriter(); List<String> strings = asList("one", "two", "three"); writeList( writer, strings ); public void writeList( Writer writer, Collection<String> strings ) { strings.stream().forEach( str -> { try { writer.write( str ); } catch (IOException e) { throw new RuntimeException( e ); } } ); }
  • 54. Avoid checked exceptions Useful error management or Wasteful bloatware ?
  • 55. Stay in control (loan pattern) public byte[] readFile(String filename) throws IOException { FileInputStream file = new FileInputStream( filename ); byte[] buffer = new byte[4096]; ByteArrayOutputStream out = new ByteArrayOutputStream( buffer.length ); int n = 0; while ( (n = file.read( buffer )) > 0 ) { out.write( buffer, 0, n ); } return out.toByteArray(); }
  • 56. Stay in control (loan pattern) public byte[] readFile(String filename) throws IOException { FileInputStream file = new FileInputStream( filename ); byte[] buffer = new byte[4096]; ByteArrayOutputStream out = new ByteArrayOutputStream( buffer.length ); int n = 0; while ( (n = file.read( buffer )) > 0 ) { out.write( buffer, 0, n ); } return out.toByteArray(); } File descriptor leak
  • 57. Stay in control (loan pattern) public byte[] readFile(String filename) throws IOException { FileInputStream file = new FileInputStream( filename ); try { byte[] buffer = new byte[4096]; ByteArrayOutputStream out = new ByteArrayOutputStream( buffer.length ); int n = 0; while ( (n = file.read( buffer )) > 0 ) { out.write( buffer, 0, n ); } return out.toByteArray(); } finally { file.close(); } }
  • 58. Stay in control (loan pattern) public byte[] readFile(String filename) throws IOException { FileInputStream file = new FileInputStream( filename ); try { byte[] buffer = new byte[4096]; ByteArrayOutputStream out = new ByteArrayOutputStream( buffer.length ); int n = 0; while ( (n = file.read( buffer )) > 0 ) { out.write( buffer, 0, n ); } return out.toByteArray(); } finally { file.close(); } } We can do better using try-with-resource
  • 59. Stay in control (loan pattern) public byte[] readFile(String filename) throws IOException { try ( FileInputStream file = new FileInputStream( filename ) ) { byte[] buffer = new byte[4096]; ByteArrayOutputStream out = new ByteArrayOutputStream( buffer.length ); int n = 0; while ( (n = file.read( buffer )) > 0 ) { out.write( buffer, 0, n ); } return out.toByteArray(); } }
  • 60. Stay in control (loan pattern) public byte[] readFile(String filename) throws IOException { try ( FileInputStream file = new FileInputStream( filename ) ) { byte[] buffer = new byte[4096]; ByteArrayOutputStream out = new ByteArrayOutputStream( buffer.length ); int n = 0; while ( (n = file.read( buffer )) > 0 ) { out.write( buffer, 0, n ); } return out.toByteArray(); } } Better, but we’re still transferring to our users the burden to use our API correctly
  • 61. Stay in control (loan pattern) public byte[] readFile(String filename) throws IOException { try ( FileInputStream file = new FileInputStream( filename ) ) { byte[] buffer = new byte[4096]; ByteArrayOutputStream out = new ByteArrayOutputStream( buffer.length ); int n = 0; while ( (n = file.read( buffer )) > 0 ) { out.write( buffer, 0, n ); } return out.toByteArray(); } } Better, but we’re still transferring to our users the burden to use our API correctly That’s a leaky abstraction!
  • 62. Stay in control (loan pattern) public static <T> T withFile( String filename, ThrowingFunction<FileInputStream, T> consumer ) throws IOException { try ( FileInputStream file = new FileInputStream( filename ) ) { return consumer.apply( file ); } } @FunctionalInterface public interface ThrowingFunction<T, R> { R apply(T t) throws IOException; } Yeah, checked exceptions suck :(
  • 63. Stay in control (loan pattern) public byte[] readFile(String filename) throws IOException { return withFile( filename, file -> { byte[] buffer = new byte[4096]; ByteArrayOutputStream out = new ByteArrayOutputStream( buffer.length ); int n = 0; while ( (n = file.read( buffer )) > 0 ) { out.write( buffer, 0, n ); } return out.toByteArray(); }); } Now the responsibility of avoiding the leak is encapsulated in our API If clients are forced to use this API no leak is possible at all!
  • 64. Break apart large interfaces into smaller versions public interface RuleEngineServices { Resource newUrlResource( URL url ); Resource newByteArrayResource( byte[] bytes ); Resource newFileSystemResource( File file ); Resource newInputStreamResource( InputStream stream ); Resource newClassPathResource( String path ); void addModule( Module kModule ); Module getModule( ReleaseId releaseId ); Module removeModule( ReleaseId releaseId ); Command newInsert( Object object ); Command newModify( FactHandle factHandle ); Command newDelete( FactHandle factHandle ); Command newFireAllRules( int max ); RuntimeLogger newFileLogger( RuntimeEventManager session, String fileName, int maxEventsInMemory ); RuntimeLogger newThreadedFileLogger( RuntimeEventManager session, String fileName, int interval ); RuntimeLogger newConsoleLogger( RuntimeEventManager session ); }
  • 65. Break apart large interfaces into smaller versions public interface RuleEngineServices { Resources getResources(); Repository getRepository(); Loggers getLoggers(); Commands getCommands(); } public interface Resources { Resource newUrlResource( URL url ); Resource newByteArrayResource( byte[] bytes ); Resource newFileSystemResource( File file ); Resource newInputStreamResource( InputStream stream ); Resource newClassPathResource( String path ); } public interface Repository { void addModule( Module module ); Module getModule( ReleaseId releaseId ); Module removeModule( ReleaseId releaseId ); } public interface Commands { Command newInsert( Object object ); Command newModify( FactHandle factHandle ); Command newDelete( FactHandle factHandle ); Command newFireAllRules( int max ); }
  • 66. Break apart large interfaces into smaller versions public interface RuleEngineServices { Resources getResources(); Repository getRepository(); Loggers getLoggers(); Commands getCommands(); } public interface Resources { Resource newUrlResource( URL url ); Resource newByteArrayResource( byte[] bytes ); Resource newFileSystemResource( File file ); Resource newInputStreamResource( InputStream stream ); Resource newClassPathResource( String path ); } public interface Repository { void addModule( Module module ); Module getModule( ReleaseId releaseId ); Module removeModule( ReleaseId releaseId ); } public interface Commands { Command newInsert( Object object ); Command newModify( FactHandle factHandle ); Command newDelete( FactHandle factHandle ); Command newFireAllRules( int max ); } Divide et Impera
  • 67. Be defensive with your data public class Person { private List<Person> siblings; public List<Person> getSiblings() { return siblings; } } What’s the problem here?
  • 68. public class Person { private List<Person> siblings; public List<Person> getSiblings() { return siblings; } } person.getSiblings().add(randomPerson); What’s the problem here? Be defensive with your data
  • 69. public class Person { private List<Person> siblings; public List<Person> getSiblings() { return siblings; } } public class Person { private List<Person> siblings; public List<Person> getSiblings() { return Collections.unmodifiableList( siblings ); } } If necessary return unmodifiable objects to avoid that a client could compromise the consistency of your data. person.getSiblings().add(randomPerson); What’s the problem here? Be defensive with your data
  • 70. Return empty Collections or Optionals public class Person { private Car car; private List<Person> siblings; public Car getCar() { return car; } public List<Person> getSiblings() { return siblings; } } What’s the problem here?
  • 71. Return empty Collections or Optionals public class Person { private Car car; private List<Person> siblings; public Car getCar() { return car; } public List<Person> getSiblings() { return siblings; } } What’s the problem here? for (Person sibling : person.getSiblings()) { ... } NPE!!!
  • 72. Return empty Collections or Optionals public class Person { private Car car; private List<Person> siblings; public Car getCar() { return car; } public List<Person> getSiblings() { return siblings; } } public class Person { private Car car; private List<Person> siblings; public Optional<Car> getCar() { return Optional.ofNullable(car); } public List<Person> getSiblings() { return siblings == null ? Collections.emptyList() : Collections.unmodifiableList( siblings ); } } What’s the problem here? for (Person sibling : person.getSiblings()) { ... } NPE!!!
  • 73. contacts.getPhoneNumber( ... ); Prefer enums to boolean parameters public interface EmployeeContacts { String getPhoneNumber(boolean mobile); }
  • 74. contacts.getPhoneNumber( ... ); Prefer enums to boolean parameters public interface EmployeeContacts { String getPhoneNumber(boolean mobile); } Should I use true or false here?
  • 75. contacts.getPhoneNumber( ... ); Prefer enums to boolean parameters public interface EmployeeContacts { String getPhoneNumber(boolean mobile); } Should I use true or false here? What if I may need to add a third type of phone number in future?
  • 76. public interface EmployeeContacts { String getPhoneNumber(PhoneType type); enum PhoneType { HOME, MOBILE, OFFICE; } } Prefer enums to boolean parameters contacts.getPhoneNumber(PhoneType.HOME);
  • 77. Use meaningful return types public interface EmployeesRegistry { enum PhoneType { HOME, MOBILE, OFFICE; } Map<String, Map<PhoneType, List<String>>> getEmployeesPhoneNumbers(); }
  • 78. Use meaningful return types public interface EmployeesRegistry { enum PhoneType { HOME, MOBILE, OFFICE; } Map<String, Map<PhoneType, List<String>>> getEmployeesPhoneNumbers(); } Employee name Employee’s phone numbers grouped by type List of phone numbers of a give type for a given employee Primitive obsession
  • 79. Use meaningful return types public interface EmployeesRegistry { enum PhoneType { HOME, MOBILE, OFFICE; } PhoneBook getPhoneBook(); } public class PhoneBook { private Map<String, EmployeeContacts> contacts; public EmployeeContacts getEmployeeContacts(String name) { return Optional.ofNullable( contacts.get(name) ) .orElse( EmptyContacts.INSTANCE ); } } public class EmployeeContacts { private Map<PhoneType, List<String>> numbers; public List<String> getNumbers(PhoneType type) { return Optional.ofNullable( numbers.get(type) ) .orElse( emptyList() ); } public static EmptyContacts INSTANCE = new EmptyContacts(); static class EmptyContacts extends EmployeeContacts { @Override public List<String> getNumbers(PhoneType type) { return emptyList(); } } }
  • 80. Optional – the mother of all bikeshedding
  • 81. Optional – the mother of all bikeshedding Principle of least astonishment??? "If a necessary feature has a high astonishment factor, it may be necessary to redesign the feature." - Cowlishaw, M. F. (1984). "The design of the REXX language"
  • 82. Optional – the mother of all bikeshedding Principle of least astonishment??? Wrong default
  • 83. Optional – the mother of all bikeshedding Principle of least astonishment??? Wrong default This could be removed if the other was correctly implemented
  • 84. API design is an iterative process and there could be different points of view ...
  • 85. … that could be driven by the fact that different people may weigh possible use cases differently...
  • 86. … or even see use cases to which you didn’t think at all
  • 87. Also a good API has many different characteristics ...
  • 88. … and they could be conflicting so you may need to trade off one to privilege another
  • 89. What should always drive the final decision is the intent of the API … but even there it could be hard to find an agreement
  • 90. ● Write lots of tests and examples against your API ● Discuss it with colleagues and end users ● Iterates multiple times to eliminate ➢ Unclear intentions ➢ Duplicated or redundant code ➢ Leaky abstraction API design is an iterative process
  • 91. ● Write lots of tests and examples against your API ● Discuss it with colleagues and end users ● Iterates multiple times to eliminate ➢ Unclear intentions ➢ Duplicated or redundant code ➢ Leaky abstraction Practice Dogfeeding API design is an iterative process
  • 92. And that’s all what you were getting wrong :) … questions? Mario Fusco Red Hat – Principal Software Engineer mario.fusco@gmail.com twitter: @mariofusco