Developing Useful APIs

578 views
450 views

Published on

Published in: Software
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
578
On SlideShare
0
From Embeds
0
Number of Embeds
15
Actions
Shares
0
Downloads
2
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Developing Useful APIs

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

×