Developing Useful APIs

64
Developing Useful APIs Dmitry Buzdin October 2013, Riga

Transcript of Developing Useful APIs

Page 1: Developing Useful APIs

Developing Useful APIsDmitry Buzdin

October 2013, Riga

Page 2: Developing Useful APIs

Dmitry Buzdin

[email protected]

@buzdin

www.buzdin.lv

Page 3: Developing Useful APIs

What is API?

Page 4: Developing Useful APIs

API is the code you use every day

Page 5: Developing Useful APIs

How many Jars do you have in your classpath?

from 20 to100 jars?

Page 6: Developing Useful APIs

Each library has its own API

!and personality

Page 7: Developing Useful APIs

Moreover, you create new reusable APIs inside your project

may be even in a separate module

Page 8: Developing Useful APIs

APIs are written by developers for

developers

Page 9: Developing Useful APIs

Everything is Open Source today!

Page 10: Developing Useful APIs

Frameworks are turning into micro-

frameworks

Page 11: Developing Useful APIs

I have to learn 100

APIs? !

Is it possible at all?

Page 12: Developing Useful APIs

Option I: lock yourself in a

dungeon

Page 13: Developing Useful APIs

The best code in the world is the one I wrote yesterday!

Page 14: Developing Useful APIs

Option II: learn to ride APIs

Page 15: Developing Useful APIs

Are all APIs different?

Page 16: Developing Useful APIs

Let’s try to understand...

Page 17: Developing Useful APIs

What is a Good API

• Easy to read

• Easy to use

• Hard to misuse

• Easy to extend

Page 18: Developing Useful APIs

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

Imagine building API for the next 10 years and

1000s of people

Page 20: Developing Useful APIs

Module Level

Page 21: Developing Useful APIs

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

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 23: Developing Useful APIs

Licensing

http://choosealicense.com/

Page 24: Developing Useful APIs

Class Level

Page 25: Developing Useful APIs

Lets build an API for a reusable gadget

Page 26: Developing Useful APIs

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

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

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

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

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

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

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

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

// 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

Lets build a Gadget Service

Page 36: Developing Useful APIs

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

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

You do not know which Dependency Injection

framework developers will use!

Spring, Guice, CDI, Dagger, PicoContainer etc.

Page 39: Developing Useful APIs

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

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

Make your api Extensible

interface Plugin {! void init(Context context);!}

How to allow people to contribute extensions?

Page 42: Developing Useful APIs

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

SPI Benefits

• No static initialization

• Automatic lookup in all Jars

• Everything is initialized in one place

Page 44: Developing Useful APIs

Annotation Scanning

• Mark extensions with custom annotations

• Use bytecode scanning library

@Extension

Page 45: Developing Useful APIs

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

Annotation Benefits

• Instantiating and using via reflection

• Easy API to explain

• Quite fast

Page 47: Developing Useful APIs

Method Level

Page 48: Developing Useful APIs

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

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

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

downloadPhoto(id, path);

void downloadPhoto(String id, Path path) {! ...!}

Rely on Runtime Exceptions

Page 52: Developing Useful APIs

void uploadPhoto(byte[] photo, String name, String type) {! ...!}

What if photo is too big? What types are supported? RTFM?

Page 53: Developing Useful APIs

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

PhotoService photoService = ...!photoService.openTransaction();!try {! photoService.uploadPhoto(...);! photoService.changeAttributes(...);!} catch (Exception e) {! photoService.rollbackTransaction();!} finally {! photoService.commitTransaction();!}

Just boring...

Page 55: Developing Useful APIs

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

PhotoService photoService = ...!photoService.doInTransaction(context -> {! context.uploadPhoto(...);! context.changeAttributes(...);!});

Java 8

Page 57: Developing Useful APIs

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

I learned something today!

Page 59: Developing Useful APIs

All APIs operate on single set of

rules & patterns

Page 60: Developing Useful APIs

APIs have fashion too

Java API from 1998

Java API from 2013

Page 61: Developing Useful APIs

You have to follow fashion trends to ride

APIs

Page 62: Developing Useful APIs

• Treat all reusable classes you write as API

• Use minimum API principle

• Learn by example from open source projects

Page 63: Developing Useful APIs

Recommended Books

Page 64: Developing Useful APIs