Skip to content

Commit 1a14fa4

Browse files
feature: Completed component design pattern implementation, testing and respective README (iluwatar#2153)
* update the explanation in README.md * iluwatar#556 update initial files * added README to component design pattern * Rearrange the file * Finalize the directory * Add test sample * Update the title for README.md * Update the title for README.md * Update the title for README.md * Update the title for README.md * Finish the component design pattern * Updated comments and docstrings for component DP, added basic tests for App and GameObject java classes. Slight modifications to pom.xml to reflect the test suite introduction. * updated comments/docstrings for all classes - wrote v1 of README.md for component design pattern. This still requires the class diagram sketch. * Update the UML and linked with the README.md * Update the README.md * Remove the additional update method and rearrange the file based on the CheckStyle plugin * Changed the structure based on the code smells feedback from PR * Documentation update - uml * Documentation update - grammar * Updated readme to reflect the use of the LOGGER instead of the system output prints. * Correct the constant name * Uses Lombok to remove getter/setter boilerplate * Rename the constant name * Branch out from master and finish all the review changes * Correct the CheckStyle warning Co-authored-by: Samman Palihapitiya Gamage <[email protected]> Co-authored-by: SammanPali <[email protected]>
1 parent 0a53b23 commit 1a14fa4

17 files changed

+636
-0
lines changed

component/README.md

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
---
2+
title: Component
3+
categories: Behavioral
4+
language: en
5+
tags:
6+
- Game programming
7+
- Domain
8+
---
9+
10+
## Intent
11+
12+
The component design pattern enables developers to decouple attributes of an objects. Essentially allowing a single
13+
component to be inheritable by multiple domains/objects without linking the objects to each other. In addition to this
14+
benefit, the component design pattern allows developer to write maintainable and comprehensible code which is less
15+
likely to result in monolithic classes.
16+
17+
![Intent](./etc/component.duplication.png "Component Design Pattern")
18+
19+
## Explanation
20+
21+
Real world example
22+
> Suppose your video game consists of a graphics component and a sound component. Including the methods and attributes of both of these features in a single java class can be problematic due to many reasons. Firstly, the graphics and sound code can create an extremely long java class which can be hard to maintain. Furthermore, graphics components may be written and implemented by a separate team as to the sound contents. If both parties work simultaneously on the same java class, this may cause conflicts and major delay. Using the component design pattern, the development team is able to create individual component classes for graphics and sound whilst providing the domain/object the reach to both of these attributes.
23+
24+
25+
In plain words
26+
> The component design pattern provides a single attribute to be accessible by numerous objects without requiring the
27+
> existence of a relationship between the objects themselves.
28+
29+
Key drawback
30+
> With the implementation of the component design pattern, it can be very difficult to create a relationship
31+
> between components. For example, suppose we require the sound component to be aware of the current animation in order
32+
> create a certain sound based upon the animation; this can be quite tricky as the component design pattern makes
33+
> components 'unaware' of other components' existence due to its decoupling nature.
34+
35+
**Programmatic Example**
36+
37+
The App class creates a demonstration of the use of the component pattern by creating two different objects which
38+
inherit a small collection of individual components that are modifiable.
39+
40+
```java
41+
public final class App {
42+
/**
43+
* Program entry point.
44+
*
45+
* @param args args command line args.
46+
*/
47+
public static void main(String[] args) {
48+
final var player = GameObject.createPlayer();
49+
final var npc = GameObject.createNpc();
50+
51+
52+
LOGGER.info("Player Update:");
53+
player.update(KeyEvent.KEY_LOCATION_LEFT);
54+
LOGGER.info("NPC Update:");
55+
npc.demoUpdate();
56+
}
57+
}
58+
```
59+
60+
Much of the program exists within the GameObject class, within this class, the player and NPC object create methods are
61+
set up. Additionally, this class also consists of the method calls used to update/alter information of the object's
62+
components.
63+
64+
```java
65+
public class GameObject {
66+
private final InputComponent inputComponent;
67+
private final PhysicComponent physicComponent;
68+
private final GraphicComponent graphicComponent;
69+
70+
public String name;
71+
public int velocity = 0;
72+
public int coordinate = 0;
73+
74+
public static GameObject createPlayer() {
75+
return new GameObject(new PlayerInputComponent(),
76+
new ObjectPhysicComponent(),
77+
new ObjectGraphicComponent(),
78+
"player");
79+
}
80+
81+
public static GameObject createNpc() {
82+
return new GameObject(
83+
new DemoInputComponent(),
84+
new ObjectPhysicComponent(),
85+
new ObjectGraphicComponent(),
86+
"npc");
87+
}
88+
89+
public void demoUpdate() {
90+
inputComponent.update(this);
91+
physicComponent.update(this);
92+
graphicComponent.update(this);
93+
}
94+
95+
public void update(int e) {
96+
inputComponent.update(this, e);
97+
physicComponent.update(this);
98+
graphicComponent.update(this);
99+
}
100+
101+
public void updateVelocity(int acceleration) {
102+
this.velocity += acceleration;
103+
}
104+
105+
public void updateCoordinate() {
106+
this.coordinate += this.velocity;
107+
}
108+
}
109+
```
110+
111+
Upon opening the component package, the collection of components are revealed. These components provide the interface
112+
for objects to inherit these domains. The PlayerInputComponent class shown below updates the object's velocity
113+
characteristic based on user's key event input.
114+
115+
```java
116+
public class PlayerInputComponent implements InputComponent {
117+
private static final int walkAcceleration = 1;
118+
119+
/**
120+
* The update method to change the velocity based on the input key event.
121+
*
122+
* @param gameObject the gameObject instance
123+
* @param e key event instance
124+
*/
125+
@Override
126+
public void update(GameObject gameObject, int e) {
127+
switch (e) {
128+
case KeyEvent.KEY_LOCATION_LEFT -> {
129+
gameObject.updateVelocity(-WALK_ACCELERATION);
130+
LOGGER.info(gameObject.getName() + " has moved left.");
131+
}
132+
case KeyEvent.KEY_LOCATION_RIGHT -> {
133+
gameObject.updateVelocity(WALK_ACCELERATION);
134+
LOGGER.info(gameObject.getName() + " has moved right.");
135+
}
136+
default -> {
137+
LOGGER.info(gameObject.getName() + "'s velocity is unchanged due to the invalid input");
138+
gameObject.updateVelocity(0);
139+
} // incorrect input
140+
}
141+
}
142+
}
143+
```
144+
145+
## Class diagram
146+
147+
![UML](./etc/component.uml.png "The UML for Component Design Pattern")
148+
149+
## Applicability
150+
151+
Use the component design pattern when
152+
153+
- you have a class which access multiple features which you would like to keep separate.
154+
- you want to reduce the length of a class.
155+
- you require a variety of objects to share a collection of components but the use of inheritance isn't specific enough.
156+
157+
## Credits
158+
159+
- [Component Design Pattern] (https://gameprogrammingpatterns.com/component.html)
160+
- [Component pattern - game programming series - Tutemic] (https://www.youtube.com/watch?v=n92GBp2WMkg&ab_channel=Tutemic)
295 KB
Loading

component/etc/component.uml.png

56.2 KB
Loading

component/etc/component.uml.puml

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
@startuml
2+
class App
3+
class GameObject
4+
5+
interface GraphicComponent
6+
interface InputComponent
7+
interface PhysicComponent
8+
9+
class ObjectGraphicComponent
10+
class DemoInputComponent
11+
class PlayerInputComponent
12+
class ObjectPhysicComponent
13+
14+
GraphicComponent <|.. ObjectGraphicComponent
15+
InputComponent <|.. DemoInputComponent
16+
InputComponent <|.. PlayerInputComponent
17+
PhysicComponent <|.. ObjectPhysicComponent
18+
19+
GameObject *-- ObjectGraphicComponent
20+
GameObject *.. DemoInputComponent
21+
GameObject *.. PlayerInputComponent
22+
GameObject *-- ObjectPhysicComponent
23+
class App {
24+
+main(String[] args)
25+
}
26+
27+
class GameObject{
28+
- inputComponent;
29+
- physicComponent;
30+
- graphicComponent;
31+
- name;
32+
- velocity
33+
- coordinate
34+
35+
+GameObject()
36+
+createPlayer()
37+
+createNpc()
38+
+demoUpdate()
39+
+update(e:int)
40+
+getName()
41+
+getVelocity()
42+
+setVelocity(acceleration:int)
43+
+getCoordinate()
44+
+setCoordinate()
45+
}
46+
47+
interface GraphicComponent{
48+
+update()
49+
}
50+
51+
interface InputComponent{
52+
+update()
53+
}
54+
55+
interface PhysicComponent{
56+
+update()
57+
}
58+
59+
class ObjectGraphicComponent{
60+
+update(gameObject:GameObject)
61+
}
62+
63+
class DemoInputComponent{
64+
-walkAcceleration
65+
+update(gameObject:GameObject,e:int)
66+
}
67+
68+
class PlayerInputComponent{
69+
-walkAcceleration
70+
+update(gameObject:GameObject,e:int)
71+
}
72+
73+
class ObjectPhysicComponent{
74+
+update(gameObject:GameObject)
75+
}
76+
77+
@enduml

component/pom.xml

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>java-design-patterns</artifactId>
7+
<groupId>com.iluwatar</groupId>
8+
<version>1.26.0-SNAPSHOT</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>component</artifactId>
13+
<dependencies>
14+
<dependency>
15+
<groupId>org.junit.jupiter</groupId>
16+
<artifactId>junit-jupiter-api</artifactId>
17+
<scope>test</scope>
18+
</dependency>
19+
</dependencies>
20+
<build>
21+
<plugins>
22+
<plugin>
23+
<groupId>org.apache.maven.plugins</groupId>
24+
<artifactId>maven-assembly-plugin</artifactId>
25+
<executions>
26+
<execution>
27+
<configuration>
28+
<archive>
29+
<manifest>
30+
<mainClass>com.iluwatar.component.App</mainClass>
31+
</manifest>
32+
</archive>
33+
</configuration>
34+
</execution>
35+
</executions>
36+
</plugin>
37+
</plugins>
38+
</build>
39+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.iluwatar.component;
2+
3+
import java.awt.event.KeyEvent;
4+
import lombok.extern.slf4j.Slf4j;
5+
6+
/**
7+
* The component design pattern is a common game design structure. This pattern is often
8+
* used to reduce duplication of code as well as to improve maintainability.
9+
* In this implementation, component design pattern has been used to provide two game
10+
* objects with varying component interfaces (features). As opposed to copying and
11+
* pasting same code for the two game objects, the component interfaces allow game
12+
* objects to inherit these components from the component classes.
13+
*
14+
* <p>The implementation has decoupled graphic, physics and input components from
15+
* the player and NPC objects. As a result, it avoids the creation of monolithic java classes.
16+
*
17+
* <p>The below example in this App class demonstrates the use of the component interfaces
18+
* for separate objects (player & NPC) and updating of these components as per the
19+
* implementations in GameObject class and the component classes.
20+
*/
21+
@Slf4j
22+
public final class App {
23+
/**
24+
* Program entry point.
25+
*
26+
* @param args args command line args.
27+
*/
28+
public static void main(String[] args) {
29+
final var player = GameObject.createPlayer();
30+
final var npc = GameObject.createNpc();
31+
32+
33+
LOGGER.info("Player Update:");
34+
player.update(KeyEvent.KEY_LOCATION_LEFT);
35+
LOGGER.info("NPC Update:");
36+
npc.demoUpdate();
37+
}
38+
}

0 commit comments

Comments
 (0)