The Presenter and View have completely different responsibilities which would indicate that they are not related. Seems like an interface would be a perfect fit in this case. Let’s go ahead and move forward with that. So how would the diagram look now?

Detachment of Responsibility

While the App.WebPost and UI.Post packages still have a loose dependence on each other, the logic is separated into their own respective packages. Class methods that were required to be public are now package private and cannot be used by outsiders. This is crucial for future development and iterations in order to enforce a strict separate of concerns at first glance. The Presenter, View, and the Builder class now have the flexibility to implement the detail of their responsibilities without affecting outside packages. Let’s review the responsibilities of each class now.

WebPresenter: It is still the “brains” of the operations. The class is now package-private but implements the UI.Post.Presenter interface to signify what it’s designed for.

View: This new interface is now the outgoing connection for the WebPost package. It clearly defines how WebPresenter responds to its views without caring how the view handles it internally. Notice that it utilizes a ViewModel as a parameter but since it does not utilize the internal of what a ViewModel is, there is no dependency. That is left to the class that implements it.

PostView: Responsible for maintaining the UI and displaying the data provided by the View. Similar to WebPresenter, it now utilizes a interface to communicate to the outside by way of the Presenter interface. It also has the flexibility to implement any other interfaces like App.WebPost.View as its requirements change.

Presenter: Very similar to the View class. This interface provides PostView to communicate to an outside Presenter without caring how it’s done.

ViewModel: The major difference in this class is the visibility of its getters. They’re now package-private in order to enforce that no other package can access its data. Only the UI.Post package has this ability. This allows App.WebPost to maintain a reference to pass back to the View without compromising the architecture.

Builder: A class that has evolved to be abstract with protected methods and only one public build() method. This enforces that only derived classes can access or modify its content how they see fit but anyone can build its current state. The biggest advantage of implementing this way is the Builder’s fields are now closely tied to the ViewModel class. Should any of the requirements change, the Builder and ViewModel are both in one file for the developer to see.

ViewModelBuilder: This new class now implements the details of creating a ViewModel. This is specific to business logic which is why it resides in App.WebPost. Notice that this class is entirely package private as they are the internal details of how a ViewModel should be defined specific to a WebPost.

Model: The requirements for this class has not changed since it was internal class since the beginning and does not interact with the outside world.

Summary

Implementing features that were once small and straightforward can quickly escalate into large system that have a lot of moving parts. This architecture design will provide many advantages that may not be obvious initially. Advantages such as…

1. Encouraging a separation of concern

There’s nothing stopping you from implementing all these features in one Activity. But for everyone’s sanity, please don’t. I can go into a number of reasons why but I think everyone can agree that no one likes to see a class (let alone a method) that is over 1000, 2000, or even 3000 lines long and has to modify it. Providing these distinct separations will encourage developers to separate their responsibilities into different systems.

2. Testability

This is the biggest advantage over the base implementation. With more features being implemented, it can be hard to know if you’re breaking previous logic without extensive regression testing. Providing interfaces and reducing dependencies will make it easier for unit and integration tests to be written.

3. Scalable Components

After separating different aspects of a feature (UI, API requests, caching, the rules for displaying the data [business logic], etc.) into their respective components, you can easily start to see the patterns within each one. Providing a separate environment (such as a package, module, etc.) for each component encourages reusability. UI that once seemed too tightly coupled with the business logic might now seem obvious that it’s being reused time and time again by multiple features.