The following summarizes the structure of the code and highlights the structure of a use case. This code will be used as an example in lectures and as the starter code for your next lab activity, so please take the time to get familiar with the code this week.
To get started exploring the code, fork this repo on GitHub and then make a clone.
Open the project in IntelliJ and make sure you can successfully run app/Main.java.
Note: you may need to set the Project SDK in the
Project Structure...menu, and possibly also manually link the Maven project.
Try the signup, login, and change password use cases by running the program. Notice that the "Log Out" button doesn't do anything when you click it — to test whether change password worked, you'll need to quit and rerun the program.
Note: some other buttons, like the "Cancel" buttons, are also not fully functional.
Explore the package structure in src\main\java\. There are packages for
the CA layers — view, interface_adapter, use_case,
entity, and data_access — as well as app, a package for the main program
and code for building everything.
Two of these packages, use_case and interface_adapter, have subpackages for each of the
two use cases: login and signup. None of the Interactor and Interface Adapter code is
shared between use cases.
Several packages don't have subpackages: data_access, entity, view, and
app.
- The same View object may have several Use Case buttons inside a single
JPanel, so separating by use case isn't possible. - Entities represent the data from the problem domain that all Use Cases manipulate.
- The Data Access layer is responsible for saving and reading the Entities.
- The main application is responsible for building the CA engine and starting the GUI. After the engine is built and the UI becomes visible, the program is driven by the user and the main program has nothing left to do.
"Sign up" is a verb phrase and "signup" is a noun. That generalizes: "check in" vs "checkin", "log in" vs "login". Two words for the verb phrase, 1 word for the noun phrase.
For example, to complete a login, you need to log in. (Say it out loud. They sound different.)
In "the login process", "login" is a noun acting as an adjective to describe "process". "Basketball coach" is another example of this English construct.
Let's compare these two use cases.
In IntelliJ, find LoginController and double click it to open it.
Now right-click on SignupController and select Open in Right Split. When you do, you will see the two controllers side by side.
They are identical in structure, differing only in the
details.
This is powerful: most controllers will look similar. Most presenters will look similar. Most interactors will look similar. Any programmer who learns about the CA will have a good understanding of any controller, interactor, and use case.
Thought question: open the CA Engine diagram and compare the types
in LoginController to the diagram. You'll notice that both controllers
have an Input Boundary that is injected in the constructor, both create
Input Data from the parameters in method execute, and both
of them call the Use Case execute method, passing in the Input Data. All
the arguments for the execute method come from the View.
Open LoginPresenter and SignupPresenter side by side. Both have
View Model variables and a View Manager Model that are injected into the
constructor.
Both also have a prepareSuccessView method that the Use Case calls
when it is complete. The job of this method is to update the View Models.
Read the code for this method in either presenter.
Notice that both of the prepareSuccessView methods mutate the state of a View Model
and call firePropertyChanged to alert the relevant View Model that
the state has changed, and ends with code that tells the View Manager Model
what the active View should be.
Both Presenters also have a prepareFailView method to handle errors.
Now compare LoginInteractor and SignupInteractor side by side. (You can drag
tabs around if you like.)
Thought question: Why doesn't the LoginInteractor have a UserFactory
but SignupInteractor does?
A Controller calls the execute method in an Interactor to start processing
the Use Case data. When it's done, the Interactor tells its Presenter what the result
is, and the Presenter puts it into the View Model and tells the View Model Manager to change
which View is showing.
Compare LoginInteractor and SignupInteractor. Notice that both
use an Input Boundary, Input Data, Output Boundary, and
Output Data. Both also have a Data Access Interface, which is what the Interactor
uses to get data relevant to the Use Case.
The Data Access Interface and Output Boundary are injected in the constructor.
Method execute is passed Input Data to process. The Interactor fetches the
appropriate piece of persistent data from the Data Access Interface, does some error checking
to make sure the Use Case makes sense, and then does whatever the Use Case
is supposed to do. Notice the Interactors both end by creating Output Data and
telling the Presenter to present it.
There are three DAOs in package data_access! All three implement the Data Access Interface
from the use cases. The Use Case code works with any of them.
-
Class
FileUserDataAccessObjectmanages data storage and retrieval in a CSV file, and also keeps the data in aMapfor easier access. This temporary storage is called a cache of the information in the file. -
Class
DBUserDataAccessObjectuses okhttp to use an API, working with JSON data. Your team might want to refer to this when you do your API work. This API is similar to the one from lab; you can read its documentation here. This is the DAO used initially in theapp.AppBuilderclass. -
Class
InMemoryDataAccessObjectdoesn't save the user data to any kind of file at all, and is intended to be used by unit tests.- It's also simple to write, which means that you can start programming your Use Cases before you even have the details of data persistence worked out!
You can change which DAO is being used by modifying the
app.AppBuildercode.
The following two exercises are additional ways to confirm your understanding of the design of the code.
- Set a breakpoint inside one of the action listeners for a button, like the "sign up" or "log in" buttons.
- Run the program in debug mode and perform the interaction.
- Step through the code to trace the flow of information through the program.
- Ensure you follow every step of the execution; it may be helpful to refer to a copy of the CA Engine diagram as you do this.
- Set a breakpoint at the very first line of the body of the
Main.mainmethod. - Run the program in debug mode.
- Step through the code to trace how the
AppBuilderis used to build the whole program, with the various Views and associated Use Case Interactors getting pieced together. - Ensure you follow every step of the execution; it may be helpful to refer to a copy of the CA Engine diagram as you do this.