• Like
Developing Useful APIs
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

Developing Useful APIs

  • 156 views
Published

 

Published in Software
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
156
On SlideShare
0
From Embeds
0
Number of Embeds
1

Actions

Shares
Downloads
0
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Developing Useful APIs Dmitry Buzdin October 2013, Riga
  • 2. Dmitry Buzdin buzdin@gmail.com @buzdin www.buzdin.lv
  • 3. What is API?
  • 4. API is the code you use every day
  • 5. How many Jars do you have in your classpath? from 20 to100 jars?
  • 6. Each library has its own API ! and personality
  • 7. Moreover, you create new reusable APIs inside your project may be even in a separate module
  • 8. APIs are written by developers for developers
  • 9. Everything is Open Source today!
  • 10. Frameworks are turning into micro- frameworks
  • 11. I have to learn 100 APIs? ! Is it possible at all?
  • 12. Option I: lock yourself in a dungeon
  • 13. The best code in the world is the one I wrote yesterday!
  • 14. Option II: learn to ride APIs
  • 15. Are all APIs different?
  • 16. Let’s try to understand...
  • 17. What is a Good API • Easy to read • Easy to use • Hard to misuse • Easy to extend
  • 18. How to Achieve That? • Lets take a look at some: • module/package level approaches • class level patterns • method/code level idioms
  • 19. Imagine building API for the next 10 years and 1000s of people
  • 20. Module Level
  • 21. Dependency Management • Put less transitive dependencies • Do you really need that commons-lang? • Do you remember about Guava incompatibilities Extreme example: OrientDB - zero dependencies!
  • 22. 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
  • 23. Licensing http://choosealicense.com/
  • 24. Class Level
  • 25. Lets build an API for a reusable gadget
  • 26. 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
  • 27. 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;! } ! }
  • 28. 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
  • 29. 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
  • 30. 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
  • 31. Gadget gadget = Gadgets.newGadget(name, id, options);! // Gadget is connected at this point! ! ! // Similar APIs! Files.createFile(...); // JDK 7! Lists.newArrayList(...); // Guava Resulting code
  • 32. 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?
  • 33. 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
  • 34. // 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!
  • 35. Lets build a Gadget Service
  • 36. Gadget Service public 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
  • 37. Gadget Service public 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
  • 38. You do not know which Dependency Injection framework developers will use! Spring, Guice, CDI, Dagger, PicoContainer etc.
  • 39. 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
  • 40. 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) {! //..! }! }
  • 41. Make your api Extensible interface Plugin {! void init(Context context);! } How to allow people to contribute extensions?
  • 42. 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.Plugin lv.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”);! }! }
  • 43. SPI Benefits • No static initialization • Automatic lookup in all Jars • Everything is initialized in one place
  • 44. Annotation Scanning • Mark extensions with custom annotations • Use bytecode scanning library @Extension
  • 45. 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
  • 46. Annotation Benefits • Instantiating and using via reflection • Easy API to explain • Quite fast
  • 47. Method Level
  • 48. 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
  • 49. 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
  • 50. 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
  • 51. downloadPhoto(id, path); void downloadPhoto(String id, Path path) {! ...! } Rely on Runtime Exceptions
  • 52. void uploadPhoto(byte[] photo, String name, String type) {! ...! } What if photo is too big? What types are supported? RTFM?
  • 53. void uploadPhoto(InputStream photo,! String name, ! PhotoType type) {! ...! }! ! public enum PhotoType {! JPEG, PNG! } Pass binaries as InputStreams Create enum for parameters
  • 54. PhotoService photoService = ...! photoService.openTransaction();! try {! photoService.uploadPhoto(...);! photoService.changeAttributes(...);! } catch (Exception e) {! photoService.rollbackTransaction();! } finally {! photoService.commitTransaction();! } Just boring...
  • 55. 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
  • 56. PhotoService photoService = ...! photoService.doInTransaction(context -> {! context.uploadPhoto(...);! context.changeAttributes(...);! }); Java 8
  • 57. 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
  • 58. I learned something today!
  • 59. All APIs operate on single set of rules & patterns
  • 60. APIs have fashion too Java API from 1998 Java API from 2013
  • 61. You have to follow fashion trends to ride APIs
  • 62. • Treat all reusable classes you write as API • Use minimum API principle • Learn by example from open source projects
  • 63. Recommended Books