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