API design in java project

368 views

Published on

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

  • Be the first to like this

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

No notes for slide

API design in java project

  1. 1. Разработка API в Java-проекте: как оказывать влияние на людей и не приобрести врагов Чашников Николай программист JetBrains Nikolay.Chashnikov@jetbrains.com
  2. 2. Как возникает API class B class A
  3. 3. Как возникает API class B class A
  4. 4. Как возникает API class B class A
  5. 5. К чему приводят недостатки API API неудачное, если ● его трудно понимать ● его неудобно использовать ● с ним легко допустить ошибку ● его проблематично менять ● оно недостаточно выразительно
  6. 6. Свойства хорошего API ● его легко понимать ● его удобно использовать ● оно защищает от ошибок ● его можно безболезненно менять ● оно достаточно мощное
  7. 7. В API — только необходимое Добавить в API просто, убрать что-то из API очень трудно. Нельзя выделять что-то в API просто “чтобы было”, “а вдруг понадобится”.
  8. 8. API не должно содержать реализацию Все методы классов в API должны быть ● либо abstract ● либо возвращать значение по умолчанию ● либо делегироваться к другим методам API
  9. 9. API не должно содержать реализацию ● отсутствие тел методов облегчает читаемость ● отсутствие тел методов упрощает наследование ● код, вынесенный в API, становится частью контракта
  10. 10. Наследование от классов API Для каждого интерфейса в API должно быть чётко установлено, предполагается ли его наследование пользователями API. Разрешать наследование только при необходимости.
  11. 11. Наследование от классов API public abstract class Car { private final String manufacturer; private final String model; public Car(String manufacturer, String model){ this.manufacturer = manufacturer; this.model = model; } public abstract void playEngineRoar(); public String getManufacturer() { return manufacturer; } public String getModel() { return model; } }
  12. 12. Наследование от классов API public class AudiQ7Diesel extends Car { public AudiQ7Diesel() { super("Audi", "Q7"); } public void playEngineRoar() { ... } }
  13. 13. Наследование от классов API public interface Car { String getManufacturer(); String getModel(); CarEngine getEngine(); } public interface CarEngine { void playEngineRoar(); } public interface CarFactory { Car createCar(String model, String manufacturer, CarEngine engine); }
  14. 14. Запрещать всё, что не разрешено ● использовать наиболее закрытые модификаторы доступа ● делать final всё, что не предполагается переопределять
  15. 15. Ешьте своё API сами. Дважды Параллельно с созданием API надо писать минимум два примера его использования.
  16. 16. Мощь и простота использования Easy things should be easy, and hard things should be possible
  17. 17. Как проще всего прочитать в String текст файла (в Java 6)? byte[] bytes = new byte[(int) file.length()]; DataInputStream input = new DataInputStream( new FileInputStream(file)); input.readFully(bytes); input.close(); return new String(bytes, "UTF-8");
  18. 18. В Java 7 это стало проще return new String( Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);
  19. 19. Упрощайте доступ к нужным методам public interface Form { /** use {@link Validators} */ void check(); } public class Validators { public static boolean isValidEmail(String s){…} public static boolean isValidPhoneNumber (String s) { … } }
  20. 20. Упрощайте доступ к нужным методам public class ContactsForm implements Form { ... public void check() { String phone = getPhoneNumberText(); if (!Validators.isValidPhoneNumber(phone)) { ... } } ... }
  21. 21. Упрощайте доступ к нужным методам public interface Form { void check(Validators validators); } public interface Validators { boolean isValidEmail(String s); boolean isValidPhoneNumber(String s); }
  22. 22. Упрощайте доступ к нужным методам public class ContactsForm implements Form { ... public void check(Validators validators) { String phone = getPhoneNumberText(); if (!validators.isValidPhoneNumber(phone)) { ... } } ... }
  23. 23. Защита от ошибок ● в случае ошибки падать как можно раньше ○ в идеале — при компиляции ● не использовать String и boolean, если есть более подходящие типы ● использовать generics вместо casts и Object ● использовать аннотации @NotNull, @Nullable
  24. 24. Слабая типизация public interface AssortedData { void putData(String key, Object data); Object getData(String key); }
  25. 25. Сильная типизация final class Key<T> {} public interface AssortedData { <T> void putData(Key<T> key, T data); <T> T getData(Key<T> key); }
  26. 26. Как при использовании этого класса можно ошибиться? public class Shop { private List<Order> orders = new ArrayList<Order>(); private Order[] ordersArray; public void addOrder(Order order) { orders.add(order); ordersArray = null; } public Order[] getOrders() { if (ordersArray == null) ordersArray = orders.toArray(new Order[orders.size()]); return ordersArray; } }
  27. 27. Например, вот так Order[] orders = shop.getOrders(); … … Arrays.sort(orders);
  28. 28. А вот как от этого защититься public class Shop { private List<Order> orders = new ArrayList<Order>(); private List<Order> unmodifiableOrders = Collections.unmodifiableList(orders); public void addOrder(Order order) { orders.add(order); } public List<Order> getOrders() { return unmodifiableOrders; } }
  29. 29. Тот номер больше не пройдёт List<Order> orders = shop.getOrders(); … … Collections.sort(orders); //вылетит UnsupportedOperationException
  30. 30. Защита от ошибок ● избегать неявных ограничений ● использовать immutable объекты
  31. 31. Навязывать нужное поведение public class DoNotOverrideEquals { public final boolean equals(Object obj) { return super.equals(obj); } public final int hashCode() { return super.hashCode(); } }
  32. 32. Навязывать нужное поведение public abstract class MustOverrideEquals { public abstract boolean equals( Object obj); public abstract int hashCode(); }
  33. 33. Изменение API При изменении методов интерфейсов, от которых не наследуется пользователь, оставлять старые варианты с пометкой deprecated.
  34. 34. Изменение API: наследование public interface Cat { void stroke(); } Как добавить параметр в метод, не ломая совместимости?
  35. 35. Изменение API: наследование /** @deprecated implement Cat2 instead */ public interface Cat { void stroke(); } public interface Cat2 extends Cat { void stroke(double force); }
  36. 36. Изменение API: наследование if (cat instanceof Cat2) { ((Cat2)cat).stroke(0.5); } else { cat.stroke(); }
  37. 37. Изменение API: наследование public abstract class AbstractCat implements Cat { /** @deprecated override stroke(double) instead */ public void stroke() {} public void stroke(double force){ stroke(); } }
  38. 38. Изменение API: наследование Безопасное расширение интерфейсов, от которых наследуется пользователь, возможно только в случае, если у них есть базовый класс, от которого все наследуются. Ждём Java 8 и default methods в интерфейсах.
  39. 39. API в виде Internal DSL Internal DSL (Domain Specific Language) — код на Java, который не похож на обычный Java-код
  40. 40. Обычные сеттеры SearchQuery query = new SearchQuery("java"); query.setSite("http://oracle.com"); query.setSortingCriteria( SortingCriteria.RELEVANCE); query.setMaximumResults(10); SearchResult result = runSearch(query);
  41. 41. DSL на билдерах SearchResult result = search("java") .onSite("http://oracle.com") .sortByRelevance() .returnFirst(10) .run();
  42. 42. DSL на билдерах: реализация public class SearchQuery { private final String text; private String site; private SearchQuery(String text) { this.text = text; } public static SearchQuery search(String text) { return new SearchQuery(text); } public SearchQuery onSite(String site) { this.site = site; return this; } ... }
  43. 43. DSL на билдерах: сложный случай Условие на место в синтаксическом дереве в IntelliJ IDEA: http://tinyurl.com/intellij-api-1
  44. 44. DSL для описания XML <web-app version="2.5"> <servlet> <servlet-name>Sample</servlet-name> <servlet-class>...</servlet-class> </servlet> </web-app>
  45. 45. DSL на интерфейсах public interface WebApp { Servlet addServlet(); List<Servlet> getServlets(); } public interface Servlet { String getServletName(); void setServletName(String name); }
  46. 46. DSL на интерфейсах: реализация java.lang.reflect.Proxy.newProxyInstance (classLoader, interfaces, handler) public interface InvocationHandler { Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
  47. 47. DSL на интерфейсах: реализация AdvancedProxy в IntelliJ IDEA platform: http://tinyurl.com/intellij-api-2
  48. 48. Правила разработки API ● в API — только необходимое ● реализации не место в API ● ограничивать наследование от классов API ○ выделять базовый класс для наследования ● ● ● ● ● ешьте своё API сами; дважды типичные задачи должны решаться просто в случае ошибки падать как можно раньше использовать мощь системы типов подумать о DSL
  49. 49. Вопросы? Если возникнут позже, пишите Nikolay.Chashnikov@jetbrains.com

×