developing useful apis
TRANSCRIPT
![Page 1: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/1.jpg)
Developing Useful APIsDmitry Buzdin
October 2013, Riga
![Page 3: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/3.jpg)
What is API?
![Page 4: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/4.jpg)
API is the code you use every day
![Page 5: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/5.jpg)
How many Jars do you have in your classpath?
from 20 to100 jars?
![Page 6: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/6.jpg)
Each library has its own API
!and personality
![Page 7: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/7.jpg)
Moreover, you create new reusable APIs inside your project
may be even in a separate module
![Page 8: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/8.jpg)
APIs are written by developers for
developers
![Page 9: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/9.jpg)
Everything is Open Source today!
![Page 10: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/10.jpg)
Frameworks are turning into micro-
frameworks
![Page 11: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/11.jpg)
I have to learn 100
APIs? !
Is it possible at all?
![Page 12: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/12.jpg)
Option I: lock yourself in a
dungeon
![Page 13: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/13.jpg)
The best code in the world is the one I wrote yesterday!
![Page 14: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/14.jpg)
Option II: learn to ride APIs
![Page 15: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/15.jpg)
Are all APIs different?
![Page 16: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/16.jpg)
Let’s try to understand...
![Page 17: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/17.jpg)
What is a Good API
• Easy to read
• Easy to use
• Hard to misuse
• Easy to extend
![Page 18: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/18.jpg)
How to Achieve That?
• Lets take a look at some:
• module/package level approaches
• class level patterns
• method/code level idioms
![Page 19: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/19.jpg)
Imagine building API for the next 10 years and
1000s of people
![Page 20: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/20.jpg)
Module Level
![Page 21: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/21.jpg)
Dependency Management
• Put less transitive dependencies
• Do you really need that commons-lang?
• Do you remember about Guava incompatibilities
Extreme example: OrientDB - zero dependencies!
![Page 22: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/22.jpg)
Packaging
com.acme.lib
PublicAPI.java
impl
PublicInterface.java
spi CustomListener.java
Internal.java
Util.java
PublicBean.java
Stuff you want to be
reused
Extension API
![Page 24: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/24.jpg)
Class Level
![Page 25: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/25.jpg)
Lets build an API for a reusable gadget
![Page 26: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/26.jpg)
Gadget gadget = new Gadget(name, id, options);!// Gadget is connected at this point
public Gadget(name, id, options) {! this.name = name;! this.id = id;! this.options = options;! connect(); // could throw an exception!}
Gadgets should always be connected
Can not create an instance for testing
![Page 27: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/27.jpg)
Create Gadget by static method or factory class
public Gadget(name, id, options) {! this.name = name;! this.id = id;! this.options = options; !}!!public static Gadget newGadget(name, id, options) {! Gadget gadget = new Gadget(name, id, options);! gadget.connect();! return gadget;!}!!public class GadgetFactory {! public Gadget newGadget(name, id, options) {! Gadget gadget = new Gadget(name, id, options);! gadget.connect();! return gadget;! } !}
![Page 28: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/28.jpg)
public static Gadget newGadget(name, id, options) {! Gadget gadget = new DefaultGadget(name, id, options);! gadget.connect();! return gadget;!}!!public interface Gadget {! void connect();!}!!public class DefaultGadget implements Gadget {! public void connect() {! }!}
Hide your gadget behind interface
Because you could change the implementation Details are well hidden
![Page 29: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/29.jpg)
public static Gadget newGadget(name, id, options) {! Gadget gadget = new DefaultGadget(name, id, options);! gadget.connect();! return gadget;!}!!public interface Gadget {! void connect();!}!!public final class DefaultGadget implements Gadget {! DefaultGadget() {! }! public void connect() {! }!}
Make it final with package-level constructor
Disallow unsanctioned
modification of your code
![Page 30: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/30.jpg)
Open Closed Principle
"software entities (classes, modules, functions, etc.) !should be open for extension, but closed for modification"
public final class DefaultGadget implements Gadget {! public void setStrategy(BehaviorStrategy s) {! // changes the behavior of this Gadget;! }!}
Allowing sanctioned
modifications
![Page 31: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/31.jpg)
Gadget gadget = Gadgets.newGadget(name, id, options);!// Gadget is connected at this point!!!// Similar APIs!Files.createFile(...); // JDK 7!Lists.newArrayList(...); // Guava
Resulting code
![Page 32: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/32.jpg)
Method Overloading
Gadget gadget = Gadgets.newGadget(name, id, options);!Gadget gadget = Gadgets.newGadget(id, options);!Gadget gadget = Gadgets.newGadget(name, options);!Gadget gadget = Gadgets.newGadget(id, enabled);!Gadget gadget = Gadgets.newGadget(id, name, enabled);!
What if different parameter combinations should be supported?
![Page 33: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/33.jpg)
public class GadgetBuilder() {!! // ...! Long id;! String name;!! GadgetBuilder withId(Long id) {! this.id = id;! return this;! }!! GadgetBuilder withName(String name) {! this.name = name;! return this;! }!! Gadget build() {! Gadget gadget = new GadgetImpl();! gadget.setId(id);! gadget.setName(name);! gadget.setOptions(options);! gadget.setEnabled(enabled)! }!}
Covering all possibilities
![Page 34: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/34.jpg)
// Quartz Trigger Builder Example!trigger = newTrigger()! .withIdentity("trigger3", "group1")! .withSchedule(cronSchedule("0 0/2 8-17 * * ?"))! .forJob("myJob", "group1")! .build();
// Constructing stuff using builder!Gadget gadget = new GadgetBuilder()! .withId(1)! .withName(“ok”)! .withOptions(options)! .build();
Much better now!
![Page 35: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/35.jpg)
Lets build a Gadget Service
![Page 36: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/36.jpg)
Gadget Servicepublic final class GadgetService {!! private static final GadgetService instance = new GadgetService();!! public static void getInstance() {! return instance;! }!! public void saveGadget(Gadget gadget) {! ...! }!}
Static fields may produce memory leaks Difficult to test code using that
![Page 37: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/37.jpg)
Gadget Servicepublic class GadgetServiceImpl implements GadgetService {! ! // To be called by factory method/class! public GadgetServiceImpl() {}!! public void saveGadget(Gadget gadget) {! ...! }!!}!!!public class MyClass {! @Inject! GadgetService service;!}
Everyone is using Dependency Injection now
![Page 38: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/38.jpg)
You do not know which Dependency Injection
framework developers will use!
Spring, Guice, CDI, Dagger, PicoContainer etc.
![Page 39: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/39.jpg)
Abstract DI
public interface BeanRegistry {!! void register(Class<?> type);!! <T> T getBean(Class<T> type);!! <T> Collection<T> getBeans(Class<T> type);!!}
https://github.com/DozerMapper/dozer/blob/master/core/src/main/java/org/dozer/inject/DozerBeanContainer.java
![Page 40: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/40.jpg)
Provide DI Bindings
• Write bindings for other frameworks
• Your beans are accessible
• Ready for the next big thing
public class SpringBeanRegistry implements BeanRegistry {! public SpringBeanRegistry(ApplicationContext context) {! //..! }!}
![Page 41: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/41.jpg)
Make your api Extensible
interface Plugin {! void init(Context context);!}
How to allow people to contribute extensions?
![Page 42: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/42.jpg)
Service Provider Interface
http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html
ServiceLoader<Plugin> pluginLoader = ServiceLoader.load(Plugin.class);!for (Plugin plugin : pluginLoader) {! plugin.init(context);!}
META-INF/services/lv.jug.api.Pluginlv.jug.api.plugin1.Plugin lv.jug.api.plugin2.Plugin!lv.jug.api.plugin3.Plugin
class MyExtensionPlugin implements Plugin {! @Override! void init(Context context) {! System.out.println(“Hello”);! }!}
![Page 43: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/43.jpg)
SPI Benefits
• No static initialization
• Automatic lookup in all Jars
• Everything is initialized in one place
![Page 44: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/44.jpg)
Annotation Scanning
• Mark extensions with custom annotations
• Use bytecode scanning library
@Extension
![Page 45: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/45.jpg)
final TypeReporter reporter = new TypeReporter() {!! @Override! public Class<? extends Annotation>[] annotations() {! return new Class[]{Extension.class};! }!! @Override! public void reportTypeAnnotation(Class<? extends Annotation> annotation, String className) {! // do something! }!!};!final AnnotationDetector cf = new AnnotationDetector(reporter);!cf.detect();
@Extension!class MyExtensionPlugin implements Plugin {! @Override! void init(Context context) {! System.out.println(“Hello”);! }!}
Finds all annotated classess
https://github.com/rmuller/infomas-asl
![Page 46: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/46.jpg)
Annotation Benefits
• Instantiating and using via reflection
• Easy API to explain
• Quite fast
![Page 47: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/47.jpg)
Method Level
![Page 48: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/48.jpg)
List<String> myList = new ArrayList<>();!updateList(myList);!return myList;
void updateList(List<String> items) {! for (Iterator<String> iterator=items.iterator(); iterator.hasNext();) {! String item = iterator.next() {! if (item.startsWith(“//”)) {! iterator.remove();! }! }!}
Modifying mutable parameters
![Page 49: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/49.jpg)
List<Item> myList = new ArrayList<>();!List<Item> updatedList = updateItems(myList);!return updatedList;
List<String> updateList(final List<String> items) {! List<String> result = new ArrayList<>();! for (String item : items) {! if (!item.startsWith(“//”)) {! result.add(item);! }! }! return result;!}
Considering all method
arguments as immutable
![Page 50: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/50.jpg)
try {! downloadPhoto(id, path);!} catch(ConnectionNotAvailable | PhotoNotFound e) {! System.out.println(“WAT!?”);!}
void downloadPhoto(String id, Path path) ! throws ConnectionNotAvailable, PhotoNotFound {! ...!}
Have to handle all exceptions
![Page 51: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/51.jpg)
downloadPhoto(id, path);
void downloadPhoto(String id, Path path) {! ...!}
Rely on Runtime Exceptions
![Page 52: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/52.jpg)
void uploadPhoto(byte[] photo, String name, String type) {! ...!}
What if photo is too big? What types are supported? RTFM?
![Page 53: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/53.jpg)
void uploadPhoto(InputStream photo,! String name, ! PhotoType type) {! ...!}!!public enum PhotoType {! JPEG, PNG!}
Pass binaries as InputStreams
Create enum for parameters
![Page 54: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/54.jpg)
PhotoService photoService = ...!photoService.openTransaction();!try {! photoService.uploadPhoto(...);! photoService.changeAttributes(...);!} catch (Exception e) {! photoService.rollbackTransaction();!} finally {! photoService.commitTransaction();!}
Just boring...
![Page 55: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/55.jpg)
PhotoService photoService = ...!photoService.doInTransaction(new Execution() {! public void work(Context context) {! context.uploadPhoto(...);! context.changeAttributes(...);! }!});!!public void doInTransaction(Execution execution) {! Context context = openTransactionContext();! try {! execution.work(context);! } catch (Exception e) {! context.rollback();! } finally {! context.commit();! }!}
Writing it once
![Page 56: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/56.jpg)
PhotoService photoService = ...!photoService.doInTransaction(context -> {! context.uploadPhoto(...);! context.changeAttributes(...);!});
Java 8
![Page 57: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/57.jpg)
public Object someServiceMethod() {! return transactionTemplate.execute(new TransactionCallback() {!! // the code in this method executes in a transactional context! public Object doInTransaction(TransactionStatus status) {! updateOperation1();! return resultOfUpdateOperation2();! }! });! }
Same approach in Spring Framework
![Page 58: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/58.jpg)
I learned something today!
![Page 59: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/59.jpg)
All APIs operate on single set of
rules & patterns
![Page 60: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/60.jpg)
APIs have fashion too
Java API from 1998
Java API from 2013
![Page 61: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/61.jpg)
You have to follow fashion trends to ride
APIs
![Page 62: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/62.jpg)
• Treat all reusable classes you write as API
• Use minimum API principle
• Learn by example from open source projects
![Page 63: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/63.jpg)
Recommended Books
![Page 64: Developing Useful APIs](https://reader034.vdocuments.mx/reader034/viewer/2022052505/554f391db4c905cd048b4f51/html5/thumbnails/64.jpg)