Велике інтерв'ю з Ханнесом Дорфманом, творцем фреймворку Mosby для Android

2 червня в московському офісі Яндекса пройде чергова Droid Party. Цього разу своїм досвідом поділиться Ханнес Дорфман. Багатьом з вас він відомий як розробник фреймворку Mosby для Android. Ханннес багато часу приділяє дослідженню підходів до Андроїд-архітектурі.

Напередодні заходу ми попросили Ханнеса відповісти на питання, які зібрали від розробників всередині Яндекса. Інтерв'ю вийшло великим і цікавим. Обговорили майбутнє мов програмування, отримали багато прикладних рад і навіть згадали вже легендарні моделі Nokia. Читайте детальніше під катом.

Для тих, хто не зможе потрапити на Droid Party, ми за традицією організуємо трансляцію, яку можна буде подивитися здесь. Там же можна зареєструватися на захід. А ті, хто живе в Пітері, зможуть взяти участь у телемості в нашому пітерському офісі.

Як звичайно, ви можете задавати в коментарях свої питання — ми передамо їх Ганнесу, і він відповість на них на Droid Party.

Інтерв'ю в оригіналі, для тих, хто віддає перевагу читати на англійській мові (English version)How did you start to code for Android?

I was always зачарований by mobile app development. I developed my first «app» at high school on symbian with qt and C++ and also did a little bit of j2me. I also had a pocket pc running Windows CE to hack around. Hence, I was excited when the first smartphones and iPhones came out, but I can't really say that android and me were love at first sight. The first time I first programmed for Android (Android 1.5 — cupcake) when I was at college in summer 2009, but I was all like where is my «main()» method, what is all that xml stuff and what the hell is this findViewById() thing. So I dropped Android for almost half a year until my Nokia N95 was not working anymore in late 2009 and it was time to buy a smartphone. iPhone was never my cup of tea and I really wanted to have a hardware keyboard on my device. I couldn't imagine to type long text on the screen. There were two options: Either the original Motorola Droid (Milestone) running Android 2.0 or Nokia N900 running Maemo. Maemo also used C++ and qt so I was very close to pick the N900 but at the end I chose the Motorola Droid, mainly because Maemo OS hadn't MMS support which obviously was the most important thing on earth :) And as soon as I had the Motorola Droid in hand I started developing for Android 2.0 (for personal only).

What is your opinion on adopting architectural solutions from JS?

JavaScript developers have something in common with the Android developers: They have a platform / SDK with basically zero guidelines from an software architectural point of view. A greenfield. The JavaScript world is still searching for the holy grail of software architecture, therefore we see almost every 3 months new JS framework and patterns arising and disappear. But some design patterns like Model-View-Intent (cycle.js), Redux and Flux introduced some really great ideas, which definitely are worth while to check out and it could makes sense to adopt the idea behind a pattern or framework on other platforms like Android too. On Android we tend to reinvent the wheel again and again and not always in a better way. In general as we developers should think outside the box and see what other platforms do to learn from them and even more important to learn from the mistakes they made.

Does React Native or another cross-platform approach have perspectives in the future in your opinion?

Good question. To be honest i'm not sure. In general i'm not super excited about React on the web because I think there are better ways and tools like RxJS (RxJava's counterpart in JavaScript to build web apps. Regarding React Native, I believe that android and iOS UI code needs to be written natively, even though Facebook did a great job to implement React Native. I'm also not sure if I would like to develop mobile apps in JS if I could write them for example in kotlin or swift. However, I believe that more and more mobile apps will share a common code base for business logic, especially between Android and iOS. Maybe tools like Xamarin could be helpful. At the company I work for we found a solution for that based on gwt, annotation processing and j2objc to share business logic across android, iOS, backend (JRuby) and web frontend (JavaScript) by writing the code once in java and translate it to native object-c and javascript code that will run on the platform specific.

What is your take on Kotlin in production? What are your thoughts about the future main language for android development Kotlin, Java8, Swift?

Kotlin is a very nice language and latest update 1.0.2 was a huge step forward. Unfortunately, at the company I work for we don't use kotlin in production yet (except some unit tests), but in my personal side projects I use kotlin intensively. Swift is a nice language too, but in contrast to other rumours from the web, I don't think that we will see Swift as programming language to develop android apps. We might see it as C / C++ alternative to be used via JNI, but I don't think that we will see Swift replacing Java to access android API/SDK any time soon. Java 8 has some nice long awaited features. Lambdas is one of the most popular ones, but is my favorite default method implementations on interfaces which allows us to use mixins. Unfortunately this is only available in API 23 and above.

Do you have any expectations regarding new Android Jack & Jill toolchain? What architecture pattern do you use in production? What are your thoughts on MVVM, it seems like Google bet on it as a future standard?

I believe that Jack & Jill will evolve into the direction that we don't use tradition java or even java 9 for android development in the future. I think with Jack & Jill we will see some kind of fork of the java programming language for android development.

Basically I use MVP, but with some ideas from others like unidirectional data flow, immutability pure and functions. It's also чи варто to say that MVP, MVVM etc. are not architectures, but rather architectural patterns to separate the View from транспортний layers. Many people refer to Robert C. martin's Clean Architecture in this context, which is a great architecture. I do implement my apps according the Clean Architecture. As always, i'm trying to stay close to the books but I don't exactly follow the books blindly.

Google offers us a data binding engine. That doesn't automatically means you have to implement MVVM. Data binding can also be used without MVVM for instance just to bind a ViewHolder in a RecyclerView. In general I prefer MVP over MVVM in combination with google's data binding because data binding breaks one of the key principles I strongly believe in: Immutability. Therefore, if you want to implement MVVM I would recommend to that by using RxJava.
Overall, Google didn't force us to a specific pattern in the past and i'm sure Google won't do that in the future with MVVM and data binding. Actually, I know many developers that would like to hear an official statement from Google on how to build an android app from a software architectures point of view. But Google has never made such an official statement. They don't want to say MVVM is better than MVC or MVP. Developers hate Google for that. But I think that Google is 100% right to not make such statements because software architecture is a topic that evolves over time. You can't say that one architectural pattern is the best and everybody should implement their apps in that way for all kind of applications. Declaring one pattern as the best solution quite often means stagnation.

How much time do you spend on writing unit tests? How do you ensure sufficient coverage?

As much time as they take. I practice TDD which says writing unit test before you write production code. Usually I stick to this rule. However, I only rarely write functional tests for UI, because the UI of my apps is pretty simple stupid. There is no business logic, there is no complex algorithm to sort elements etc. in my UI layer. There can't go much wrong. Another problem I have with functional UI Tests (espresso) is that they take minutes to run. That destroys TDD. You can't do TDD efficiently running unit tests for the code you have written takes minutes. Compared to backend development where TDD feedback loops is about 2-3 seconds more then 10 seconds is unacceptable for practicing TDD. So I, personally, decided not to write functional UI tests. As already said, my UI layer is pretty simple. Therefore, testing if clicking on a button triggers the correct action is not important that in my opinion. In practice I found out that «visual bugs» occur more often especially when working on a team with a lot of xml layouts. I see more value in testing if the UI looks as expected rather than checking if a button behaves correctly. Therefore, I decided to do screenshot-testing to prevent visual regression facebook.github.io/screenshot-tests-for-android.

Last but not least, how do I ensure sufficient coverage? Well, developers are good in maths and statistics. Many developers use code coverage tools like JaCoCo. But as already said, i'm not testing UI and i'm also not testing POJO's i.e. getters and setters. So what if such a code coverage tool tells me that I have a coverage of 80%. What does 80% actually mean in this context? I can take a look closer at the code coverage per package to get a better understanding, but I came to the conclusion that such a number can't tell you whether you need to write more unit tests or not. I ensure sufficient coverage by code review. Usually one of my coworkers detects during code review if a unit test doesn't makes sense or a specific part of the code needs to be tested too or if one unit test doesn't make sense at all because it adds no value or is kind of duplicated.

Do you prefer Fragment based or View based approach for screen building, any thoughts on mosby-conductor/mosby-flow?

Usually, I use Fragments and I don't have any major issue with Fragments, but I do understand people complaining about Fragments. The idea of Fragments is great but the current implementation is far from perfect. This is where Conductor comes in. Conductor is basically a better implementation of Fragments. Flow, on the other hand, is a navigation stack replacement. It misses the concept of controllers (Fragments) entirely and uses plain android.view.View. This is a super simple concept regarding lifecycle management and is extendable but may or may not requires to write a lot of code manually. Since controllers are missing Flow is providing a ServiceFactory which allows a View in Flow corresponding to access components for a given View. This is what mosby-flow does. This plugin provides a ServiceFactory to provide a Presenter for your View tight to the view's lifecycle. At the end you can use Mosby's convenient API (same as for Fragments and Activities) to implement the MVP with pattern Flow. mosby-conductor offers the same for Conductor. At the moment I tend to prefer Conductor over Flow. With this two plugins along with Mosby's build-in support for Fragments and Activities I have the freedom to refactor my app, i.e. from Fragment to Conductor, by touching only the View layer, because Model and Presenters stay untouched as they can be used independent of the prefered View layer container (Activity, Fragment, Conductor, Flow).

What do you think about using JVM bytecode patching over annotation processing for Android libraries? Is Clean architecture the most suitable architecture for Android development at the moment or there are other unappreciated options?

The problem with JVM bytecode patching is that you can't debug bytecode easily. Therefore I prefer annotation processing because you can debug generate the code in your IDE's debugger. If we are honest every piece of software has bugs, so have bytecode manipulating libraries and annotation processing libraries. The main question is: if there is a bug in such a library, how much will effect such a bug my daily development? Imagine you are working on a app but something is not working as expected because of a bug in such a library. If the заподіяння library is a bytecode patching library it's harder to find out that the bug has been caused by the library and harder to apply a hotfix. In contrast, if an annotation processing library based is the source of the bug in your app, you simply can attach your IDE's debugger. Overall I think it's all about trust: If you trust the developer, i.e. Square of a bytecode manipulating library then it's ok to use that library.

The clean architecture seems to be the defacto standard and for good reasons. Lately, we saw more programming languages like kotling or Swift adopting concepts from functional programming languages. Although the Clean Architecture is a timeless concept, I think that in the future we will use more and more functional programming paradigms where concepts like boundaries and use cases will still be there in theory but will be hidden behind pure functions. That's what I meant previously with «stay close to the books but don't exactly follow the books».

Mosby library implies the usage of one presenter view and one in Fragment/activity. Do you think it's a good solution? In big projects we often have to use composite views — several presenters, several views in one fragment interacting with each other. What solution is best in your opinion?

Yes, I think every view should have exactly one Presenter. Every complex View can be split down in multiple subviews with each subview having his own Presenter. Views and Presenters should never interact with each other (if not really necessary, i.e. Navigation in your app), your app should establish a unidirectional data flow where your subview updates the model (via his own presenter), the same model will be observed by another subview's presenter which will then update the other subview. No need that the first subview interacts with the other subview directly.

Mosby treats Activity/Fragment as View, and in this realisation these components answer for the lifecycle, routing and implementing dependencies. Do you think such a configuration contradicts SRP? How justified is the abstraction of Android classes in your opinion?

That's a good point. Infact Activity / Fragment is contradicting SRP heavily. It's kind of controller, manages lifecycle, interacts with UI widgets and so on. In Mosby I decided to treat Activity / Fragment. However, in Mosby you don't necessarily have to do since that there is a method in MvpActivity / MvpFragment called getMvpView() you can override. This method returns the MVP View the presenter interacts with. Per default this is the Activity / Fragment itself but you can introduce another layer just for MVP View and return that one in that method. By doing so you could have one dedicated «MVP View» layer and could treat Activity as lifecycle management component only. Sounds reasonable right, so why not doing that per default? Because if we do so then we are adding yet another layer and that's the point where adding yet another layer is the beginning of overengineering. If your think your Activity / Fragment is doing too much and contradicts SRP too much than go ahead and introduce more like the layers MVP View layer (tip: data binding engine could be used) but if you take a look at an average Activity / Fragment powered by Mosby usually they are only implementing the MVP View interface. In that case even if we don't have perfect SRP this it's completely fine to let Activity / Fragment implement the MVP View Interface directly in my opinion. Overengineering is as bad as writing bad code.

What changes are planned for the third version of Mosby?

Apart from some internal clean up «under the hood» I will add better support for child Fragments and for ViewGroups in combination with retaining Presenters which will also be helpful for a better Flow integration. Another feature people ask me quite is often MVP in RecyclerView / ViewHolders. I do have implemented something like this in one of our apps at work and I might consider to add that. But things are tricky with RecyclerView (ViewHolders are recycled, Adapter's dataset can change at any time, screen orientation changes, etc.). I have to evaluate if I really want to add this. I'm afraid that once I have published such a solution, things will get out of hands quickly and will be misused. I don't want to open pandora's box.

What is the current development stage of Mosby? Are you going to add some new features or just keep fixing bugs? What's your take on lifecycle handling in MVP and View vs Fragments, taking into account there's a Mosby-Conductor plugin now? There's a MPV library Moxy created supposedly because of some flaws in Mosby. Do you know of it and have you done any comparisons?

I'm working on Mosby 3.0 (snapshots are already available). Unfortunately, right now I don't have that much time to work on Mosby, but I hope to continue my work at the end June. I tried my best to build Mosby as some kind of fundament you can use as base to build your application on top of Mosby by giving you enough space for your own interpretation and implementation of MVP. For example, I personally think that Presenter's don't need lifecycle callback methods and therefore Mosby's default Presenter doesn't have that methods. But if you think it makes sense for your app to have such a lifecycle aware Presenter you simply have to create a base presenter class with lifecycle methods. Mosby has a plugin mechanism so that you can use that lifecycle aware base Presenter class in your whole app easily. I think Moxy is a nice approach and that's a good example of what I meant with «software architecture evolves» previously. There are some implementation details that I don't agree with and have solved otherwise in Mosby but that doesn't mean that my way is the right way. Who am I to judge about the work of others? Therefore I will not compare Mosby and Moxy. I recommend to take a look at both and to make your own mind. The important bit is that you should pick a library that matches your needs: that may be Moxy, that may be Mosby, that may be Nucleus, that may be something entirely different.

Do you consider Mosby library an acceptable solution for production or is it more of a concept? Mosby is used by our company in productions for years in applications with millions of users. For us it works great and is production ready. There's been a lot of talk lately about big companies overcomplicating the interviews, because they asked front-end developers about basic recursive algorithms (like depth-first-search), data structure («reverse linked list»), etc. What approach do you have to hiring people? How do you conduct interviews? What do you consider essential for a middle-level Android developer, for example?

I'm not that much involved in the hiring process at our company. I do interviews only if the CTO / CEO is not available. Usually a candidate gets a problem to work on at home. Basically he has to build a very simple Android app. Usually, he has 1 week to send us his solution. Then we take a look at the invite code and him to a personal interview. In the case that I am your interviewer I will not ask you data structure specific questions or let you solve quizzes on a white board. I want to create a relaxed atmosphere for the candidate. Then I ask him some basic question about his background. I think the background of a candidate is very important. It makes a huge difference if a candidate is coming from college and tries to get his first job (I might ask some more theoretical questions during the interview) or a candidate already has working experience (more practical questions). Ideally the candidate has already work on an open source project. I will give him a computer (or even better he will bring his own machine to the interview) and we step through his code and I want him to explain me certain things like why he implemented certain things in that specific way, why he chose that data structure for a certain problem and so on. So indirectly we will talk about data structure. Data structures are important also for front end developers. But I don't expect the candidate to know how to rotate red-black tree. But he should have good arguments why he has decided to use a list and not a set or a map or a tree. Afterwards we will do the same with his demo app he developed at home, but with live refactoring. For me it's important to hire a good java developer, who has a basic understanding in the java programming language, data structures, threading and synchronization and garbage collection. This is more important than for me having a very experienced android developer because I think that you can learn the Android SDK quickly, but it takes years to become a good java developer. I don't ask annoying questions like what is the difference between a WeakReference and a PhantomReference, but if I get the feeling that the candidate is specialized in a certain area I would give him the chance to talk about his skills. With the second par, the review of his app he has developed at home, I want to see how skilled a candidate is on a very important part of development: refactoring. In the last part of the interview with me I will let the candidate ask me questions of whatever topic he likes: the company, the code base of our apps, even about football if he wants to. Last but not least, I also want to hire people from which my Co-Workers and I can learn. Every new employee brings new knowledge into a company and I want to ensure that the candidate is willing and has the courage to share this knowledge with us.

From a middle-level Android developer I expect to have a good knowledge of the Java programming language, lifecycle management of Android components like Activity and Services, he should be able to build efficient layouts in XML, has a basic knowledge of SQL and a good understanding of the HTTP protocol. Design Patterns and software architecture are a plus but not a strong requirement.

What do you think about the fact that it takes a long time to compile projects with the help of Gradle. There Are some approaches to architecture that help minimize the time spent on compiling?

Compilation time is horrible. I can't even imagine how much money companies lose everyday by slow compile times. I don't care how this problem will be solved, it doesn't matter if it's javac or Jack & Jill or an advanced version of aapt or whatever else can be optimized, but this problem has to be solved as soon as possible. I used Facebook's Buck build system for some time and it was so much faster but I didn't wanted to switch to permanently an unofficial build system. I also had some problems with configuring Buck properly, but this was my fault I guess. Buck is definitely a good alternative.

Real or Arsenal? :)
I love football, I really do. So just in case that we will run out of android related topics at droid party, I can talk about football for hours. Be prepared, i'm Real Madrid and Bayern Munich supporter.

Як ви почали програмувати під Android?

Мене завжди приваблювала розробка мобільних додатків. Своє перше додаток, ще студентом, я написав на Symbian з допомогою Qt і C++. Ще я трохи займався J2ME. У мене також був КПК на Windows CE для всякої программистской всячини. Коли почали з'являтися смартфони та айфони, все це мене дуже надихнуло, але не можу сказати, що відносини між мною та Android були теплими з першої зустрічі. Я почав програмувати під Android (Android 1.5 Cupcake) в коледжі влітку 2009 р., але тоді я був на рівні типу «де мій метод main()», що з себе представляє xml і з чим їдять цей findViewById()?». В результаті я закинув Android майже на півроку, поки моя Nokia N95 не наказала довго жити і не прийшов час купити собі смартфон. iPhone ніколи мені не подобався, і, крім того, для мене дуже важливим моментом було наявність у апарату аналогової клавіатури. Я не міг собі уявити, як я буду вводити довгий текст на тачскріні. У мене було два варіанти: або оригінальний Motorola Droid (Milestone) з Android 2.0 або Nokia N900 під керуванням Maemo. Платформа Maemo також передбачала можливість використовувати C + + та Qt, тому я був близький до того, щоб вибрати N900, але врешті-решт мій вибір припав на Motorola Droid, і головним чином тому, що ОС Maemo не підтримувала MMS, що, ясна річ, важливіше всього на світі! :) І як тільки я дістав Motorola Droid, я почав програмувати під Android 2.0 — правда, суто для особистих цілей.

Яка ваша думка з приводу застосування архітектурних рішень з JS?

У JavaScript-розробників є дещо спільне з Android-розробниками: їх платформа/SDK практично не має гайдлайнов з архітектурної точки зору. Це реально неоране поле. Світ JavaScript раніше знаходиться в пошуках свого «архітектурного грааля», в результаті чого кожні три місяці ми спостерігаємо, як нові фреймворки та підходи змінюють один одного. Разом з тим патерни проектування типу Model-View-Intent (cycle.js), Redux і Flux привнесли низку по-справжньому знаменних концепцій, які дійсно має сенс спробувати впроваджувати у платформи на зразок Android — мова йде про ідеї, що лежать в основі тих чи інших патернів або середовищ розробки. У Android-програмістів існує тенденція знову і знову винаходити велосипед, і у цього велосипеда не завжди круглі колеса. Простіше кажучи, нам слід часом відходити від загальноприйнятих рішень і цікавитися розвитком інших платформ, щоб почерпнути для себе щось нове, а найголовніше — зробити висновки з чужих помилок.

Як по-вашому, мають React Native та інші крос-платформні підходи перспективи?

Хороше питання. Якщо чесно, не впевнений. Взагалі кажучи, я не у великому захваті від застосування React для веб-розробки, оскільки вважаю, що є більш досконалі засоби та інструменти для розробки веб-додатків, наприклад, RxJS (аналог RxJava в JavaScript). Що стосується React Native, я вважаю, що код користувальницького інтерфейсу для Android і iOS потрібно писати під певну платформу, і це незважаючи на те, що хлопці з Facebook досягли прекрасних результатів, застосовуючи React Native. Я також не впевнений, що захотів би створювати мобільні додатки на JS, якщо б у мене була можливість писати їх на мові Kotlin або Swift. Разом з тим, мені здається, що все більше і більше мобільних додатків будуть використовувати загальну кодову базу для реалізації бізнес-логіки, і особливо це стосується Android — і iOS-додатків. Можливо, нам варто звернути увагу на інструментарій типу Xamarin. У компанії, де я працюю, ми знайшли рішення, засноване на GWT, обробці анотацій і J2ObjC, для перевикористання коду, що відповідає за бізнес-логіку, Android, iOs, сервері (JRuby) і веб-додатку (JS) реалізуючи її спочатку на Java, а потім, транслюючи ObjectiveC або JavaScript, працює на певній платформі.

Яка ваша думка з приводу використання мови Kotlin в розробці додатків? Як ви думаєте, яка мова буде використовуватися в майбутньому для розробки Android-додатків: Kotlin, Java8, Swift?

Kotlin — це чудовий мову, і останнє оновлення 1.0.2 стало справжнім проривом. На жаль, в нашій компанії ми поки застосовуємо Kotlin лише для кількох юніт-тестів, а не в якості мови розробки. Однак у своїх особистих проектах я використовую Kotlin дуже активно. Swift — теж цікавий мову, але я не можу погодитися з чутками, які ходять в інтернеті, і не думаю, що Swift буде використовуватися для розробки додатків під Android. Ми могли б розглядати його в якості альтернативи C / C++ для використання з допомогою JNI, але я не думаю, що ми побачимо Swift в якості заміни Java для роботи з API Android / SDK в скільки-небудь незабаром. В Java 8 є ряд цікавих функцій, яких всі давно чекали. Лямбда-вирази — одна з найпопулярніших серед них, але особисто мені найбільше подобаються default-методи у інтерфейсів, що дозволяють використовувати домішки (mixins). На жаль, ця можливість доступна тільки в API 23 і вище.

Що ви чекаєте від нового пакету інструментальних засобів Android Jack і Jill? Яку архітектурну модель ви використовуєте в розробці? Що ви думаєте про MVVM? Є підстави вважати, що Google позиціонує цей шаблон як майбутній стандарт.

Я вважаю, що Jack і Jill стануть розвиватися в напрямку, де ми в перспективі не будемо використовувати традиційний Java або навіть Java 9 для розробки під Android. Я думаю, що у випадку з Jack і Jill ми станемо свідками появи свого роду відгалуження Java для розробки під Android. Я в основному використовую MVP, але також запозичую деякі ідеї з інших технологій — наприклад, unidirectional data flow, immutability, pure functions. Важливо також відзначити, що MVP, MVVM і т. п. є не архітектурами, а швидше архітектурними підходами для відділення View від нижчих шарів. У розглянутому контексті багато хто посилається на концепцію, викладену в Clean Architecture Роберта Мартіна. Вона являє собою зразок для наслідування. В розробці додатків я завжди намагаюся дотримуватися принципів «чистого архітектури». Як завжди, при цьому я намагаюся дотримуватися рекомендацій в книгах, але не дотримуюся вказівок сліпо. Так, Google пропонує нам механізм зв'язування даних (data binding). Але з цього не випливає, що треба неодмінно використовувати саме MVVM. Data binding можна також використовувати і без MVVM, наприклад шляхом простого байндинга ViewHolder в RecyclerView. Взагалі я віддаю перевагу MVP перед MVVM в поєднанні з гугловським механізмом зв'язування даних, оскільки останнє порушує один з ключових принципів, яких я суворо дотримуюся: мова йде про иммутабельности (immutable). Таким чином, якщо ви хочете реалізувати MVVM, то я б рекомендував використовувати RxJava.

В цілому, Google ніколи не нав'язував нам якийсь конкретний підхід в минулому, і я впевнений, не буде робити цього в майбутньому з MVVM і зв'язуванням даних. Більш того, я знаю багатьох розробників, які бажали б отримати офіційні рекомендації від Google з приводу того, як слід будувати додатки під Android з точки зору архітектури. Але Google ніколи не озвучує подібні речі на офіційному рівні. Google не хоче заявляти, що MVVM краще, ніж MVC або MVP. Розробники за таку позицію Google недолюблюють. Однак я вважаю, що хлопці з Google на сто відсотків праві, не роблячи подібних заяв, оскільки архітектура — це річ, яка еволюціонує з часом. Не можна стверджувати, що якийсь архітектурний підхід є найкращим і що додатка можна розробляти лише одним способом для всіх можливих завдань. Якщо оголосити якийсь патерн в якості найкращого рішення, це часто призводить до стагнації.

Скільки часу у вас йде на написання модульних тестів? Як ви забезпечуєте достатнє покриття?

Я витрачаю стільки часу, скільки буде потрібно. Я практикую розроблення через тестування (TDD), принцип якої полягає в тому, що писати модульні тести слід перед написанням робочого коду. Зазвичай я дотримуюся цього правила. Однак я пишу нечасто функціональні тести для користувача інтерфейсу, оскільки в моїх програмах він зазвичай до смішного простий. Бізнес-логіки, складного алгоритму сортування елементів і т. п. в моїх інтерфейсах ви не знайдете. Тут ще треба постаратися, щоб допустити помилку. Інша проблема, з якою я стикаюся, маючи справу з функціональними UI-тестами (Espresso), полягає в тому, що їх виконання займає кілька хвилин. Це йде врозріз з принципами TDD. Ефективно здійснювати розробку на основі тестування неможливо, якщо час виконання модульних тестів для написаного вами коду обчислюється хвилинами. Якщо порівнювати з розробкою серверних додатків, де ітерації при використанні TDD вимірюються 2-3 секундами, час, що перевищує 10 секунд, є неприйнятним для TDD-розробки. Тому я прийняв для себе рішення не писати функціональні тести для користувача інтерфейсу. Як я вже зазначав, мій UI-шар досить простий. Тому тестування того, викликає натискання на кнопку потрібну дію, не є, на мою думку, таким вже й важливим. Я переконався на практиці, що «візуальні баги» виникають особливо часто, коли працюєш у команді з використанням великої кількості xml-макетів. Я бачу набагато більше сенсу у тестуванні, яке перевіряє, чи виглядає інтерфейс як треба, а не правильно поводиться кнопка. Тому для попередження візуальної регресії я вирішив проводити тестування за допомогою скріншотів facebook.github.io/screenshot-tests-for-android

І наостанок торкнемося не менш важливу тему — те, як я забезпечую достатнє покриття. Взагалі розробники добре розбираються в математиці і статистиці. Багато з них використовують інструменти тестування покриття коду начебто JaCoCo. Але, як я вже говорив, я не тестую UI. Також я не тестую об'єкти POJO, тобто методи get і set. Що, наприклад, відбувається, якщо тест перевірки покриття коду повідомляє мені, що покриття становить 80%? Що 80% в реальності означають в заданому контексті? Можна пильніше поглянути на попакетное покриття коду, щоб отримати більш повну картину, але я прийшов до висновку, що така інформація не дозволяє зрозуміти, чи треба писати більше модульних тестів чи ні. Я визначаю, чи достатньо покриття коду тестами на код-рев'ю. Зазвичай хтось з моїх колег визначає під час код-рев'ю, що який-небудь модульний тест не вирішує поставленого завдання або певна частина коду також потребує тестуванні. Або один з модульних тестів взагалі не має сенсу, оскільки не несе в собі ніякої користі або якимось чином дублює якийсь інший.

чи Віддаєте ви предпочтиние підходу до побудови екрану на основі фрагментів або на основі View, що думаєте про mosby-conductor/mosby-flow?

Я зазвичай використовую фрагменти, і з ними у мене великих проблем не буває, але я розумію людей, які скаржаться на цей підхід. Ідея, на якій базуються фрагменти, дуже цікава, але поточна її реалізація далека від досконалості. Тут на допомогу приходить Conductor. По суті, Conductor — це більш вдала реалізація фрагментів. Flow, з іншого боку, слугує заміною навігаційного стеку. Даний підхід взагалі не використовує концепцію контролерів (фрагментів), застосовуючи замість цього стандартний android.view.View. Це надзвичайно проста концепція з точки зору управління життєвим циклом, і вона допускає розширення, але в певних випадках може знадобитися написання великого обсягу коду вручну. Оскільки контролери не застосовуються, Flow надає клас ServiceFactory, який дозволяє View Flow отримати доступ до відповідних компонентів для кожного View. Суть роботи mosby-flow в наступному: даний плагін містить клас ServiceFactory, який надає Presenter View для, прив'язаний до його життєвого циклу. В довершення всього ви можете використовувати зручний API Mosby (як для фрагментів і активностей) для впровадження MVP з використанням Flow. Mosby-conductor пропонує аналогічну можливість для Conductor'a. В даний час я віддаю перевагу Conductor, ніж Flow. З цими двома плагінами, а також вбудованою підтримкою Mosby фрагментів і активують я маю можливість рефакторіть код мого додатка, наприклад перейти з фрагментів на Conductor, зачіпаючи лише шар View. При цьому модель і Presenters залишаються незайманими, оскільки їх можна використовувати незалежно для предпочитаемого контейнера для шару View (Activity, Fragment, Conductor, Flow).

— Що ви думаєте про модифікації JVM байткода (bytecode patching) замість обробки анотацій для андроїдних бібліотек? Є «чиста архітектура» в даний час найбільш підходящої для розробки під Android або є інші варіанти, які поки обійдені увагою?

Проблема з модифікацією JVM байткода полягає в тому, що налагодження байткода не надто просте заняття. У зв'язку з цим я віддаю перевагу обробку анотацій, оскільки налагодження створюваного коду можна виробляти в налагоджувач IDE. Якщо бути правдивим до кінця, в будь-якій програмі є помилки, як вони є і в бібліотеках маніпуляцій з байткодом, і в бібліотеках обробки анотацій. Головне питання ось у чому: якщо в такій бібліотеці є баг, як сильно він вплине на мою повсякденну роботу? Уявіть, що ви працюєте над додатком, але щось не йде як треба, і це пов'язано з помилками в такій бібліотеці. Якщо викликала проблему бібліотека є бібліотекою для модифікації байткода, визначити, що помилка пов'язана з несправністю в цій бібліотеці, буде важче, і, відповідно, складніше виправити помилку. З іншого боку, якщо джерелом помилки у вашому додатку є бібліотека обробки анотацій, вам достатньо підключити IDE-відладчик. В цілому я вважаю, що це питання довіри: якщо ви довіряєте розробнику бібліотеки обробки байткода, наприклад Square, використовуйте її без роздумів. «Чиста архітектура», мабуть, є фактичним стандартом, і на те є вагомі причини. Останнім часом можна побачити, як все більше мов програмування, наприклад Kotlin або Swift, переймають концепції, що використовуються у функціональних мовах програмування. Хоча «чиста архітектура» є концепцією на всі часи, я вважаю, що в перспективі ми будемо використовувати все більше парадигм функціонального програмування, де такі поняття, як межі та сценарії використання, як і раніше будуть існувати в теорії, але не видно за фасадом чистих функцій. Це те, що я раніше мав на увазі під словами «дотримуватися рекомендацій з книг», але не сліпо слідувати вказівкам».

Бібліотека Mosby передбачає використання одного презентера (Presenter) і одного виду (View) у фрагменті/активності. Ви вважаєте, що це правильне рішення? У великих проектах ми часто повинні використовувати складові види — кілька презентеров і кілька видів в одному фрагменті, що взаємодіють один з одним. Яке рішення є найкращим, на ваш погляд?

Я вважаю, що кожному виду повинен відповідати строго один презентер. Кожен складний вид можна розбити на безліч підвидів, при цьому з кожним підвидом використовується один презентер. Види і презентери не повинні взаємодіяти між собою (хіба тільки в разі крайньої необхідності, як, наприклад, для навігації у вашому додатку), в додатку повинен бути організований односторонній data flow, при якому підвид оновлює модель (через власний Presenter), та ж модель потрапляє в поле зору презентера іншого підвиду, який потім оновлює той, інший підвид. Немає необхідності для першого підвиду безпосередньо взаємодіяти з іншим підвидом.

Mosby інтерпретує фрагменти/активності як вид, і в такій реалізації ці компоненти відповідають за життєвий цикл, роутинг і впровадження залежностей. Ви вважаєте, що така конфігурація суперечить протоколу SRP? Наскільки обґрунтовано абстрагування класів у Android, на ваш погляд?

Відмінний питання. Насправді Activity/Fragment дуже сильно суперечить протоколу SRP. Це свого роду контролер, який управляє життєвим циклом, взаємодіє з інтерфейсними віджетами і т. д. Я вирішив інтерпретувати Activity/Fragment в Mosby. Однак у Mosby не обов'язково надходити таким чином, оскільки у класі MvpActivity/MvpFragment є метод getMvpView(), результат якого можна перевизначити. Даний метод повертає MVP-вид, з яким взаємодіє Presenter. За замовчуванням це безпосередньо Activity/Fragment, однак можна ввести додатковий шар спеціально для MVP-виду і отримати його результатом виконання методу. В процесі цього можна використовувати спеціально виділений шар MVP View і інтерпретувати Activity як компонент управління життєвим циклом. Все начебто правильно. Чому б не поступати таким чином завжди? Причина в тому, що, роблячи це, ми додаємо ще один шар, а додавання ще одного шару є початком штучного ускладнення системи. Якщо ви вважаєте, що ваші Activity/Fragment роблять занадто багато і надмірно суперечать протоколу SRP, тоді можна ввести більше шарів начебто MVP View (підказка: можна використовувати механізм зі зв'язуванням даних), але якщо подивитися на середні Activity/Fragment, які реалізуються засобами Mosby, вони зазвичай лише реалізують інтерфейс MVP View. У цьому випадку, навіть якщо не визнавати протокол SRP досконалим, на мій погляд, немає нічого поганого в тому, щоб застосовувати Activity/Fragment для прямої реалізації інтерфейсу MVP View. Штучне ускладнення системи нітрохи не краще написання поганого коду.

Які плануються зміни у третій версії Mosby?

Крім деякої оптимізації «начинки» бібліотеки, я додам більш досконалу підтримку для вкладених фрагментів і ViewGroups в поєднанні з збереженням презентеров, що також полегшить інтеграцію Flow. Інший функціональної рисою, про яку мене часто запитують, чи є MVP в RecyclerView/ViewHolders. Я вже реалізував щось подібне в одному з наших програм, і не виключено, що я додам і цю функціональність. Але з RecyclerView все не так просто (ViewHolders перевикористовуються, масив даних адаптера може змінюватися в будь-який час, змінюється орієнтація екрана тощо). Потрібно зважити всі за і проти, якщо я дійсно хочу поступити таким чином. Боюся, що, як тільки я опублікую подібне рішення, ситуація швидко вийде з-під контролю і функціонал буде використаний неналежним чином. Мені не хотілося б відкривати ящик Пандори.

На якій стадії знаходиться розробка Mosby в даний час? Чи збираєтеся ви додавати нові функції, або ж ви просто продовжуєте шліфувати продукт, усуваючи помилки? Яка ваша думка про обробці життєвих циклів в MVP View у противагу Fragments, беручи до уваги той факт, що вже існує плагін Mosby-Conductor? Стало відомо про створення бібліотеки MVP під назвою Moxy — по всій видимості, з-за деяких вад, виявлених у Mosby. Чи знаєте ви про це і проводили якісь порівняння?

Я працюю зараз над Mosby 3.0 (вже можна ознайомитися з попередньою версією). На жаль, прямо зараз у мене не так багато часу для роботи над Mosby, але я сподіваюся продовжити її в кінці червня. Я намагався зробити все, щоб побудувати Mosby як свого роду фундамент, який можна використовувати в якості основи для розробки додатків, надаючи при цьому достатньо можливостей для власної інтерпретації та імплементації коштів MVP. Наприклад, я особисто вважаю, що класу Presenter не потрібні методи зворотного виклику для життєвого циклу, тому в Mosby за замовчуванням вони не включені в нього. Але якщо, на ваш погляд, вашому додатком добре було б мати подібний Presenter з функціями життєвого циклу, вам достатньо просто створити базовий клас Presenter з методами обробки життєвого циклу. До складу Mosby входить механізм, за допомогою якого ви можете з легкістю використовувати цей базовий клас Presenter з функціями життєвого циклу у всьому вашому додатку. Вважаю, що Moxy — це гідний варіант, який досить точно ілюструє мої слова про те, що програмна архітектура з часом еволюціонує. Існують деякі моменти імплементації, з якими я не згоден і я вирішив по-своєму в Mosby, але це не значить, що мій спосіб є правильним. Хто я такий, щоб судити про роботу інших? Тому проводити порівняння між Mosby і Moxy я не буду. Я рекомендую подивитися на те й інше і самостійно зробити висновки. Тут важливо вибрати бібліотеку, що відповідає вашим потребам: це може бути Moxy, це може бути Mosby, це може бути Nucleus або взагалі що-небудь зовсім інше.

чи Вважаєте ви бібліотеку Mosby прийнятним рішенням для розробки додатків або її слід розглядати більшою мірою як концепції?

Моя компанія використовує Mosby для розробки вже багато років, створивши програми, аудиторія яких нараховує мільйони користувачів. Нашим потребам ця бібліотека відповідає повністю, вона готова до використання в розробці додатків.

останнім часом точиться чимало розмов з приводу того, що великі компанії занадто ускладнюють співбесіди, розпитуючи розробників користувальницького інтерфейсу про основні рекурсивних алгоритмів (наприклад, пошук в глибину), структурі даних (зворотний зв'язний список) і т. п. Який підхід використовується при наймі співробітників? Як ви проводите співбесіди? Що ви вважаєте головним для Android-розробника середнього рівня?

Я не те щоб сильно залучений в процес найму у своїй компанії. Я проводжу співбесіди тільки у разі, якщо технічний директор або генеральний директор не мають такої можливості. Зазвичай кандидат на роботу отримує завдання для виконання вдома. Як правило, йому пропонується розробити дуже просте додаток для Android. Зазвичай в його розпорядженні один тиждень на те, щоб надіслати нам рішення. Після цього ми розглядаємо написаний код їм і запрошуємо на співбесіду. Якщо я проводжу співбесіду, я не задаю питань про структуру даних і не пропоную вирішувати завдання на дошці. Я прагну створити довірливу обстановку для кандидата. Спочатку я ставлю базові питання про його професійному досвіді. Я надаю професійного досвіду дуже велике значення. Величезна різниця, хто перед тобою: вчорашній студент, який вперше влаштовується на роботу (при цьому я можу поставити йому ряд теоретичних питань під час співбесіди), або людина з досвідом роботи (тоді я ставлю питання більш практичного властивості). В ідеалі кандидат, звичайно, повинен мати досвід роботи у проектах з відкритим вихідним кодом. Потім я даю йому комп'ютер (а ще краще, якщо він прийде на співбесіду зі своїм ноутбуком), і ми крок за кроком пройдемо по його коду, в ході чого я прошу пояснити ті або інші місця, наприклад, чому він реалізував певні речі одним способом, а не іншим, чому обрав певну структуру даних для тієї чи іншої задачі і т. д. Таким обхідним шляхом ми обговорюємо структуру даних. Структури даних важливі також і для розробки користувальницького інтерфейсу. Я не чекаю від кандидата вміння повертати червоно-чорне дерево. Але він повинен мати вагомі аргументи на користь того, чому він вирішив використовувати список, а не безліч, картку або дерево. Пізніше ми робимо те ж саме з демонстраційним додатком, яке він підготував будинку, при цьому на ходу реорганізуємо його код з метою рефакторінгу. Для мене важливо найняти на роботу хорошого Java-розробника з основними знаннями в області мови програмування Java, структур даних, організації потокової обробки, синхронізації і сміття. Це набагато важливіше для мене, ніж дістати дуже досвідченого розробника Android-додатків, тому що вивчити комплект для розробки ПЗ під Android можна досить швидко, а от на те, щоб стати хорошим Java-розробник, йдуть роки. Я не ставлю незручних запитань типу «яка різниця між слабкою посиланням та фантомної?», але, якщо відчуваю, що людина спеціалізується в певній галузі, я дам йому можливість розповісти докладніше про своїх навичках і уміннях. В ході другої частини співбесіди, під час аналізу додатка, розробленого кандидатом в рамках домашнього завдання я хочу переконатися, наскільки добре він розбирається в дуже важливої частини процесу розробки: мова йде про рефакторинге коду. В останній частині співбесіди, проведеного мною, я надаю кандидату можливість задавати мені запитання на будь-яку тему: про компанії, про кодової базі наших додатків, навіть про футбол, якщо йому захочеться поговорити про це. І останнє, але не менш важливе: я також прагну наймати людей, у яких і я, і мої колеги могли б чому-небудь навчитися. Кожен новий співробітник привносить в компанію нові знання, і я прагну упевнитися, що кандидат на роботу може ділитися своїми знаннями з нами і не боїться діяти таким чином. Від Android-розробника середнього рівня я очікую гарного знання мови програмування Java, управління життєвим циклом таких компонентів Android, як Activity та Services; він повинен вміти створювати ефективні макети на XML, мати базові знання про SQL і добре розбиратися в протоколі HTTP. Знання патернів проектування і програмної архітектури є плюсом, але не надто важливою вимогою.

Яка ваша думка з приводу того, що компіляція проектів за допомогою Gradle займає надто багато часу? Чи є які-небудь підходи до архітектури, які допомагають звести до мінімуму час компіляції?

Компіляція займає дуже багато часу. Неможливо уявити, скільки грошей летить на вітер з-за повільної складання білдів. Неважливо, як ця проблема буде вирішена, чи буде це реалізовано через javac, Jack і Jill або просунуту версію aapt або будь-який інший спосіб оптимізації, але рішення має бути знайдено як можна швидше. Якийсь час я використовував систему складання Buck від Facebook, і вона працювала набагато швидше, але мені не хотілося назавжди переходити на неофіційну систему збірок. У мене також були певні проблеми з належною налаштуванням роботи Buck, але, гадаю, тут вже справа в моїх уміннях. Buck, безумовно, це хороша альтернатива.

«Реал» або «Арсенал»? :)

Я дуже люблю футбол, чесно. Тому, якщо ми раптом вичерпаємо всі теми, обговорюючи Android на Droid Party, я готовий говорити про футбол годинами. Майте на увазі, я фанат мадридського «Реала» і мюнхенської «Баварії».
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.