Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b35494a

Browse files
committedApr 18, 2024
docs: extension objects
1 parent 59d3e52 commit b35494a

File tree

8 files changed

+65
-38
lines changed

8 files changed

+65
-38
lines changed
 

‎extension-objects/README.md

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
---
2-
title: Extension objects
3-
category: Behavioral
2+
title: Extension Objects
3+
category: Structural
44
language: en
55
tag:
6-
- Extensibility
6+
- Encapsulation
7+
- Extensibility
8+
- Object composition
9+
- Polymorphism
710
---
811

9-
# Extention Objects Pattern
12+
## Also known as
13+
14+
* Interface Extensions
1015

1116
## Intent
12-
Anticipate that an object’s interface needs to be extended in the future. Additional
13-
interfaces are defined by extension objects.
17+
18+
The Extension Objects pattern allows for the flexible extension of an object's behavior without modifying its structure, by attaching additional objects that can dynamically add new functionality.
1419

1520
## Explanation
21+
1622
Real-world example
1723

1824
> Suppose you are developing a Java-based game for a client, and in the middle of the development process, new features are suggested. The Extension Objects pattern empowers your program to adapt to unforeseen changes with minimal refactoring, especially when integrating additional functionalities into your project.
@@ -27,10 +33,10 @@ Wikipedia says
2733
2834
**Programmatic example**
2935

30-
The aim of utilising the Extension Objects pattern is to implement new features/functionality without having to refactor every class.
31-
The following examples shows utilising this pattern for an Enemy class extending Entity within a game:
36+
The aim of utilising the Extension Objects pattern is to implement new features/functionality without having to refactor every class. The following examples shows utilising this pattern for an Enemy class extending Entity within a game:
3237

3338
Primary App class to execute our program from.
39+
3440
```java
3541
public class App {
3642
public static void main(String[] args) {
@@ -50,7 +56,9 @@ public class App {
5056
}
5157
}
5258
```
59+
5360
Enemy class with initial actions and extensions.
61+
5462
```java
5563
class Enemy extends Entity {
5664
public Enemy(String name) {
@@ -72,7 +80,9 @@ class Enemy extends Entity {
7280
}
7381
}
7482
```
83+
7584
EnemyExtension class with overriding extendAction() method.
85+
7686
```java
7787
class EnemyExtension implements EntityExtension {
7888
@Override
@@ -81,7 +91,9 @@ class EnemyExtension implements EntityExtension {
8191
}
8292
}
8393
```
94+
8495
Entity class which will be extended by Enemy.
96+
8597
```java
8698
class Entity {
8799
private String name;
@@ -105,32 +117,62 @@ class Entity {
105117
}
106118
}
107119
```
120+
108121
EntityExtension interface to be used by EnemyExtension.
122+
109123
```java
110124
interface EntityExtension {
111125
void extendedAction();
112126
}
113127
```
128+
114129
Program output:
130+
115131
```markdown
116132
Enemy performs the initial action.
117133
Enemy wants to attack you.
118134
Enemy has advanced towards you!
119135
```
120-
In this example, the Extension Objects pattern allows the enemy entity to perform unique initial actions and advanced actions when specific extensions are applied. This pattern provides flexibility and extensibility to the codebase while minimizing the need for major code changes.
121136

137+
In this example, the Extension Objects pattern allows the enemy entity to perform unique initial actions and advanced actions when specific extensions are applied. This pattern provides flexibility and extensibility to the codebase while minimizing the need for major code changes.
122138

123139
## Class diagram
140+
124141
![Extension_objects](./etc/extension_obj.png "Extension objects")
125142

126143
## Applicability
127-
Use the Extension Objects pattern when:
128144

129-
* you need to support the addition of new or unforeseen interfaces to existing classes and you don't want to impact clients that don't need this new interface. Extension Objects lets you keep related operations together by defining them in a separate class
130-
* a class representing a key abstraction plays different roles for different clients. The number of roles the class can play should be open-ended. There is a need to preserve the key abstraction itself. For example, a customer object is still a customer object even if different subsystems view it differently.
131-
* a class should be extensible with new behavior without subclassing from it.
145+
This pattern is applicable in scenarios where an object's functionality needs to be extended at runtime, avoiding the complications of subclassing. It's particularly useful in systems where object capabilities need to be augmented post-deployment, or where the capabilities might vary significantly across instances.
132146

133-
## Real world examples
147+
## Known Uses
134148

149+
* Extending services in an application server without altering existing code.
150+
* Plugins in IDEs like IntelliJ IDEA or Eclipse to add features to the base application.
151+
* Enabling additional features in enterprise software based on license levels.
135152
* [OpenDoc](https://en.wikipedia.org/wiki/OpenDoc)
136153
* [Object Linking and Embedding](https://en.wikipedia.org/wiki/Object_Linking_and_Embedding)
154+
155+
## Consequences
156+
157+
Benefits:
158+
159+
* Enhances flexibility by allowing dynamic extension of an object's capabilities.
160+
* Promotes loose coupling between the base object and its extensions.
161+
* Supports the [Open/Closed Principle](https://java-design-patterns.com/principles/#open-closed-principle) by keeping the object open for extension but closed for modification.
162+
163+
Trade-offs:
164+
165+
* Can increase complexity due to the management of extension objects.
166+
* May introduce performance overhead if the interaction between objects and extensions is not efficiently designed.
167+
168+
## Related Patterns
169+
170+
* [Decorator](https://java-design-patterns.com/patterns/decorator/): Similar in intent to add responsibilities dynamically, but uses a different structure.
171+
* [Composite](https://java-design-patterns.com/patterns/composite/): Also manages a group of objects, which can be seen as a form of extension.
172+
* [Strategy](https://java-design-patterns.com/patterns/strategy/): Offers an alternative way to change the behavior of an object dynamically.
173+
174+
## Credits
175+
176+
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/4aBMuuL)
177+
* [Pattern-Oriented Software Architecture: A System of Patterns](https://amzn.to/3Q9YOtX)
178+
* [Patterns of Enterprise Application Architecture](https://amzn.to/3W6IZYQ)

‎extension-objects/src/main/java/concreteextensions/Commander.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,8 @@
3333
/**
3434
* Class defining Commander.
3535
*/
36-
@Getter
37-
@RequiredArgsConstructor
3836
@Slf4j
39-
public class Commander implements CommanderExtension {
40-
41-
private final CommanderUnit unit;
37+
public record Commander(CommanderUnit unit) implements CommanderExtension {
4238

4339
@Override
4440
public void commanderReady() {

‎extension-objects/src/main/java/concreteextensions/Sergeant.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,8 @@
3333
/**
3434
* Class defining Sergeant.
3535
*/
36-
@Getter
37-
@RequiredArgsConstructor
3836
@Slf4j
39-
public class Sergeant implements SergeantExtension {
40-
41-
private final SergeantUnit unit;
37+
public record Sergeant(SergeantUnit unit) implements SergeantExtension {
4238

4339
@Override
4440
public void sergeantReady() {

‎extension-objects/src/main/java/concreteextensions/Soldier.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,8 @@
3333
/**
3434
* Class defining Soldier.
3535
*/
36-
@Getter
37-
@RequiredArgsConstructor
3836
@Slf4j
39-
public class Soldier implements SoldierExtension {
40-
41-
private final SoldierUnit unit;
37+
public record Soldier(SoldierUnit unit) implements SoldierExtension {
4238

4339
@Override
4440
public void soldierReady() {

‎extension-objects/src/test/java/concreteextensions/CommanderTest.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,7 @@
3636
import static org.junit.jupiter.api.Assertions.assertEquals;
3737

3838
/**
39-
* Created by Srdjan on 03-May-17.
40-
*
41-
* Modified by ToxicDreamz on 15-Aug-20
39+
* CommanderTest
4240
*/
4341
class CommanderTest {
4442

@@ -56,10 +54,9 @@ void shouldExecuteCommanderReady() {
5654
commander.commanderReady();
5755

5856
List<ILoggingEvent> logsList = listAppender.list;
59-
assertEquals("[Commander] " + commander.getUnit().getName() + " is ready!", logsList.get(0)
57+
assertEquals("[Commander] " + commander.unit().getName() + " is ready!", logsList.get(0)
6058
.getMessage());
6159
assertEquals(Level.INFO, logsList.get(0)
6260
.getLevel());
6361
}
64-
65-
}
62+
}

‎extension-objects/src/test/java/concreteextensions/SergeantTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ void sergeantReady() {
5454
sergeant.sergeantReady();
5555

5656
List<ILoggingEvent> logsList = listAppender.list;
57-
assertEquals("[Sergeant] " + sergeant.getUnit().getName() + " is ready!", logsList.get(0)
57+
assertEquals("[Sergeant] " + sergeant.unit().getName() + " is ready!", logsList.get(0)
5858
.getMessage());
5959
assertEquals(Level.INFO, logsList.get(0)
6060
.getLevel());

‎extension-objects/src/test/java/concreteextensions/SoldierTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ void soldierReady() {
5555
soldier.soldierReady();
5656

5757
List<ILoggingEvent> logsList = listAppender.list;
58-
assertEquals("[Soldier] " + soldier.getUnit().getName() + " is ready!", logsList.get(0)
58+
assertEquals("[Soldier] " + soldier.unit().getName() + " is ready!", logsList.get(0)
5959
.getMessage());
6060
assertEquals(Level.INFO, logsList.get(0)
6161
.getLevel());

‎extension-objects/src/test/java/units/UnitTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
class UnitTest {
3636

3737
@Test
38-
void testConstGetSet() throws Exception {
38+
void testConstGetSet() {
3939
final var name = "testName";
4040
final var unit = new Unit(name);
4141
assertEquals(name, unit.getName());

0 commit comments

Comments
 (0)
Please sign in to comment.