Большое интервью с Ханнесом Дорфманом, создателем фреймворка Mosby для Android

в 12:27, , рубрики: android, droid party, mosby, Блог компании Яндекс, Программирование, разработка мобильных приложений, Разработка под android, Тестирование мобильных приложений

2 июня в московском офисе Яндекса пройдет очередная Droid Party. В этот раз своим опытом поделится Ханнес Дорфман. Многим из вас он известен как разработчик фреймворка Mosby для Android. Ханннес много времени уделяет исследованию подходов к Андроид-архитектуре.

В преддверии мероприятия мы попросили Ханнеса ответить на вопросы, которые собрали от разработчиков внутри Яндекса. Интервью получилось большим и интересным. Обсудили будущее языков программирования, получили много прикладных советов и даже вспомнили уже легендарные модели Nokia. Читайте подробнее под катом.

Большое интервью с Ханнесом Дорфманом, создателем фреймворка Mosby для Android - 1

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

Как обычно, вы можете задавать в комментариях свои вопросы — мы передадим их Ханнесу, и он ответит на них на Droid Party.

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

I was always fascinated 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 used also 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 we as 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 specific platform.

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 my favorite is 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 and pure functions. It's also worthwhile to say that MVP, MVVM etc. are not architectures, but rather architectural patterns to separate the View from underlying 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 be used also 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 stupid simple. 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 that important 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 than rather 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 closer look 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 to access corresponding 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 pattern with 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 are there 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 the generate 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 causing 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 based library 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 and one view 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 then will 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 that since 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 layers like the 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 often is 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 code and invite 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 worked 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 for me than 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. Are there 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 permanently to 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. Cуть работы 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, я готов говорить о футболе часами. Имейте в виду, я фанат мадридского «Реала» и мюнхенской «Баварии».

Автор: Яндекс


Поделиться новостью

* - обязательные к заполнению поля