Skip to content

Commit d109d43

Browse files
committed
Initial commit
0 parents  commit d109d43

36 files changed

+1226
-0
lines changed

.gitignore

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
### IntelliJ IDEA ###
2+
out/
3+
!**/src/main/**/out/
4+
!**/src/test/**/out/
5+
6+
### Eclipse ###
7+
.apt_generated
8+
.classpath
9+
.factorypath
10+
.project
11+
.settings
12+
.springBeans
13+
.sts4-cache
14+
bin/
15+
!**/src/main/**/bin/
16+
!**/src/test/**/bin/
17+
18+
### NetBeans ###
19+
/nbproject/private/
20+
/nbbuild/
21+
/dist/
22+
/nbdist/
23+
/.nb-gradle/
24+
25+
### VS Code ###
26+
.vscode/
27+
28+
### Mac OS ###
29+
.DS_Store!/users.csv
30+
/users.csv

.idea/.gitignore

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/checkstyle-idea.xml

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules.xml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CACoding.iml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<module type="JAVA_MODULE" version="4">
3+
<component name="NewModuleRootManager" inherit-compiler-output="true">
4+
<exclude-output />
5+
<content url="file://$MODULE_DIR$">
6+
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
7+
</content>
8+
<orderEntry type="inheritedJdk" />
9+
<orderEntry type="sourceFolder" forTests="false" />
10+
</component>
11+
<component name="SonarLintModuleSettings">
12+
<option name="uniqueId" value="ce2f91cc-4cf6-4286-a14e-4c3d4f923b96" />
13+
</component>
14+
</module>

README.md

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# Clean Architecture
2+
3+
# Preamble
4+
5+
This is an example of a Clean Architecture implementation.
6+
7+
We keep showing you the Clean Architecture picture, the green, pink, yellow, and
8+
white picture with all the boxes and arrow. We will call this image the _"CA
9+
Engine"_.
10+
11+
Pro tip: as you read this document, you'll want to look at the CA Engine a lot.
12+
Pull it up on your phone if you don't have an external monitor. Or arrange your
13+
laptop windows side by side. Or find the Engine in the textbook, if you have
14+
a hard copy.
15+
16+
## The Java implementation
17+
18+
These packages correspond to the various areas of the program:
19+
20+
* `data_access`
21+
* `entity`
22+
* `interface_adapter`
23+
* `use_case`
24+
* `view`
25+
26+
There is one more package, `app`, that contains classes whose job it is to build
27+
the CA Engine and start it running.
28+
29+
In most cases, each class is named according to its role in the CA Engine.
30+
31+
### Entities
32+
33+
These objects represent the fundamental data for the application. The classes
34+
are named after concepts from the problem domain, like "bank account", "personal
35+
profile", and "game board".
36+
37+
Most entities have a corresponding factory class. These classes manufacture
38+
entities. See the Factory Pattern. The factories are mostly used in the data
39+
access layer.
40+
41+
### Data Access
42+
43+
These Data Access Objects (DAO) store and retrieve entity data using files or a
44+
database. They mostly do four things: _create_, _read_, _update_, and _delete_
45+
(CRUD).
46+
47+
Often, there is one DAO object per entity, and that DAO reads and writes a
48+
single file where each row contains information about one entity.
49+
50+
DAOs have a method that returns an entity object. There is often a map of keys
51+
to entities, where the key is some kind of id. When the main method creates a
52+
DAO, it injects any necessary entity factories.
53+
54+
### Use Case Interactors
55+
56+
Use Case Interactor objects each do a single (possibly complicated) thing: given
57+
data supplied by the user, do what the user wants with that data, then gather
58+
any resulting data that the user wants to look at. That might be the result of a
59+
bank transaction, updated data on your profile, or a move on a game board.
60+
61+
The data supplied by the user is gathered by the `Controller`, which tells the
62+
`UseCaseInteractor` to do its job. The `Controller` passes in `InputData`, which
63+
comes from the user through the `View`.
64+
65+
The state of the entities will often change as a result of a use case
66+
interaction: some may be created, some may be deleted, and some may be mutated.
67+
`UseCaseInteractor`s use DAOs to get entities. When the main program creates an
68+
interactor, it injects any necessary DAOs.
69+
70+
If the `UseCaseInteractor` needs to look up data -- perhaps fetch a bank account
71+
balance -- then it will call a method in the appropriate DAO, which will look
72+
up the required data and return an Entity containing it.
73+
74+
When the use case interaction is complete, the `UseCaseInteractor` will create
75+
an `OutputData` object containing any new information that should be represented
76+
in the `ViewModel`, and tells its `Presenter` object to update its `ViewModel`.
77+
78+
When the main program instantiates a `UseCaseInteractor`, it injects a
79+
`Presenter`.
80+
81+
### View
82+
83+
Classes in the `view` package manage the user interface. `View` classes describe
84+
different screens of the application. The `ViewManager`'s job is to swap which
85+
screen is showing.
86+
87+
Most `View` objects have a corresponding `ViewModel` object in the
88+
`interface_adaptor` package. Each `View` will listen to its `ViewModel`, and
89+
react when it hears that there have been changes.
90+
91+
When the main program creates a `View`, it injects the `ViewModel`.
92+
93+
When the user performs an action, perhaps clicking a button, it causes a call on
94+
an _action method_. When you create a button, you need to tell it to call an
95+
`actionPerformed` method when it's clicked. Each button has its own
96+
`actionPerformed` method. There are similar action methods for keystrokes and so on.
97+
98+
Each `actionPerformed` method calls a method in a `Controller` object to trigger
99+
a use case interaction. When the main program builds the `View`, it injects any
100+
necessary `Controller`s.
101+
102+
When an action method calls its `Controller` method, it passes in data the user
103+
entered --- usually numbers and text, from text fields and so on.
104+
105+
### Interface Adapters
106+
107+
The `ViewModel` objects contain all data that is shown to the user. There is
108+
usually one `ViewModel` per `View`, and one for the `ViewManager`. These view
109+
managers know the objects that are listening to them, and tell them when the
110+
data changes. (When we say "tell", we mean "call a method".)
111+
112+
There is typically one `Controller`, one `Presenter`, and one
113+
`UseCaseInteractor` per action method.
114+
115+
`Controller` objects are given raw data from the `View`, and their job is to
116+
make the data useful for a use case interaction. They might receive
117+
`"26/04/2024"` as a `String` and instantiate a `LocalDateTime` object. Or they
118+
might receive two integers, `42` and `55`, and create a `Currency` object
119+
representing $42.55. ªOften, a `String` or number needs no such conversion, and
120+
is left as-is.)
121+
122+
When the `Controller` has converted all the data, it put it into an Input Data
123+
object that contains the information needed to execute the use case. The
124+
`Controller` calls a method in the `UseCaseInteractor` to execute the use case
125+
interaction.
126+
127+
`Controller`s and `Presenter`s never use entities.
128+
129+
When a `Controller` is instantiated, the main program injects a
130+
`UseCaseInteractor` object.
131+
132+
A `Presenter`'s job is to update its `ViewModel`, which will tell the `View`
133+
that there has been an update.
134+
135+
## A note about dependencies
136+
137+
The `UseCaseInteractor` is the heart of your program. Everything else exists to
138+
support it. If you decide to switch from using plain-text files to using a
139+
database, _the `UseCaseInteractor` should not change at all_.
140+
141+
To accomplish this, the `UseCaseInteractor` publishes a `DataAccessInterface`
142+
specifying the operations it needs to save and retrieve data. The DAO class
143+
implements this interface.
144+
145+
Remember that the main program injects the `DAO` into the `UseCaseInteractor`.
146+
To change how you're persisting data, you would write a new `DAO` class that also
147+
implements the `DataAccessInterface`, and then change the main program so that
148+
it injects that `DAO` instead.
149+
150+
That's also why the `OutputBoundary` exists: if you want to change the user
151+
interface (from, say, Java Swing to a web application), you would need to write
152+
all new `View`s and perhaps new `ViewModel`s and `Controller`s and `Presenter`s.
153+
154+
The `InputBoundary` exists to make it clear how a `Controller` should use the
155+
`UseCaseInteractor`.

src/app/Main.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package app;
2+
3+
import interface_adapter.LoginViewModel;
4+
import interface_adapter.SignupViewModel;
5+
import interface_adapter.ViewManagerModel;
6+
import view.LoginView;
7+
import view.SignupView;
8+
import view.ViewManager;
9+
10+
import javax.swing.*;
11+
import java.awt.*;
12+
13+
public class Main {
14+
public static void main(String[] args) {
15+
// Build the main program window, the main panel containing the
16+
// various cards, and the layout, and stitch them together.
17+
18+
// The main application window.
19+
JFrame application = new JFrame("Login Example");
20+
application.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
21+
22+
CardLayout cardLayout = new CardLayout();
23+
24+
// The various View objects. Only one view is visible at a time.
25+
JPanel views = new JPanel(cardLayout);
26+
application.add(views);
27+
28+
// This keeps track of and manages which view is currently showing.
29+
ViewManagerModel viewManagerModel = new ViewManagerModel();
30+
new ViewManager(views, cardLayout, viewManagerModel);
31+
32+
// The data for the views, such as username and password, are in the ViewModels.
33+
// This information will be changed by a presenter object that is reporting the
34+
// results from the use case. The ViewModels are observable, and will
35+
// be observed by the Views.
36+
LoginViewModel loginViewModel = new LoginViewModel();
37+
SignupViewModel signupViewModel = new SignupViewModel();
38+
39+
SignupView signupView = SignupUseCaseFactory.create(viewManagerModel, loginViewModel, signupViewModel);
40+
views.add(signupView, signupView.viewName);
41+
42+
LoginView loginView = new LoginView(loginViewModel);
43+
views.add(loginView, loginView.viewName);
44+
45+
viewManagerModel.setActiveView(signupView.viewName);
46+
viewManagerModel.firePropertyChanged();
47+
48+
application.pack();
49+
application.setVisible(true);
50+
}
51+
}

src/app/SignupUseCaseFactory.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package app;
2+
3+
import data_access.FileUserDataAccessObject;
4+
import data_access.UserSignupDataAccessInterface;
5+
import entity.CommonUserFactory;
6+
import entity.UserFactory;
7+
import interface_adapter.*;
8+
import use_case.SignupInputBoundary;
9+
import use_case.SignupInteractor;
10+
import use_case.SignupOutputBoundary;
11+
import view.SignupView;
12+
13+
import javax.swing.*;
14+
import java.io.IOException;
15+
16+
public class SignupUseCaseFactory {
17+
18+
/** Prevent instantiation. */
19+
private SignupUseCaseFactory() {}
20+
21+
public static SignupView create(ViewManagerModel viewManagerModel, LoginViewModel loginViewModel, SignupViewModel signupViewModel) {
22+
23+
try {
24+
SignupController signupController = createUserSignupUseCase(viewManagerModel, signupViewModel, loginViewModel);
25+
return new SignupView(signupController, signupViewModel);
26+
} catch (IOException e) {
27+
JOptionPane.showMessageDialog(null, "Could not open user data file.");
28+
}
29+
30+
return null;
31+
}
32+
33+
private static SignupController createUserSignupUseCase(ViewManagerModel viewManagerModel, SignupViewModel signupViewModel, LoginViewModel loginViewModel) throws IOException {
34+
UserSignupDataAccessInterface userDataAccessObject = new FileUserDataAccessObject("./users.csv", new CommonUserFactory());
35+
36+
// Notice how we pass this method's parameters to the Presenter.
37+
SignupOutputBoundary signupOutputBoundary = new SignupPresenter(viewManagerModel, signupViewModel, loginViewModel);
38+
39+
UserFactory userFactory = new CommonUserFactory();
40+
41+
SignupInputBoundary userSignupInteractor = new SignupInteractor(
42+
userDataAccessObject, signupOutputBoundary, userFactory);
43+
44+
return new SignupController(userSignupInteractor);
45+
}
46+
}

0 commit comments

Comments
 (0)