Refactoring Plaid App – A reactive MVP approach
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Dorfmann, Hannes
Nick Butcher, developer advocate at Google, has open sourced an android app called Plaid with an outstanding UI, meaningful animations and a lot of others material design goodies. However, from the software architecture’s point of view, the architecture of this app more “traditional” so that both, beginners and expert developers, can understand the source code easily. Unfortunately, that means that there is a lack of separation of concerns that modern software architectures offers. This talk discusses how to improve the architecture of this app by applying Model-View-Presenter (MVP) to create modular and decoupled components that are easy to test. Furthermore, this talk shows how to improve the code quality by using well known libraries like RxJava (foreknowledge is not a strong requirement), dependency injection and how to test such an App by doing Test-Driven-Development (TDD) from the very beginning. The aim of this talk is to showcase the importance of a well thought out software architecture and how to implement such an MVP based architecture and last but not least to clarify what the word “reactive” actually means in this context.
 Okay. Thank you for the very kind introduction. And by the way, if you have the, uh, Droid.com app installed, please update. There's a bug fix in the Play Store. Oh, sorry. Okay, who knows what does is cells? Yeah, exactly. So cells. Well, the human body is interesting. I don't know if you know, but there are over 100 billion cells in our body and 50 billion die every day. So basically our body is, will be renewed every day and it takes about 10 years to be completely a new person kind of. And more interesting is that those cells are not only replaced by the body, but some of them get stronger, get better. In other words, the human body is refactoring ourselves. And as already said, or as has been already introduced, my name is Hannes and I'm working at Tickaroo. And today we are talking about refactoring the Plaid app. Please raise your hand if you know what the Plaid app is. Okay, quite a few. But, oh, at least I would expect a little bit more because it's quite popular in the developer community, I would say. Uh, not a question. Please raise your hand if you have been slowed down by bad code. So why do you write bad code? Just think about it. And if you have spotted bad code, then you may want to touch it, to change it, right? Make a little change here, a little change there. But then you have to fear to break it, right? And that's where this is very useful. Test-driven development. Test-driven development is basically the only way to ensure that you can refactor, that you can change your code without having to fear to break it. But before we get started, I want to touch some key principles, like solid. Who knows what solid is or what solid stands for? Please raise your hand. Oh, yeah. A good number. So I don't cover all of them, but basically they were five principles introduced by Uncle Bob, or Robert C. Martin's, as his real name is, but he's better known as Uncle Bob. And I will only talk about the single responsibility, because for this talk it's the most important one. So single responsibility, that means a class should only be touched by you as developer for one reason. And it should only be responsible for one thing. So what does that mean? What's the problem with that class? Who can spot it? Please speak out. Yeah. Scream out. Yeah, exactly. That's too many. It does print. It has page. And actually we could refactor that to something like this. We could make a book, which is only responsible for keeping track of pages, and a printer. And the printer gets the book. So now we only have to touch book if you want to change something with pages. And if you want to change the printer implementation, we touched the printer class. So this actually prevents us from making bucks, kind of. Or at least it prevents us from doing stupid things. So now think about activities and fragments. What is the single responsibility of them? Do they have single responsibility at all? Another important thing is immutability. So the way I think of immutability is sitting in a baseball stadium. And if you were in the tribune somewhere in the middle and you get hungry during the match because we all know baseball could take long time, and you will stream over to the vendor to pass you a hotdog. But you were sitting in the middle, so you can't stand up. But what you will do is you stream at them and say, hey, pass me a hotdog. And what he will do is he will give your hotdog to the first one sitting in, to the first person sitting in your row, and he will pass it to his neighbor, until you have it in your hand. And basically, you want an immutable hotdog because you don't want someone touching your hotdog, someone eating a bit of the hotdog, right? And that's the same with programming. If you pass around objects, you don't want other components to touch them, modify them because they only introduce it to you to debug. And moreover, if you use it in a multithreaded environment, this ensures that you don't have race conditions. So how do we do immutable classes in the Java world? Well, there's a nice feature, a nice library called R2Value, who have heard of R2Value before? Yeah, good. So I don't have to introduce that. Along, it's an annotation processor by Google, and basically it generates code out of this class. So you annotate an abstract class as R2Value, then you can also add some extensions like implements possible because there are some extensions for this annotation processor, which automatically generates, for instance, the possible code, the possible interface for you. And there's more. You can do some adjacent annotation for various JSON serializers. And you can also annotate that with add column name to get the cursor map from SQL cursor to your database. And finally, instantiate it, it will build something like that. So you write the new class that will be generated, it's called auto value underscore and your class name, and it has package private visibility. So it's not possible to instantiate it without within another package. So this prevents us from creating an instance of hot dog without wanting to make it immutable. Okay, let's talk about model view controller just for a moment. So model view controller has been invented by Rainskout in 1979 and he was a small dog programmer. And his way of thinking or defining model view presenter was, okay, there's a model and the view, which was an UI widget, for instance, was observing this model. And then there was a controller and the controller was manipulating the model and then the view will be updated automatically because it was observing the model, right? So there was not one big view, there was not one big controller, like we would declare activities or fragments nowadays as controllers. But his idea was mainly something like this, if you have a UI like that or a screen, then every checkbox has its own on check click list now, which is the controller, the checkbox will observe a Boolean flag from a model class and that's the model class actually. And the view is just this UI widget. And now think about what you define as model view controller, or at least the most of us do. We have this big activity with a lot of responsibilities, we talked about single responsibility before. And this kind of reasoning about model view controller, from my point of view, is definitely better. And we should definitely go away from this idea to have one big screen with one big controller with one model, which would split it to smaller UI pieces. Okay, so now let's start with Platt. So Platt was built by Nick Butcher, who is a Google Android developer advocate. And he has built this app. And the main idea of this app is you can have some a list of design inspirational sources like Dribbley and designer, designer news and so on. So there are some sources he fetches over HTTP and displays those items in this list. And it's quite popular on GitHub. It has over 5,000 stars and it's still under development. And quite common in the Android development community, I would say. So let's have a quick look. So the app starts like that. You can scroll. Oh, yeah, you can enable, you have different sources and you can enable each source. So you have a source like popular Dribbley and other from design stories and so on. And it has some really, really nice UI with material design and it's really, really good. It has a really great user experience. But as we will see later from the code quality or from the separation of concerns, we talked before single responsibility, there may be some issues. You can also uncheck and you see that the list is always responding to your filter settings. Okay, just another short video. You can also search for topics like hello. And then you can say I will add this hello to my source list. So basically I can add a filter, a new filter and then it will be displayed in this list. So you can add arbitrary many sources and enable and disable them. And yeah, it also has some details, really nice animations, all the material stuff. It's really, really awesome. Okay, so the problem is no clear separation. It uses async task, which is not a problem per se, but it uses, it also uses Android services, which is also great. But or at least from my point of view, it's not perfect to use short preferences for storing a list of sources which are enabled and disabled. But yeah, I know people do that to quickly get started and you don't have to write ORMs and so on. But from my point of view, a database will be better and we will actually reflect that to use a database afterwards. So the first code smells lead to also UI smells. So for instance, we see that no internet connection and the loading indicator at the same time. Why? Because there are many components manipulating the state of the UI at the same time. Or actually there are not many components, there are many functions in the activity doing that. There are also other things like I actually don't have activated any source, but there are sources displayed. So you see there are some kind of little issues and we're going to reflect to them or at least reflect to the code a little bit to avoid such issues. Okay, let's take a closer look at architecture. So the home activity was basically the list of the items I showed before. And basically it contains a connectivity manager. The connectivity manager listens for internet connection or not, internet connection and displays the error state with no internet connection or not. Then there are a lot of animations which also check for internet connection, check for should loading be displayed and so on. So those two big components are, yeah, manipulating the state of the UI. But there's more. There's the feed adapter which is the cycle adapter for the items in the list. There's the filter adapter which is the adapter for the filter where we can enable and disable sources. And if we add a new component, we start the search activity. If you want to add a new source, we start the search activity and then on activity result we get back the news source we have enabled. And then the main activity will store that somehow. Okay, so on activity result store gets the source back and it stores in the filter adapter. Okay, so the filter adapter has a list of sources and basically when we add a new source, we add it to the filter adapter. Then there is a data manager. And the data manager also has a reference to the filter adapter because whenever we get a new, we want to load new sources, we query this filter adapter to get the list of sources. And basically the data manager also contains a list of calls. It uses Retrofit to make calls and it stores that in a hash map because afterwards it has to clean up the calls when for instance we rotate the screen and so on. And it also contains some page index. So it has some pagination feature. So when you scroll around, you'll add loads the next pages from your sources and so on. And here it stores the kind of yeah, now with this source I'm on page one, with this source I'm on page two and so on and it counts that on. So the data manager is also quite a huge class. And as always said, the filter adapter is connected directly to the data manager. And the data manager is connected to the filter adapter. The filter adapter is connected to the filter adapter. And the filter adapter is connected to the source manager which is to share the sources in the shared preferences. So you see a little problem here with this architecture. All these arrows pointing around. And finally, yeah, the home activity also knows about the data manager because it has to cancel it on destroy. Okay, refactoring. So how do we start with refactoring? As always said, we can't really touch anything without failing to break it without having tests. And usually the app connects to the internet and brings some data from the internet from dribbly designer stories, designer depots and so on. So what we want is to mock the internet to create reproducible tests because we want the test to be always the same. Otherwise we can't compare it if it fails or not. Right? So what we can do, or at least what I usually do is I use this tool called postman. I grab the URL in there and then I get the feed as answer. Then I store this JSON response into a file. And there is a, well, it is a Java gradle, not feature, but yeah, Java gradle way. Usually there are resources in your source slash test slash resources folder. You can put in resources like in the web development. That's quite common. And we also can do that since the Android gradle plugin 1.5, I think, on Android. And then we can basically load this class so we don't have to hard code this JSON string, this huge JSON file in our string in a Java file. We can load it dynamically. Then we also need a mock server, right? And there is obviously from square, who else, offering along with OK HTTP and OK HTTP mock server we can use. And finally, to stick that in because Blatt has no dependency injection, we need a rule. And the JUnit rule allows us to, we will see a code quite after, allows us to hook in this mock server without changing the actual production code. Okay. And there are some links where you can find a little bit more about this rule stuff from Art and Sinner to Lin, quality matters. That's also quite popular, I guess. But this rule thing is a little bit not tricky, but a little bit strange if you haven't got in touch with it. Quality matters might be a good starting point. So how this could look like. We want to write an activity test for our home activity. So we use Espresso. We use that rule with the add mock server. So we automatically use instead of OK HTTP, this OK HTTP mock server. And then we can say server and queue the new mock response with the data from our JSON file which we have stored previously in the resources folder. And then we basically say, okay, unview with the ID, verify that it is displayed. And we could also write some custom matches like to count the items in the recycler view. And since we are always getting the same JSON feed, there are always the same items displayed in this recycler view, right? But there's more. That's only the happy path, right? We test it. We also have to test what happens if, for instance, the server returns HTTP status code 500. And so on. So I have skipped that yet for this session because it's obviously too long. But I guess you get the point, right? Okay, let's talk about the clean architecture a little bit. Because so far we talked a little bit about model view controller, but actually this is just a design pattern to display, to separate the view, the UI from your model. And it's not an architecture. So please raise your hands if you are familiar with the clean architecture. Oh, yeah, great. So I don't have to explain it in detail. But basically on the right, you see entities. Entities are just Java objects. And then there is an Interactor. And the Interactor loads entities. And then there's this dotted line which means there's a boundary. There's an interface. Then the presenter will get the result from the Interactor. Ideally creates a presentation model. And the presentation model contains all the information the UI needs to display it. So for instance, if the color of a button should be red and not green, or it depends on the state of the model, this presentation model will already pre-optimize that for the view so that the view is pretty dumb. The view gets this model and only displays it. So that's the clean architecture. People tend to use use cases and introduce interact on repository patterns and so on. But see the thing with this is this is a guideline. And there's a very smart guy called Jesse Wilson. And he has written this very inspiring blog post about breaking the rules. So what he basically says is if you are new, for instance, he said, I'm not the designer, and I'm new in this topic. I don't know how to design an app. So there are the material guidelines, and I will follow them because I don't know how to do it better. And the same is true for the clean architecture. If you are new and you don't know how to separate your concerns and you structure your UI, decouple your UI from the rest, and build an architecture for your app, please follow this clean architecture. But we should get to the point where we have that in our mind all the time we write. So we don't have to write, oh, that's an interact, oh, that's a present, oh, that's an entity. And that's basically what he says. So back to our pretty arrows. How could we reflect that? So the first thing is the source manager. The source manager is clearly some kind of business logic. So it should be interact directly with the data manager, right? So what we do is we, there's a shortcut in IntelliJ, we say extract interface. We make an interface out of the source manager providing us all the public API, all the public methods that can be called. Then we create, oh, sorry, there was an error. Okay, I don't know. My bad. So then we create a source DAO. DAO stands for data access objects because we now want to interact with a database. And we make this DAO by using the adapter pattern, we wrap it in a little class that implements the same source manager interface as it has been before. But actually then we use the source DAO, which uses reactive programming, Eric's Java. So who's familiar with Eric's Java? Please raise your hands. Oh, great. That's awesome. But why are you actually using Eric's Java? Please speak out. Because of threading. That's a trend. Yeah, that's true. Well, don't get me wrong, Eric's Java is awesome. But just saying it's a trend, it's not really a good point of view, right? Separation of concerns. Yeah, a little bit. But yeah, okay, let's say for now. Sorry. You get pretty code. Yeah, but actually, if you think of pretty code, you may think all these operators like flat map and so on, right? Error handling on error. Yeah, exactly. But see the main thing with reactive programming is that it basically is that who knows what this class is, and who has used it before. Yeah. So basically, reactive programming is all about observing observing a data. Actually, it is a stream. Okay, that's a little bit more than this class offers us. But it's just observing data. Okay, so our source star interacts with the SQL light database. And we use the library from Square called SQL bright, which is a reactive wrapper around it. So basically, it provides us a way to say, okay, query, and we get past the select statement in, and we get an observable of our list of items out, for instance, okay. And the source now looks like this. So I've written that in Kotlin because I really like Kotlin. And it offers get all sources enable sources to disable and enable source and insert and delete. Then we also talked about immutability for obviously, we want our objects to be immutability, to be immutable. So we annotate, even if you use Kotlin, I use auto value there and not Kotlin's data class. And we annotate that fields with the column name and this call name and call enable this basically just a string static final string field with the constant. Okay, and then let's start with TDD actually. So so far, we just have written this express a test to see if everything works if we changed the source now. Instead of the source manager. So with TDD, we follow these rules. First, we will write a failing test. And that sounds a little bit strange, I know. But we will see what what I mean with that. And then we write just enough tests to get past with our unit tests. Okay, and then we repeat over that. So basically, we write a test like, okay, insert that item. And we also that it returns one because we inserted one item. So the test will fail now. Obviously, we'll fail because we haven't implemented it yet. So now we implemented. And then we implemented something like this. Okay, and then the unit test passes. Then we go ahead and write a little bit more for unit test. Now we write a query to get out the inserted item. And now it again fails because we haven't implemented it yet. Then we implement this one. And finally, we are done. Okay, so back to our refactoring. The source manager is now source.dow. And the data manager interacts with the source.dow. So we also run our app, our express the test and it should pass. And now the feet, we changed the feet adapter. The feet adapter don't interact with the filter adapter anymore. But they are just observing the data manager. Right. So now they have references to the data manager. But at least there are not so circular things as we had seen before. Who knows what this is? Yeah. It's cache coherence protocol. Basically, in your multi CPU core, every core has a cache. And each cache has to synchronize his state with another core in your CPU. And they are doing that by using an event bus. Why are they using an event bus? Because they want to, because otherwise it's hard to implement that coherence algorithm. And they rely on a feature that only one core is sending an event over the event bus at the time. So why I'm talking about the event bus? Because people use event bus to separate the UI from the business logic, let's say. And that's fine. But the thing about event bus is it can get out of hands quickly. Then you use the event bus over a little there, over a little here. Just for things where you actually want to use the observer pattern. And people tend to forget about the observer pattern. So we had talked about reactive and then people started with the idea of our reactive event bus, our X bus. But please don't do that. That doesn't make sense. So we talked about the before that the reactive program is all about observing data or observing something. So basically instead of observing on an event bus, observe on that data object directly, that makes much more sense. Okay, so we will split our view presenter and model. Now we are going to implement the model view presenter pattern. But I'm pretty sure that who's not familiar with that pattern? Because I think the most of you are. Yeah, everyone. So don't have to talk about that. But how do we test that? The nice thing about this pattern is that we can test each component separately. So the view for the view we can mock a presenter and simply say, Okay, now display the loading educator. Now display the content. Now display error. And we write a simple test for that. And we don't the same for the presenter for the presenter. We mock the view and maybe the model and see if the presenter calls the right view function at the right methods like show content. And the model actually we already have written tests for the model like the database query. Okay, single responsibility. Now the data manager, previously the data manager has a lot of things to do like making calls and so on. So now we refactor that. We create little classes like the route. The route is basically just making an HTTP call to a backend endpoint. The router actually bundles all endpoints together. So we have a list of all routes in there. Then we have a page because we want to pagination and the responsibility of that class is just to say, we are page one, we are page two, we are page three and so on. And now let's put that all together to something like that. We have the source, the route, the router and the page. And now we connect that with Eric's Java. We make them all observable. So what does that mean? Now, I will show you a short video. When we start with that screen. So we will now call up the filter driver on the right and enable the source. Right now there's no source activated. But the state now is the filter fragment, which is where we saw the list of filters. And the filter presenters are connected to the source manager, to the source. And basically they have made a query like get me all filters. And on the left side, you see the home activity, which is connected through all that page router source. Thing to load the items. And what now happens is if we click here and enable a source, then actually a UI event will be triggered here. Right? This UI event goes down to the source. From the source, it goes to the SQL write database. And now happens something really, really interesting. Since it wasn't observable, we simply rerun the queries. And then please note that even if the icon from popular driblies is now displayed as enabled, it hasn't been enabled because of your click. That's a little bit of an issue of this diagram. But now it goes the way back from your source. Now with the new list of filters of sources, well, this popular driblies is enabled. And then we simply say notify data set changed. Because we have the source made immutable, we can say source.set enabled true. That's not possible in our API because it's immutable. So the only thing who is able to edit the list of filters or the list of sources is the source, the SQL write the underlying database. We always on every change get a new list of items out. And the interesting fact is that on the left hand side, on the left side, the same query will be rerun. The DAO will inform the route that it has been changed. It will add the new route. The route will be passed along the way up because it's all observable. And finally, it displays the item in the home activity. So here are the items. And the nice feature about that is that we have an only directional data flow. There are no presenter to present the communication. They don't communicate with each other at all. There's no need for that. They communicate all the way down, all the way up. And the same for services. If we start over making a new post to Dribble, which is also possibly from this flat app, we would start just an intense service saying, okay, now insert that item. And then we put that in the database and all the views will be updated automatically because we are observing by Eric's Java the database. And we have this only directional data flow. Okay, see, the thing is we shouldn't worry about too much about our UI. And that's actually a problem for my point of view with Scrum because we already want to iterate fast and show the customers new things. And then we really focus on the UI. But we really shouldn't focus more on the model on your business logic. And so we have effects with that. Now we have this only directional data flow, everything is cool. But actually, we are in the same situation as it was before. So we have spent two months on that refactoring that app. The question is who pays us for that refactoring because customers usually don't want for pay for bug fixing and for refactoring. So those those two guys are basically digit from the Google team said that who cares about architecture. And that's right. It's not that important about architecture because the user only cares about the UI. But we want to move fast, right? And we only can move fast if you do test driven development if we care about the architecture. And as you see, this automatically updating results in less UI box, which is a better user experience. Okay, so move fast without favoring breaking things with and the VM. Sorry, I meant test driven development. And keep in mind that the code you write today will be legacy code tomorrow and you better be prepared for that. Basically, there will be war out there with you and your code you have written yesterday and some friendly fire from some co workers code. And in the human body is doing the same. The cells in our human body today are legacy cells tomorrow. So we better build cell alike components in our code by applying a clean architecture by taking care of unidirectional data flow. And there's also topics and talks in this conference about this unidirectional data flow about other patterns like redux and so on. So last but not least, I want to thank my employer for supporting me and we are hiring. And if you have questions, we I think we have two minutes left. For your questions, I have a microphone over here just raise your hand one, one, one, one, one, one, one. You talked about mocking JSON object objects and do you need testing with mocked objects? How can I effectively test my mocked data if the response from the app changes every day if the structure keeps on changing? How can I maybe implement a good flow to be aware of these changes and to keep the tests working with actualized JSON? I can't hear you very well because the speakers are in your direction. Could you please repeat the last part? How can I keep on testing with mocked data when the response from the app changes every day if the structure if there's an app team working on the app and the response changes every day? How can I keep the unit tests working? You mean how do you test when no, sorry, I don't get it. You mean how to test the unit how to test various responses at the same time? No, you just have an app and the response of the app, the structure of the response changes often. How can I keep on running this unit test? Well, yeah, that's something you have to adjust. That's that's a problem. But that's not really a problem, I would say, but that's that happens. That's that's not an issue that you have to do that for your app anyway. That's not an issue for your JSON. Actually, with that approach, you just have to change that JSON deserializing stuff and not really the unit test, the unit test will be work fine. But you have to refatch the JSON feed and so it in the resource folder. Yeah. Guys, please keep seated and keep quiet until for the next three minutes or so because there are some more questions and we want to give people the chance to ask those questions. We will have plenty of time to find the next room for the next talk. Thank you. Hi, my question was, we are also trying to use like MVP pattern and also like this is reactive Java and where we do the second I have to step up. I can't hear you. Okay, now. Okay, so we are using MVP pattern with early Java is like observables and immutable objects. And I face one issue. For example, we are having like a huge like card in one hour, recite their view. And like all of this is immutable. And basically, we don't have to interact with it a lot. And there is like a small tiny thing that may be changed. And with all their observables, we are going like we're making this like going down saying that we change it. And whenever we are changing it, we are creating a new object and replacing the old object with a new object like and notify data sign changed. Yeah, the issue I faced, it was like not really nicely updated. So basically, whole view was like blinking. And like, even if we need to update like a small, really small part, because like the whole object is immutable. So for me, it was like, my solution was to keep like this small changes mutable, and the whole object immutable. But it's like not exactly the, yeah, that's strange that you while we do at work pretty the same thing. And we do a lot with item animator. For instance, we I'm working on a on the kicker app. And here we have some match result displayed. And we have some tip guessing feature. So we can sweep flip this cells by using this unidirectional data flow and a custom recycled view item animator. And we never have faced such a problem, as you said, with blinking and so on. Like, it was the same object. For example, there is like, there is a like button. Yeah, I get your point. Like, for example, and there's like, you need to only to make this like button like liked, for example, and the whole like picture, which is over there. It's like, whenever we are changing it, we are like basically updating the object and it's like changing like one to another one. And it doesn't look that nice. Okay. Yeah, another has faced this issue. But the problem I see with your approach is that now you get a little bit away from the traditional, let's say from the Reanscart model, your controller thing, because now you have a state in your UI, and you have a state in your model. And maybe they are not in the same state, which might may or may not be an issue. But once you start with that, it's just one step further to do much uglier things, I would say. But to be honest, I never have faced such an issue regarding immutability, blinking UI and performance of creating a list every time again. But maybe we can talk about it later. Or you can explain me a little bit better. Thank you. I think we have one last question before we have to break up here. Hi. You said that you are still using auto value besides instead of Kotlin data class. Could you say why? Basically, because of the extension function they provide by auto value, I can create, they can create or I can, there are libraries for auto value like auto value possible. So basically, I can make my class implement possible, and my auto value annotate the class possible. And it does all the rest for me. It generates the possible interface automatically. So you mean the extensions because of the auto value extensions? Exactly. Yeah. Because of the auto value extensions. That's the big point for me. Okay. Thank you. Thanks. Thank you. Thanks on this again. Thank you, everybody.
 Okay. Thank you for the very kind introduction. And by the way, if you have the, uh, Droid.com  app installed, please update. There's a bug fix in the Play Store. Oh, sorry. Okay, who  knows what does is cells? Yeah, exactly. So cells. Well, the human body is interesting.  I don't know if you know, but there are over 100 billion cells in our body and 50 billion  die every day. So basically our body is, will be renewed every day and it takes about 10  years to be completely a new person kind of. And more interesting is that those cells are  not only replaced by the body, but some of them get stronger, get better. In other words,  the human body is refactoring ourselves. And as already said, or as has been already introduced,  my name is Hannes and I'm working at Tickaroo. And today we are talking about refactoring  the Plaid app. Please raise your hand if you know what the Plaid app is. Okay, quite a few.  But, oh, at least I would expect a little bit more because it's quite popular in the  developer community, I would say. Uh, not a question. Please raise your hand if you  have been slowed down by bad code. So why do you write bad code? Just think about it. And  if you have spotted bad code, then you may want to touch it, to change it, right? Make  a little change here, a little change there. But then you have to fear to break it, right?  And that's where this is very useful. Test-driven development. Test-driven development is basically  the only way to ensure that you can refactor, that you can change your code without having  to fear to break it. But before we get started, I want to touch some key principles, like  solid. Who knows what solid is or what solid stands for? Please raise your hand. Oh, yeah.  A good number. So I don't cover all of them, but basically they were five principles introduced  by Uncle Bob, or Robert C. Martin's, as his real name is, but he's better known as Uncle  Bob. And I will only talk about the single responsibility, because for this talk it's  the most important one. So single responsibility, that means a class should only be touched  by you as developer for one reason. And it should only be responsible for one thing.  So what does that mean? What's the problem with that class? Who can spot it? Please speak  out. Yeah. Scream out. Yeah, exactly. That's too many. It does print. It has page. And  actually we could refactor that to something like this. We could make a book, which is  only responsible for keeping track of pages, and a printer. And the printer gets the book.  So now we only have to touch book if you want to change something with pages. And if you  want to change the printer implementation, we touched the printer class. So this actually  prevents us from making bucks, kind of. Or at least it prevents us from doing stupid  things. So now think about activities and fragments. What is the single responsibility  of them? Do they have single responsibility at all? Another important thing is immutability.  So the way I think of immutability is sitting in a baseball stadium. And if you were in  the tribune somewhere in the middle and you get hungry during the match because we all  know baseball could take long time, and you will stream over to the vendor to pass you  a hotdog. But you were sitting in the middle, so you can't stand up. But what you will do  is you stream at them and say, hey, pass me a hotdog. And what he will do is he will give  your hotdog to the first one sitting in, to the first person sitting in your row, and  he will pass it to his neighbor, until you have it in your hand. And basically, you want  an immutable hotdog because you don't want someone touching your hotdog, someone eating  a bit of the hotdog, right? And that's the same with programming. If you pass around  objects, you don't want other components to touch them, modify them because they only  introduce it to you to debug. And moreover, if you use it in a multithreaded environment,  this ensures that you don't have race conditions. So how do we do immutable classes in the Java  world? Well, there's a nice feature, a nice library called R2Value, who have heard of  R2Value before? Yeah, good. So I don't have to introduce that. Along, it's an annotation  processor by Google, and basically it generates code out of this class. So you annotate an  abstract class as R2Value, then you can also add some extensions like implements possible  because there are some extensions for this annotation processor, which automatically  generates, for instance, the possible code, the possible interface for you. And there's  more. You can do some adjacent annotation for various JSON serializers. And you can  also annotate that with add column name to get the cursor map from SQL cursor to your  database. And finally, instantiate it, it will build something like that. So you write  the new class that will be generated, it's called auto value underscore and your class  name, and it has package private visibility. So it's not possible to instantiate it without  within another package. So this prevents us from creating an instance of hot dog without  wanting to make it immutable. Okay, let's talk about model view controller just for a  moment. So model view controller has been invented by Rainskout in 1979 and he was a  small dog programmer. And his way of thinking or defining model view presenter was, okay,  there's a model and the view, which was an UI widget, for instance, was observing this  model. And then there was a controller and the controller was manipulating the model  and then the view will be updated automatically because it was observing the model, right?  So there was not one big view, there was not one big controller, like we would declare  activities or fragments nowadays as controllers. But his idea was mainly something like this,  if you have a UI like that or a screen, then every checkbox has its own on check click  list now, which is the controller, the checkbox will observe a Boolean flag from a model class  and that's the model class actually. And the view is just this UI widget. And now think  about what you define as model view controller, or at least the most of us do. We have this  big activity with a lot of responsibilities, we talked about single responsibility before.  And this kind of reasoning about model view controller, from my point of view, is definitely  better. And we should definitely go away from this idea to have one big screen with one  big controller with one model, which would split it to smaller UI pieces. Okay, so now  let's start with Platt. So Platt was built by Nick Butcher, who is a Google Android developer  advocate. And he has built this app. And the main idea of this app is you can have some  a list of design inspirational sources like Dribbley and designer, designer news and so  on. So there are some sources he fetches over HTTP and displays those items in this list.  And it's quite popular on GitHub. It has over 5,000 stars and it's still under development.  And quite common in the Android development community, I would say. So let's have a quick  look. So the app starts like that. You can scroll. Oh, yeah, you can enable, you have  different sources and you can enable each source. So you have a source like popular Dribbley  and other from design stories and so on. And it has some really, really nice UI with material  design and it's really, really good. It has a really great user experience. But as we  will see later from the code quality or from the separation of concerns, we talked before  single responsibility, there may be some issues. You can also uncheck and you see that the  list is always responding to your filter settings. Okay, just another short video. You can also  search for topics like hello. And then you can say I will add this hello to my source  list. So basically I can add a filter, a new filter and then it will be displayed in this  list. So you can add arbitrary many sources and enable and disable them. And yeah, it  also has some details, really nice animations, all the material stuff. It's really, really  awesome. Okay, so the problem is no clear separation. It uses async task, which is not  a problem per se, but it uses, it also uses Android services, which is also great. But  or at least from my point of view, it's not perfect to use short preferences for storing  a list of sources which are enabled and disabled. But yeah, I know people do that to quickly  get started and you don't have to write ORMs and so on. But from my point of view, a database  will be better and we will actually reflect that to use a database afterwards. So the  first code smells lead to also UI smells. So for instance, we see that no internet connection  and the loading indicator at the same time. Why? Because there are many components manipulating  the state of the UI at the same time. Or actually there are not many components, there are many  functions in the activity doing that. There are also other things like I actually don't  have activated any source, but there are sources displayed. So you see there are some kind of  little issues and we're going to reflect to them or at least reflect to the code a little  bit to avoid such issues. Okay, let's take a closer look at architecture. So the home  activity was basically the list of the items I showed before. And basically it contains  a connectivity manager. The connectivity manager listens for internet connection or not, internet  connection and displays the error state with no internet connection or not. Then there  are a lot of animations which also check for internet connection, check for should loading  be displayed and so on. So those two big components are, yeah, manipulating the state of the UI.  But there's more. There's the feed adapter which is the cycle adapter for the items in  the list. There's the filter adapter which is the adapter for the filter where we can  enable and disable sources. And if we add a new component, we start the search activity.  If you want to add a new source, we start the search activity and then on activity result  we get back the news source we have enabled. And then the main activity will store that  somehow. Okay, so on activity result store gets the source back and it stores in the  filter adapter. Okay, so the filter adapter has a list of sources and basically when we  add a new source, we add it to the filter adapter. Then there is a data manager. And  the data manager also has a reference to the filter adapter because whenever we get a new,  we want to load new sources, we query this filter adapter to get the list of sources.  And basically the data manager also contains a list of calls. It uses Retrofit to make  calls and it stores that in a hash map because afterwards it has to clean up the calls when  for instance we rotate the screen and so on. And it also contains some page index. So it  has some pagination feature. So when you scroll around, you'll add loads the next pages from  your sources and so on. And here it stores the kind of yeah, now with this source I'm on page  one, with this source I'm on page two and so on and it counts that on. So the data manager is  also quite a huge class. And as always said, the filter adapter is connected directly to the  data manager. And the data manager is connected to the filter adapter. The filter adapter is  connected to the filter adapter. And the filter adapter is connected to the source manager which  is to share the sources in the shared preferences. So you see a little problem here with this  architecture. All these arrows pointing around. And finally, yeah, the home activity also knows  about the data manager because it has to cancel it on destroy. Okay, refactoring. So how do we  start with refactoring? As always said, we can't really touch anything without failing to break it  without having tests. And usually the app connects to the internet and brings some data from the  internet from dribbly designer stories, designer depots and so on. So what we want is to mock the  internet to create reproducible tests because we want the test to be always the same. Otherwise  we can't compare it if it fails or not. Right? So what we can do, or at least what I usually do is  I use this tool called postman. I grab the URL in there and then I get the feed as answer. Then I  store this JSON response into a file. And there is a, well, it is a Java gradle, not feature, but  yeah, Java gradle way. Usually there are resources in your source slash test slash resources folder. You  can put in resources like in the web development. That's quite common. And we also can do that since  the Android gradle plugin 1.5, I think, on Android. And then we can basically load this class so we  don't have to hard code this JSON string, this huge JSON file in our string in a Java file. We can  load it dynamically. Then we also need a mock server, right? And there is obviously from square, who  else, offering along with OK HTTP and OK HTTP mock server we can use. And finally, to stick that in  because Blatt has no dependency injection, we need a rule. And the JUnit rule allows us to, we will  see a code quite after, allows us to hook in this mock server without changing the actual production code.  Okay. And there are some links where you can find a little bit more about this rule stuff from  Art and Sinner to Lin, quality matters. That's also quite popular, I guess. But this rule thing is a  little bit not tricky, but a little bit strange if you haven't got in touch with it. Quality matters  might be a good starting point. So how this could look like. We want to write an activity test for  our home activity. So we use Espresso. We use that rule with the add mock server. So we automatically  use instead of OK HTTP, this OK HTTP mock server. And then we can say server and queue the new mock  response with the data from our JSON file which we have stored previously in the resources folder. And then we basically say,  okay, unview with the ID, verify that it is displayed. And we could also write some custom matches like to  count the items in the recycler view. And since we are always getting the same JSON feed, there are always the  same items displayed in this recycler view, right? But there's more. That's only the happy path, right? We  test it. We also have to test what happens if, for instance, the server returns HTTP status code 500. And so on. So I have  skipped that yet for this session because it's obviously too long. But I guess you get the point, right? Okay, let's talk  about the clean architecture a little bit. Because so far we talked a little bit about model view controller, but  actually this is just a design pattern to display, to separate the view, the UI from your model. And it's not an  architecture. So please raise your hands if you are familiar with the clean architecture. Oh, yeah, great. So I don't  have to explain it in detail. But basically on the right, you see entities. Entities are just Java objects. And then  there is an Interactor. And the Interactor loads entities. And then there's this dotted line which means there's a  boundary. There's an interface. Then the presenter will get the result from the Interactor. Ideally creates a  presentation model. And the presentation model contains all the information the UI needs to display it. So for instance,  if the color of a button should be red and not green, or it depends on the state of the model, this presentation model  will already pre-optimize that for the view so that the view is pretty dumb. The view gets this model and only displays  it. So that's the clean architecture. People tend to use use cases and introduce interact on repository patterns and so  on. But see the thing with this is this is a guideline. And there's a very smart guy called Jesse Wilson. And he has  written this very inspiring blog post about breaking the rules. So what he basically says is if you are new, for  instance, he said, I'm not the designer, and I'm new in this topic. I don't know how to design an app. So there are the  material guidelines, and I will follow them because I don't know how to do it better. And the same is true for the  clean architecture. If you are new and you don't know how to separate your concerns and you structure your UI,  decouple your UI from the rest, and build an architecture for your app, please follow this clean architecture. But we  should get to the point where we have that in our mind all the time we write. So we don't have to write, oh, that's an  interact, oh, that's a present, oh, that's an entity. And that's basically what he says. So back to our pretty  arrows. How could we reflect that? So the first thing is the source manager. The source manager is clearly some kind of  business logic. So it should be interact directly with the data manager, right? So what we do is we, there's a shortcut in  IntelliJ, we say extract interface. We make an interface out of the source manager providing us all the public API, all  the public methods that can be called. Then we create, oh, sorry, there was an error. Okay, I don't know. My bad. So  then we create a source DAO. DAO stands for data access objects because we now want to interact with a database. And  we make this DAO by using the adapter pattern, we wrap it in a little class that implements the same source manager  interface as it has been before. But actually then we use the source DAO, which uses reactive programming, Eric's  Java. So who's familiar with Eric's Java? Please raise your hands. Oh, great. That's awesome. But why are you actually  using Eric's Java? Please speak out. Because of threading. That's a trend. Yeah, that's true. Well, don't get me  wrong, Eric's Java is awesome. But just saying it's a trend, it's not really a good point of view, right?  Separation of concerns. Yeah, a little bit. But yeah, okay, let's say for now. Sorry. You get pretty  code. Yeah, but actually, if you think of pretty code, you may think all these operators like flat map and so on,  right? Error handling on error. Yeah, exactly. But see the main thing with reactive programming is that it basically is  that who knows what this class is, and who has used it before. Yeah. So basically, reactive programming is all about  observing observing a data. Actually, it is a stream. Okay, that's a little bit more than this class offers us. But it's  just observing data. Okay, so our source star interacts with the SQL light database. And we use the library from  Square called SQL bright, which is a reactive wrapper around it. So basically, it provides us a way to say, okay,  query, and we get past the select statement in, and we get an observable of our list of items out, for instance, okay. And the  source now looks like this. So I've written that in Kotlin because I really like Kotlin. And it offers get all sources  enable sources to disable and enable source and insert and delete. Then we also talked about immutability for obviously, we  want our objects to be immutability, to be immutable. So we annotate, even if you use Kotlin, I use auto value there and not  Kotlin's data class. And we annotate that fields with the column name and this call name and call enable this basically  just a string static final string field with the constant. Okay, and then let's start with TDD actually. So so far, we  just have written this express a test to see if everything works if we changed the source now. Instead of the source  manager. So with TDD, we follow these rules. First, we will write a failing test. And that sounds a little bit  strange, I know. But we will see what what I mean with that. And then we write just enough tests to get past with our  unit tests. Okay, and then we repeat over that. So basically, we write a test like, okay, insert that item. And we  also that it returns one because we inserted one item. So the test will fail now. Obviously, we'll fail because we haven't  implemented it yet. So now we implemented. And then we implemented something like this. Okay, and then the unit  test passes. Then we go ahead and write a little bit more for unit test. Now we write a query to get out the inserted  item. And now it again fails because we haven't implemented it yet. Then we implement this one. And finally, we are  done. Okay, so back to our refactoring. The source manager is now source.dow. And the data manager interacts with the  source.dow. So we also run our app, our express the test and it should pass. And now the feet, we changed the feet  adapter. The feet adapter don't interact with the filter adapter anymore. But they are just observing the data manager.  Right. So now they have references to the data manager. But at least there are not so circular things as we had seen  before. Who knows what this is? Yeah. It's cache coherence protocol. Basically, in your multi CPU core, every core has  a cache. And each cache has to synchronize his state with another core in your CPU. And they are doing that by using  an event bus. Why are they using an event bus? Because they want to, because otherwise it's hard to implement that  coherence algorithm. And they rely on a feature that only one core is sending an event over the event bus at the  time. So why I'm talking about the event bus? Because people use event bus to separate the UI from the business logic,  let's say. And that's fine. But the thing about event bus is it can get out of hands quickly. Then you use the event  bus over a little there, over a little here. Just for things where you actually want to use the observer pattern. And  people tend to forget about the observer pattern. So we had talked about reactive and then people started with the idea of  our reactive event bus, our X bus. But please don't do that. That doesn't make sense. So we talked about the before that the  reactive program is all about observing data or observing something. So basically instead of observing on an event bus,  observe on that data object directly, that makes much more sense. Okay, so we will split our view presenter and model. Now  we are going to implement the model view presenter pattern. But I'm pretty sure that who's not familiar with that pattern?  Because I think the most of you are. Yeah, everyone. So don't have to talk about that. But how do we test that? The nice  thing about this pattern is that we can test each component separately. So the view for the view we can mock a presenter  and simply say, Okay, now display the loading educator. Now display the content. Now display error. And we write a simple test  for that. And we don't the same for the presenter for the presenter. We mock the view and maybe the model and see if the  presenter calls the right view function at the right methods like show content. And the model actually we already have  written tests for the model like the database query. Okay, single responsibility. Now the data manager, previously the data  manager has a lot of things to do like making calls and so on. So now we refactor that. We create little classes like the  route. The route is basically just making an HTTP call to a backend endpoint. The router actually bundles all endpoints  together. So we have a list of all routes in there. Then we have a page because we want to pagination and the responsibility of  that class is just to say, we are page one, we are page two, we are page three and so on. And now let's put that all  together to something like that. We have the source, the route, the router and the page. And now we connect that with  Eric's Java. We make them all observable. So what does that mean? Now, I will show you a short video. When we start with  that screen. So we will now call up the filter driver on the right and enable the source. Right now there's no source  activated. But the state now is the filter fragment, which is where we saw the list of filters. And the filter  presenters are connected to the source manager, to the source. And basically they have made a query like get me all filters.  And on the left side, you see the home activity, which is connected through all that page router source. Thing to load the  items. And what now happens is if we click here and enable a source, then actually a UI event will be triggered here.  Right? This UI event goes down to the source. From the source, it goes to the SQL write database. And now happens  something really, really interesting. Since it wasn't observable, we simply rerun the queries. And then please note that even  if the icon from popular driblies is now displayed as enabled, it hasn't been enabled because of your click. That's a little bit  of an issue of this diagram. But now it goes the way back from your source. Now with the new list of filters of sources,  well, this popular driblies is enabled. And then we simply say notify data set changed. Because we have the source made  immutable, we can say source.set enabled true. That's not possible in our API because it's immutable. So the only thing who is  able to edit the list of filters or the list of sources is the source, the SQL write the underlying database. We always on  every change get a new list of items out. And the interesting fact is that on the left hand side, on the left side, the same  query will be rerun. The DAO will inform the route that it has been changed. It will add the new route. The route will be  passed along the way up because it's all observable. And finally, it displays the item in the home activity. So here are the  items. And the nice feature about that is that we have an only directional data flow. There are no presenter to present the  communication. They don't communicate with each other at all. There's no need for that. They communicate all the way down,  all the way up. And the same for services. If we start over making a new post to Dribble, which is also possibly from this  flat app, we would start just an intense service saying, okay, now insert that item. And then we put that in the database and  all the views will be updated automatically because we are observing by Eric's Java the database. And we have this  only directional data flow. Okay, see, the thing is we shouldn't worry about too much about our UI. And that's actually a problem for my  point of view with Scrum because we already want to iterate fast and show the customers new things. And then we really focus on  the UI. But we really shouldn't focus more on the model on your business logic. And so we have effects with that. Now we have  this only directional data flow, everything is cool. But actually, we are in the same situation as it was before. So we have  spent two months on that refactoring that app. The question is who pays us for that refactoring because customers usually don't  want for pay for bug fixing and for refactoring. So those those two guys are basically digit from the Google team said  that who cares about architecture. And that's right. It's not that important about architecture because the user only cares about  the UI. But we want to move fast, right? And we only can move fast if you do test driven development if we care about the  architecture. And as you see, this automatically updating results in less UI box, which is a better user experience. Okay, so  move fast without favoring breaking things with and the VM. Sorry, I meant test driven development. And keep in mind that  the code you write today will be legacy code tomorrow and you better be prepared for that. Basically, there will be war out  there with you and your code you have written yesterday and some friendly fire from some co workers code. And in the human body is  doing the same. The cells in our human body today are legacy cells tomorrow. So we better build cell alike components in our  code by applying a clean architecture by taking care of unidirectional data flow. And there's also topics and talks in this  conference about this unidirectional data flow about other patterns like redux and so on. So last but not least, I want to thank  my employer for supporting me and we are hiring. And if you have questions, we I think we have two minutes left. For your  questions, I have a microphone over here just raise your hand one, one, one, one, one, one, one. You talked about mocking  JSON object objects and do you need testing with mocked objects? How can I effectively test my mocked data if the  response from the app changes every day if the structure keeps on changing? How can I maybe implement a good flow to be aware of  these changes and to keep the tests working with actualized JSON?  I can't hear you very well because the speakers are in your direction. Could you please repeat the last part? How can I keep on  testing with mocked data when the response from the app changes every day if the structure if there's an app team working on  the app and the response changes every day? How can I keep the unit tests working?  You mean how do you test when no, sorry, I don't get it. You mean how to test the unit how to test various responses at the  same time?  No, you just have an app and the response of the app, the structure of the response changes often. How can I keep on running this unit  test?  Well, yeah, that's something you have to adjust. That's that's a problem. But that's not really a problem, I would say, but that's  that happens. That's that's not an issue that you have to do that for your app anyway. That's not an issue for your JSON.  Actually, with that approach, you just have to change that JSON deserializing stuff and not really the unit test, the unit  test will be work fine. But you have to refatch the JSON feed and so it in the resource folder. Yeah.  Guys, please keep seated and keep quiet until for the next three minutes or so because there are some more questions and we  want to give people the chance to ask those questions. We will have plenty of time to find the next room for the next  talk. Thank you.  Hi, my question was, we are also trying to use like MVP pattern and also like this is reactive Java and  where we do the second I have to step up. I can't hear you. Okay, now.  Okay, so we are using MVP pattern with early Java is like observables and immutable objects. And I face one issue. For  example, we are having like a huge like card in one hour, recite their view. And like all of this is immutable. And  basically, we don't have to interact with it a lot. And there is like a small tiny thing that may be changed. And with  all their observables, we are going like we're making this like going down saying that we change it. And  whenever we are changing it, we are creating a new object and replacing the old object with a new object like and  notify data sign changed. Yeah, the issue I faced, it was like not really nicely updated. So basically, whole view was  like blinking. And like, even if we need to update like a small, really small part, because like the whole object is  immutable. So for me, it was like, my solution was to keep like this small changes mutable, and the whole object  immutable. But it's like not exactly the, yeah, that's strange that you while we do at work pretty the same thing. And  we do a lot with item animator. For instance, we I'm working on a on the kicker app. And here we have some match  result displayed. And we have some tip guessing feature. So we can sweep flip this cells by using this unidirectional  data flow and a custom recycled view item animator. And we never have faced such a problem, as you said, with  blinking and so on.  Like, it was the same object. For example, there is like, there is a like button. Yeah, I get your point. Like, for  example, and there's like, you need to only to make this like button like liked, for example, and the whole like  picture, which is over there. It's like, whenever we are changing it, we are like basically updating the object  and it's like changing like one to another one. And it doesn't look that nice.  Okay. Yeah, another has faced this issue. But the problem I see with your approach is that now you get a little bit  away from the traditional, let's say from the Reanscart model, your controller thing, because now you have a state in  your UI, and you have a state in your model. And maybe they are not in the same state, which might may or may not  be an issue. But once you start with that, it's just one step further to do much uglier things, I would say. But to be  honest, I never have faced such an issue regarding immutability, blinking UI and performance of creating a list  every time again. But maybe we can talk about it later. Or you can explain me a little bit better.  Thank you. I think we have one last question before we have to break up here.  Hi. You said that you are still using auto value besides instead of Kotlin data class. Could you say why?  Basically, because of the extension function they provide by auto value, I can create, they can create or I can, there  are libraries for auto value like auto value possible. So basically, I can make my class implement possible, and my  auto value annotate the class possible. And it does all the rest for me. It generates the possible interface automatically.  So you mean the extensions because of the auto value extensions?  Exactly. Yeah. Because of the auto value extensions. That's the big point for me. Okay. Thank you. Thanks.  Thank you. Thanks on this again. Thank you, everybody.
_droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_0_119.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_120_491.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_492_11195.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_11196_20819.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_20820_44939.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_44940_47039.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_47040_47987.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_47988_48779.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_48780_49151.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_49152_49955.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_49956_51851.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_51852_52751.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_52752_53447.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_53448_54143.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_54144_54371.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_54372_56507.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_56508_57455.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_57456_57983.png _droidconDE__Hannes_Dorfmann___Refactoring_Plaid_A_wWyPc_HN77c_57984_58859.png