Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- public interface LazyEntity<V> {
- V getWrappedEntity();
- }
- /*
- есть конвеншенсы по именам типов-дженериков
- конвеншенcы
- http://stackoverflow.com/questions/2900881/generic-type-parameter-naming-convention-for-java-with-multiple-chars
- https://docs.oracle.com/javase/tutorial/java/generics/types.html
- тут уместнее
- LazyEntity<T>
- */
- ********************************************
- public interface LazyElementEntity extends LazyEntity<WebElement> {
- /*
- логичнее этот интерфейс назвать LazyElement
- с классами-лейзи-элементами
- как у нас будут обстоять дела
- лейзи-элементы любого вида - должны уметь то - что описано в interface LazyElementEntity
- но - получать вебелемент мы будем - по-разному (фактически - реазицация getWrappedEntity)
- например
- мы уже реализовали - getWebDriver().findElement()
- а будет еще и такой вариант - получить вебэлемент - для такого лейзи-элемента
- lazyElement.find(".destroy")
- lazyCollection.find(cssClass("edited"))
- lazyCollection.get(3)
- т е - в итоге - у нас будет несколько классов, имплементирующих interface LazyElementEntity
- причем - отличаться у них будут лишь конструкторы и реазицация getWrappedEntity
- а реализация остальных методов, да и вообще набор методов - один-в-один
- вот и получим такую иерархию
- самый общий интерфейс
- LazyEntity<T>
- его потомки - интерфейсы
- LazyElement
- LazyCollection
- абстрактные классы
- AbstractLazyElement
- AbstractLazyCollection
- (реализуем все методы, кроме абстрактного getWrappedEntity,
- ну и конструктор нам тут не нужен - т к класс абстрактный)
- набор классов - для элементов и коллекций
- наши уже разработанные варианты
- LazyWebDriverElement
- LazyWebDriverCollection
- список будет пополняться)
- получишь что-то такое http://joxi.ru/GrqLOX3SNZPLoA
- пока закрасила те классы - которые еще у нас не появилимь)
- ну и разумно - это в рамках core - выделить в отдельный пекедж
- wrappers - хорошее общее название
- т к вот эти наши классы - это лишь обертка вокруг вебэлемента/списка вебэлементов
- есть конвеншенсы и по этому поводу - какие имена давать интерфейсу, какие - абстракному предку,
- какие - классам-потомкам
- статья про это (лучший вариант)
- http://www.vertigrated.com/blog/2011/02/interface-and-class-naming-anti-patterns-java-naming-convention-tautologies/
- еще полезные линки
- http://stackoverflow.com/questions/2814805/java-interfaces-implementation-naming-convention
- https://web.archive.org/web/20130331071928/http://isagoksu.com/2009/development/java/naming-the-java-implementation-classes
- http://programmers.stackexchange.com/questions/152759/should-i-add-an-abstract-prefix-to-my-abstract-classes
- Interfaces, abstract classes - good practice
- http://stackoverflow.com/questions/1932247/abstract-classes-and-interfaces-best-practices-in-java
- http://joxi.ru/E2pdR1lFB1pyM2
- */
- ******************************************
- @Override
- public List<WebElement> shouldHave(Condition<List<WebElement>> condition) {
- List<WebElement> elements;
- elements = waitFor(this).until(condition);
- return elements;
- }
- /*
- можно написать в одну строку
- return waitFor(this).until(condition);
- а еще лучше - объявляй в интерфейсе и имплементируй метод с параметром Condition<List<WebElement>>... conditions
- кстати, заметь)
- мы немного смухлевали в структуре для кондишенов)
- CollectionCondition & ElementCondition - это как раз абстрактные классы-потомки конечных кондишенов
- но - в именах этих классов - мы не указывали Abstract
- да и методы, возвращающие кондишены - нам возвращают не значение типа интерфейс - а
- CollectionCondition или ElementCondition
- Если заморочиться на полную - можно и это подправить)
- переименовать классы - CollectionCondition & ElementCondition
- в AbstractCollectionCondition & AbstractElementCondition
- а для того, чтобы наш фреймворк оперировал интерфейсными типами для кондишенов -
- дореализуем интерфейсы
- CollectionCondition & ElementCondition
- и уже методы из CollectionConditions & ElementConditions - будут возвращать значение типа интерфейс
- CollectionCondition & ElementCondition
- и в качестве типов параметров у методов should - будут использованы интерфейсы CollectionCondition & ElementCondition
- ты, кстати, с интерфейсами фишку словила - параметр метода shouldHave - интерфейс Condition<List<WebElement>>
- молодец)
- ну и получаем варианты
- 1 - занудно выполнить все конвеншенсы - как я выше описала
- 2 - оставить как сейчас + использовать типы абстрактных классов CollectionCondition & ElementCondition -
- как типы возвращаемых методами значений и типы параметров методов
- Да, нарушены конвеншенсы
- но, дело в том, что и в первом варианте - тоже не совсем все гладко)
- ведь добавляемые в вариант 1 интерфейсы CollectionCondition & ElementCondition -
- не используются как интерфейсы, от которых имплементированы классы
- т к оба варианта - с недостатками
- но второй - лаконичнее - разумно использовать второй
- а если хочется заморочиться и потренироваться - тогда первый))
- */
- *******************************
- @Override
- public WebElement shouldBe() {
- return null;
- }
- /*
- добавь жизни этому методу))
- параметры и реализацию
- */
- ************************************
- public WaitFor(By locator) {
- this.entity = new LazyElement(locator);
- }
- /*
- уже не нужен
- посмотри = по-моему, не используется нигде
- */
- **************************************
- public abstract class CollectionCondition extends AbstractCondition<List<WebElement>> {
- ....
- // @Override
- // public List<WebElement> getWrappedEntity() {
- // return getWebDriver().findElements(locator);
- // }
- /*
- избавляйся от этих комментариев - у кондишенов такого зверя теперь не будет)
- теперь все по уму - лейзи-объекты такое сами про себя рассказывают
- */
- *********************************
- public class MinimumSizeOf extends SizeOf {
- ...
- @Override
- public List<WebElement> check(List<WebElement> wrappedEntity) {
- /*
- а вот тут нам плохую службу сослужил рефакторинг)
- в конечных кондишенах - лучше бы параметр назывался - elements - для списка
- и element - для элемента
- пробегись и поправь
- а вот в рамках абстрактного класса термин wrappedEntity для этого параметра - наиболее точный
- */
- ******************************************
- public abstract class AbstractCondition<V> implements Condition<V>{
- protected LazyEntity entity;
- ....
- // public String toString() {
- // return String.format(getClass().getSimpleName() +
- // "\nfor " + identity() + " found by " + locator +
- // (expected() == "" ? "" : "\nexpected: " + expected()) +
- // (actual() == "" ? "" : "\nactual: " + actual()));
- // }
- /*
- а вот это - зря закомментила)
- ну да, тут у нас нету локатора)
- но
- у нас есть entity
- если у всех классов, имплементирующих LazyEntity - будет нормально реализованнй метод toString()
- то и наша эта фраза будет ок - мы рсто будем использовать вместо locator - entity
- */
- ********************************************
- public class LazyCollection implements LazyCollectionEntity {
- public class LazyElement implements LazyElementEntity {
- /*
- тут в toString() - можно вернуть просто - locator.toString();
- */
- **************************************************
- /*
- следующий наворот )
- WebElement - это тоже интерфейс
- Если мы отнаследуем наш интерфейс LazyElement от WebElement
- Это то же самое что и заявить - "наш LazyElement - полная замена WebElement - и везде где вы его использовали
- вы теперь можете использовать наш лейзи враппер
- который помимо всех фич вебэлемента еще и будет ждать себя..."
- конечно, можно было бы абстрактный класс реализовать имплементирующим два интерфейса
- Класс может быть реализован от нескольких интерфейсов
- но тогда, при использовании переменной типа LazyElement - мы не получим функциональности WebElement
- потому тут правильнее именно отнаследовать LazyElement от WebElement
- Это в java-ООП мире достаточно прикольная практика -
- всегда, когда ты хочешь сделать какие-то штуки внутри объекта публичными -
- выносить их сначала в отдельный интерфейс - и потом его имплементировать
- потому что просто сделать какие-то штуки публичными - это недостаточно "строго"
- если ты четко заявляешь - мой объект поддерживает такие-то интерфейсы, тогда
- все могут общаться с твоим объектом через четко прописанное API этого интерфейса
- тут надо внимательно к вопросу подойти)
- в рамках LazyElement - не реализовывай методов, одноименных с методами WebElement
- да, мы для некоторых методов потерям chainable возможности
- но зато не будет двусмысленностей
- в абстрактном классе AbstractLazyElement - реализуй все вебэлементовские методы
- думаю, проблем с этим не будет
- думай - в каких методах и чего надо ждать - ничего не ждать / ждать видимости / ждать наличия в DOM
- */
- **********************
- /*
- следующий наворот )
- наворачиваем Collection )
- наследуем этот интерфейс и от Iterable<LazyElement>
- цель - уметь писать такой код
- elements = $$(...);
- for(LazyElement element:elements) {
- ...
- }
- т е уметь обходить нашу коллекцию элементов в таком варианте цикла
- чистая теория
- http://tutorials.jenkov.com/java-generics/implementing-iterable.html
- нам она практически не пригодится )
- просто для понимания - разумно ознакомиться
- достаточно понять, что
- при имплементировании интерфейса Iterable<...>
- надо будет реализовать метод
- public Iterator<ProxyElement> iterator()
- Дальше мы схитрим)
- List<...> тоже наследник Iterable<...>
- если мы внутри метода iterator()
- создадим и заполним список list типа List<LazyElement> элементами из коллекции
- то тогда можно вернуть в качестве результата метода iterator() - list.iterator();
- загвоздка в том, что в коллекции getWrappedEntity() возвращает значение типа List<WebElement>
- а мы хотим получить List<LazyElement>
- Вот и пришло время создать еще одного потомка для AbstractLazyElement
- назовем его LazyWrappedWebElement (обернутый веб элемент)
- будем передавать ему в конструктор вебЭлемент
- как ты понимаешь, его реазизация getWrappedEntity() - будет возвращать тот же веб элемент
- И теперь опять возвращаемся к методу iterator()
- Если в новый список List<LazyElement>
- Ты будешь складывать новосозданные объекты типа LazyWrappedWebElement
- То это будет ОК (т к LazyWrappedWebElement - потомок абстрактного класса который имплементирует интерфейс LazyElement)
- Проверяем новую функциональность на коде типа
- for (LazyElement element:tasks) {
- System.out.println(element.getText());
- element.shouldHave(text("а"));
- }
- */
- **********************************
- public abstract class AbstractCondition<V> implements Condition<V>{
- ...
- public abstract String identity();
- public abstract String expected();
- public abstract String actual();
- /*
- ну и еще один наворот на тему интерфейсов )
- как выше писала - класс может имплементировать несколько интерфейсов
- и прикольно - всю функциональность описывать на уровне интерфейсов
- это дает ряд интересных полезных эффектов
- вот тут например -
- описать интерфейс DescribesResult
- с методами identity(), expected() и actual()
- и пусть класс AbstractCondition<V> имплементирует как Condition<V>, так и DescribesResult
- первая мелочь - можно описания абстрактных методов отсюда убрать
- (они же и так объявлены в интерфейсе, а реализовывать мы будем в кондишенах уже)
- вторая приятная и важная вещь - соблюли лучше Single Responsibility Principle
- третья приятная вещь
- конечно, при условии, если таки для переменных-кондишенов будем использовать типы интерфейсов
- CollectionCondition & ElementCondition (выше описаннный вариант 1)
- когда мы будем оперировать переменной
- CollectionCondition condition
- или
- ElementCondition condition
- то для такой переменной - мы не сможем вызвать методы identity() / expected() / actual()
- т е - там, где это было нужно - у нас эта функциональность была видна
- а там, где это не нужно - этого вообще не видно
- */
Advertisement
Add Comment
Please, Sign In to add comment