49
Разработка API в Java-проекте: как оказывать влияние на людей и не приобрести врагов Чашников Николай программист JetBrains [email protected]

API design in java project

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: API design in java project

Разработка API в Java-проекте:как оказывать влияние на людей

и не приобрести врагов

Чашников Николайпрограммист

[email protected]

Page 2: API design in java project

Как возникает API

class B class A

Page 3: API design in java project

Как возникает API

class B class A

Page 4: API design in java project

Как возникает APIclass B class A

Page 5: API design in java project

К чему приводят недостатки API

API неудачное, если

● его трудно понимать

● его неудобно использовать

● с ним легко допустить ошибку

● его проблематично менять

● оно недостаточно выразительно

Page 6: API design in java project

Свойства хорошего API

● его легко понимать

● его удобно использовать

● оно защищает от ошибок

● его можно безболезненно менять

● оно достаточно мощное

Page 7: API design in java project

В API — только необходимое

Добавить в API просто, убрать что-то из API очень трудно.

Нельзя выделять что-то в API просто “чтобы было”, “а вдруг понадобится”.

Page 8: API design in java project

API не должно содержать реализацию

Все методы классов в API должны быть● либо abstract● либо возвращать значение по умолчанию● либо делегироваться к другим методам

API

Page 9: API design in java project

API не должно содержать реализацию

● отсутствие тел методов облегчает читаемость

● отсутствие тел методов упрощает наследование

● код, вынесенный в API, становится частью контракта

Page 10: API design in java project

Наследование от классов API

Для каждого интерфейса в API должно быть чётко установлено, предполагается ли его наследование пользователями API.Разрешать наследование только при необходимости.

Page 11: API design in java project

Наследование от классов 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

Наследование от классов APIpublic class AudiQ7Diesel extends Car { public AudiQ7Diesel() { super("Audi", "Q7"); }

public void playEngineRoar() { ...

}

}

Page 13: API design in java project

Наследование от классов 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

Запрещать всё, что не разрешено

● использовать наиболее закрытые модификаторы доступа

● делать final всё, что не предполагается переопределять

Page 15: API design in java project

Ешьте своё API сами. Дважды

Параллельно с созданием API надо писать минимум два примера его использования.

Page 16: API design in java project

Мощь и простота использования

Easy things should be easy, and hard things should be possible

Page 17: API design in java project

Как проще всего прочитать в 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

В Java 7 это стало проще

return new String( Files.readAllBytes(file.toPath()),

StandardCharsets.UTF_8);

Page 19: API design in java project

Упрощайте доступ к нужным методам

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

Упрощайте доступ к нужным методам

public class ContactsForm implements Form { ...

public void check() { String phone = getPhoneNumberText();

if (!Validators.isValidPhoneNumber(phone)) { ...

}

}

...

}

Page 21: API design in java project

Упрощайте доступ к нужным методам

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

Упрощайте доступ к нужным методам

public class ContactsForm implements Form { ...

public void check(Validators validators) { String phone = getPhoneNumberText();

if (!validators.isValidPhoneNumber(phone)) { ...

}

}

...

}

Page 23: API design in java project

Защита от ошибок● в случае ошибки падать как можно

раньше ○ в идеале — при компиляции

● не использовать String и boolean, если есть более подходящие типы

● использовать generics вместо casts и Object

● использовать аннотации @NotNull, @Nullable

Page 24: API design in java project

Слабая типизация

public interface AssortedData { void putData(String key, Object data); Object getData(String key);}

Page 25: API design in java project

Сильная типизация

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

Как при использовании этого класса можно ошибиться?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

Например, вот так

Order[] orders = shop.getOrders();

Arrays.sort(orders);

Page 28: API design in java project

А вот как от этого защититься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

Тот номер больше не пройдёт

List<Order> orders = shop.getOrders();

Collections.sort(orders);

//вылетит UnsupportedOperationException

Page 30: API design in java project

Защита от ошибок

● избегать неявных ограничений● использовать immutable объекты

Page 31: API design in java project

Навязывать нужное поведение

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

Навязывать нужное поведение

public abstract class MustOverrideEquals { public abstract boolean equals( Object obj);

public abstract int hashCode();}

Page 33: API design in java project

Изменение API

При изменении методов интерфейсов, от которых не наследуется пользователь, оставлять старые варианты с пометкой deprecated.

Page 34: API design in java project

Изменение API: наследование

public interface Cat { void stroke();}

Как добавить параметр в метод, не ломая совместимости?

Page 35: API design in java project

Изменение 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

Изменение API: наследование

if (cat instanceof Cat2) { ((Cat2)cat).stroke(0.5);}else { cat.stroke();}

Page 37: API design in java project

Изменение 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

Изменение API: наследование

Безопасное расширение интерфейсов, от которых наследуется пользователь, возможно только в случае, если у них есть базовый класс, от которого все наследуются.

Ждём Java 8 и default methods в интерфейсах.

Page 39: API design in java project

API в виде Internal DSL

Internal DSL (Domain Specific Language) — код на Java, который не похож на обычный Java-код

Page 40: API design in java project

Обычные сеттеры

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

DSL на билдерах

SearchResult result =

search("java") .onSite("http://oracle.com") .sortByRelevance()

.returnFirst(10)

.run();

Page 42: API design in java project

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

DSL на билдерах: сложный случай

Условие на место в синтаксическом дереве в IntelliJ IDEA: http://tinyurl.com/intellij-api-1

Page 44: API design in java project

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

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

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

DSL на интерфейсах: реализация

AdvancedProxy в IntelliJ IDEA platform: http://tinyurl.com/intellij-api-2

Page 48: API design in java project

Правила разработки API● в API — только необходимое● реализации не место в API● ограничивать наследование от классов API

○ выделять базовый класс для наследования● ешьте своё API сами; дважды● типичные задачи должны решаться просто● в случае ошибки падать как можно раньше● использовать мощь системы типов● подумать о DSL

Page 49: API design in java project

Вопросы?

Если возникнут позже, пишите[email protected]