Skip to content

Commit 024831a

Browse files
tranansoiluwatar
andauthored
docs: Explanation for Type-Object iluwatar#2266 (iluwatar#2581)
* Write real world example and in plain words for type-object pattern * Type-Object Programmatic Example --------- Co-authored-by: Ilkka Seppälä <[email protected]>
1 parent b71eea4 commit 024831a

File tree

1 file changed

+135
-3
lines changed

1 file changed

+135
-3
lines changed

typeobjectpattern/README.md

Lines changed: 135 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,142 @@ As explained in the book Game Programming Patterns by Robert Nystrom, type objec
1818

1919
> Allowing flexible creation of new “classes” by creating a single class, each instance of which represents a different type of object
2020
21-
## Real World Example
21+
## Explanation
22+
Real-world example
23+
> You are working on a game with many different breeds of monsters. Each monster breed has different values for the attributes, such as attack, health, intelligence, etc. You want to create new monster breeds, or modify the attributes of an existing breed, without needing to modify the code and recompiling the game.
24+
25+
In plain words
26+
> Define a type object class, and a typed object class. We give each type object instance a reference to a typed object, containing the information for that type.
27+
28+
**Programmatic example**
29+
30+
Suppose we are developing a game of Candy Crush. There are many different candy types, and we may want to edit or create new ones over time as we develop the game.
31+
32+
First, we have a type for the candies, with a field name, parent, points and Type.
33+
34+
```java
35+
@Getter(AccessLevel.PACKAGE)
36+
public class Candy {
37+
38+
enum Type {
39+
CRUSHABLE_CANDY,
40+
REWARD_FRUIT
41+
}
42+
43+
String name;
44+
Candy parent;
45+
String parentName;
46+
47+
@Setter
48+
private int points;
49+
private final Type type;
50+
51+
Candy(String name, String parentName, Type type, int points) {
52+
this.name = name;
53+
this.parent = null;
54+
this.type = type;
55+
this.points = points;
56+
this.parentName = parentName;
57+
}
58+
59+
}
60+
```
2261

23-
Let's consider a real-world example. Say, we are working on a game which has a hero and many monsters which are going to attack the hero. These monsters have certain attributes like attack, points etc. and come in different 'breeds' like zombie or ogres. The obvious answer is to have a base Monster class which has some fields and methods, which may be overriden by subclasses like the Zombie or Ogre class. But as we continue to build the game, there may be more and more breeds of monsters added and certain attributes may need to be changed in the existing monsters too. The OOP solution of inheriting from the base class would not be an efficient method in this case.
24-
Using the type-object pattern, instead of creating many classes inheriting from a base class, we have 1 class with a field which represents the 'type' of object. This makes the code cleaner and object instantiation also becomes as easy as parsing a json file with the object properties.
62+
The field data for candy types are stored in the JSON file ```candy.json```. New candies can be added just by appending it to this file.
63+
64+
```json
65+
{"candies" : [
66+
{
67+
"name" : "fruit",
68+
"parent" : "null",
69+
"type" : "rewardFruit",
70+
"points" : 20
71+
},
72+
{
73+
"name" : "candy",
74+
"parent" : "null",
75+
"type" : "crushableCandy",
76+
"points" : 10
77+
},
78+
{
79+
"name" : "cherry",
80+
"parent" : "fruit",
81+
"type" : "rewardFruit",
82+
"points" : 0
83+
},
84+
{
85+
"name" : "mango",
86+
"parent" : "fruit",
87+
"type" : "rewardFruit",
88+
"points" : 0
89+
},
90+
{
91+
"name" : "purple popsicle",
92+
"parent" : "candy",
93+
"type" : "crushableCandy",
94+
"points" : 0
95+
},
96+
{
97+
"name" : "green jellybean",
98+
"parent" : "candy",
99+
"type" : "crushableCandy",
100+
"points" : 0
101+
},
102+
{
103+
"name" : "orange gum",
104+
"parent" : "candy",
105+
"type" : "crushableCandy",
106+
"points" : 0
107+
}
108+
]
109+
}
110+
```
111+
112+
The JSON file is parsed, instanciating each Candy type, and storing it in a hashtable. The ```type``` field is matched with the ```Type``` enum defined in the Candy class.
113+
```java
114+
public class JsonParser {
115+
Hashtable<String, Candy> candies;
116+
117+
JsonParser() {
118+
this.candies = new Hashtable<>();
119+
}
120+
121+
void parse() throws JsonParseException {
122+
var is = this.getClass().getClassLoader().getResourceAsStream("candy.json");
123+
var reader = new InputStreamReader(is);
124+
var json = (JsonObject) com.google.gson.JsonParser.parseReader(reader);
125+
var array = (JsonArray) json.get("candies");
126+
for (var item : array) {
127+
var candy = (JsonObject) item;
128+
var name = candy.get("name").getAsString();
129+
var parentName = candy.get("parent").getAsString();
130+
var t = candy.get("type").getAsString();
131+
var type = Type.CRUSHABLE_CANDY;
132+
if (t.equals("rewardFruit")) {
133+
type = Type.REWARD_FRUIT;
134+
}
135+
var points = candy.get("points").getAsInt();
136+
var c = new Candy(name, parentName, type, points);
137+
this.candies.put(name, c);
138+
}
139+
setParentAndPoints();
140+
}
141+
142+
void setParentAndPoints() {
143+
for (var e = this.candies.keys(); e.hasMoreElements(); ) {
144+
var c = this.candies.get(e.nextElement());
145+
if (c.parentName == null) {
146+
c.parent = null;
147+
} else {
148+
c.parent = this.candies.get(c.parentName);
149+
}
150+
if (c.getPoints() == 0 && c.parent != null) {
151+
c.setPoints(c.parent.getPoints());
152+
}
153+
}
154+
}
155+
}
156+
```
25157

26158
## In Plain Words
27159

0 commit comments

Comments
 (0)