Gang Of Four has done an amazing job of summarising and identifying common challenges that business has faced in the past. The evolution of application design has brought their work into a new context, much like the improvements to Java that have been added to the platform in recent years. Such progress leads to the conclusion that design patterns and anti-patterns need to be reconsidered. This presentation reveals how to increase delivery flow and improve the fast-feedback loop while identifying bottlenecks and removing obstacles from the codebase. During the presentation, we will uncover the nature of several anti-patterns and smoothly translate them into design patterns as required by everyday business. Together, we explore similar approaches provide by another JVM languages like Kotlin or Scala to reveal the power and simplicity of Java. This helps increase productivity while improving the quality of daily decisions supported by proper visualisation from Java Flight Recorder
2. Safe Harbour Statement
All what you will hear can be di
ff
erent, this presentation is for
motivational purposes …
3. Miroslav Wengner
• Husband, Father, Software Engineer, Author, Blogger, Technology Enthusiast
• Book Author: Practical Design Patterns for Java Developers [Packt]
• JCP: Executive Committee Board Member, OpenJDK Committer
• Java Mission Control Project, Open-Source projects
• Co-Author of Robo4J Project (Duke Award)
• Java Champion, JavaOne RockStar
• Principal Engineer at OpenValue
4. • Group Of Talented and Motivated Individuals
• Netherlands, Germany, Switzerland, Austria…
• Fun, Development, Trainings, Migration and
more…
5. Agenda
• [study] cost of the bad code
• [engineering] pyramid of project healthiness
• [power features] gang of 21
• [anti-patterns] let’s get serious
• [anti-patterns, platform] squeezing resources and proper tools
• [design-patterns] 2 cents make di
ff
• [design-patterns] creational, structural, behavioral and concurrency
• Conclusion
• Q/A
6. [study] cost of the bad code
• CNBC Global CFO Council about [2018, Stripe and Harris Poll]
• Company lose upward $300 billion/year
• technical debts
• maintaining legacy
• impacts of bad code
• 57% of the week productive time ?
?
Average Working Week
10%
34%
57%
Work time
Tech debt
Bad code
7. [engineering] pyramid of focus healthiness
information span
consequence
of
poor-solution
innovations
requirements stream
tooling awareness
domain knowledge
< DISCIPLINE >
pyramid of project healthiness
FOCUS
ARCHITECTURE
CODE
PLATFORM
8. [power features] gang of 21
• innovation through stability and compatibility “guarantees”
• Language syntax
• Local-Variable type inference (323)
• Record Patterns (440), Sealed classes (409)
• Switch expression and pattern matching (441)
• Text Blocks (378)
• Internal APIs
• Sequenced Collection (431)
• Virtual Threads and Executors(444) ?
<= Module system (200, 261)
9. [power features] gang of 21
sealed interface Val permits Ch {};
record Ch(Integer v) implements Val {
String toText() {
var number = v;
return switch (number) {
case Integer i
when i > 74 -> {yield new String("".getBytes(), Charset.defaultCharset());}
case null -> “”;
default -> Character.toString(number);
};}}
Executors.newVirtualThreadPerTaskExecutor().submit(() -> System.out.println("""
%sON%d""".formatted(IntStream.of(74, 67, 80)
.mapToObj(Ch::new).map(Ch::toText).collect(Collectors.joining()),
ThreadLocalRandom.current().nextInt(2023, 2024))));
?
10. [anti-patterns] let’s get serious
keep it simple
• works … for me, now
• FITYMI
• innovation through bottlenecks?
• we
fi
x it later … so when … ?
• test free …
?
• limits an ability to e
ff
ectively address the issue: bottleneck
11. [anti-patterns, requirements] originated by code
Cut and paste programming
Spaghetti code
Blob
Lava
fl
ow
Functional decomposition
Boat anchor
expecting next challenge from outdated
God Class
PoC to Production
past is knocking
keep it single
12. [anti-patterns, domain]: originated by architecture
?
Golden hammer
Continuous obsolescence
Input kludge
Working in mine
fi
eld
Ambiguous viewpoint
Poltergeists
Dead end
believe and not verify
Test? appears like magic …
don’t remove
old good approach
onion
don't touch
13. [anti-patterns, platform] squeezing resources
MODEL:
record Sensor(int value) {}
LOGIC:
Collection<Sensor> set = provider.values();
for (Sensor e : set) {
SensorAlarmSystemUtil.evaluateAlarm(provider, e.value(), measurementCount);
}
UTILS:
void evaluateAlarm(Map<Integer, Sensor> storage, Integer criticalValue, long measurementNumber) {
if (!storage.containsKey(criticalValue)) {…}
“Der liebe Gott steckt im Detail”
Aby Warburg
15. [anti-patterns, platform] squeezing resources
?
record Sensor(Integer value) {}
Collection<Sensor> set = provider.values();
for (Sensor e : set) {
SensorAlarmSystemUtil.evaluateAlarm(provider, e.value(), measurementCount);
}
void evaluateAlarm(Map<Integer, Sensor> storage, Integer criticalValue, long measurementNumber) {
if (!storage.containsKey(criticalValue)) {
System.out.println(“SHOULD NOT HAPPEN!”);
}
FEEL DIFFERENCE ?
16. [anti-patterns, platform] proper tools
?
MODEL:
for (int i = 0; i < THREADS_NUMBER; i++) {
builder.addRunnable(
new VehicleAnalyticalWorker(i,ELEMENTS_NUMBER, new ArrayList<>(), new ArrayList<>())
);
}
LOGIC:
public int countIntersections(VehicleDataContainer c) {
int count = 0;
for (int n : collection) {
if (c.getCollection().contains(n)) {
count++;
}
}
return count;
}
17. [anti-patterns, platform] proper tools
Internal JDK APIs, java.base
• Impact:
• produce less code
• e
ff
ective behaviour
• optimisation under the hood
• selecting the right tool
?
18. [anti-patterns, platform] proper tools
?
MODEL:
for (int i = 0; i < THREADS_NUMBER; i++) {
builder.addRunnable(
new VehicleAnalyticalWorker(i, ELEMENTS_NUMBER, new HashSet<>(), new HashSet<>())
);
}
FEEL DIFFERENCE ?
Anti-pattern known as a CODE SMELL
LOAD DISTRIBUTION
24. [design-patterns] creational - java
?
Builder
class Car implements Vehicle {
final static class Builder {
private Part cabin = Part.PART_EMPTY;
private Part engine = Part.PART_EMPTY;
public Builder addCabin(String name) {
this.cabin = new Part(name);
return this;
}
separate the composition from the representation
record Part(String name) {
static final Part PART_EMPTY = new Part("");
}
25. [design-patterns] creational - scala
?
Builder
class Car(val name: String = "no_name",
val cabin: Part = EMPTY_PART,
val engine: Part = EMPTY_PART) extends Vehicle {
override def showParts(): Seq[Part] = Seq(cabin, engine)
}
object Car {
case class Builder private(cabin: Part = EMPTY_PART, engine: Part = EMPTY_PART) {
def withEngine(name: String): Builder = {
val part = Part(name)
copy(engine = part)
}
separate the composition from the representation
object Part {
val EMPTY_PART = new Part("")
}
case class Part(name: String = ""){ }
26. [design-patterns] creational - kotlin
?
Builder
class Car(
private val name: String,
private val cabin: Part = Part.PART_EMPTY,
private val engine: Part = Part.PART_EMPTY
) : Vehicle {
data class Builder(
var name: String = "no_name",
var cabin: Part = Part.PART_EMPTY,
var engine: Part = Part.PART_EMPTY
) {
fun withName(name: String) = apply {
this.name = name
}
separate the composition from the representation
class Part constructor(val name: String){
companion object {
val PART_EMPTY = Part("")
}
override fun toString(): String {
return "Part(${name})"
}
}
27. [design-patterns] creational - java 21
?
Factory Method
static Vehicle produce(Specs specs) {
return switch (specs) {
case CarSpecs s when s.specType().startsWith("sport") -> {
var car = new Car("sport_car");
System.out.println("factory, car, more action");
yield car;
}
case TruckSpecs ts -> new Truck("magic_truck");
default -> throw new IllegalArgumentException("""
not supported specs:'%s'""".formatted(specs));
};
}
centralise the class instantiation
28. [design-patterns] creational - kotlin
?
Factory Method
fun produce(specs: Specs): Vehicle {
return when (specs) {
is CarSpecs -> {
if (specs.specType.startsWith(“sport")) {
val car = Car("sport_car")
println("car:${car}")
return car
} else {
throw IllegalArgumentException("not supported car:’${specs.specType}'")
}
}
is TruckSpecs -> Truck("magic_truck")
else -> throw IllegalArgumentException("not supported specs:'$specs'")
}
}
centralise the class instantiation
29. [design-patterns] creational - scala
?
Factory Method
def produce(specs: Specs): Vehicle = specs match {
case carSpec: CarSpecs if carSpec.specType.startsWith("sport") => {
val car = new Car(name = "sport_car")
print("factory, car, more action")
car
}
case truckSpec: TruckSpecs => new Truck("magic_truck")
case _ => throw new IllegalArgumentException(s"not supported specs:'$specs'")
}
centralise the class instantiation
31. [design-patterns] structural
?
Adapter
sealed interface VehicleCommand permits StartCommand, StopCommand {..}
record StartCommand(…) implements VehicleCommand {
…
@Override
public void process(String command) {
if(command.contains("start")){
switch (vehicle){
case StandardVehicle v when v.getLoadWeight() > 1 ->
System.out.printf("""
%s with description:’%s’… v, v.getDescription(), v.getLoadWeight());
case StandardVehicle v -> System.out.printf("""
%s with description:'%s'%n""", v, v.getDescription());
case SportVehicle v -> System.out.println(v);
default -> System.out.println("NOTHING");
}
executed.set(true);
allows to work classes together
32. [design-patterns] structural
?
Facade
sealed interface VehicleCommand permits StartCommand, StopCommand {..}
uni
fi
ed interfaces to underlaying subsystems
public interface VehicleFactory<T> {
Vehicle produce(T type);
}
34. [design-patterns] behavioral
?
Command like action, event on what client act
public record StartCommand(…) implements VehicleCommand {…
public final class StopCommand implements VehicleCommand {
private final Vehicle vehicle;
private boolean executed;
public StopCommand(Vehicle vehicle){
this.vehicle = vehicle;
}
@Override
public void process(String command) {
…
35. [design-patterns] behavioral
?
Caching
Observer
public class Driver {
private final List<VehicleCommand> commands = new ArrayList<>();
public Driver addCommand(VehicleCommand command){…}
public void executeCommands(final String command){
commands.forEach(c -> {
// Records decomposition
if(c instanceof StartCommand(Vehicle v, AtomicBoolean state)){
….
}
public void printCommands() {
System.out.printf("""
Considered COMMANDS:%n%s""", commands.stream()
.map(VehicleCommand::status)
.collect(Collectors.joining("")));
react on demand reuse knowledge
36. [design-patterns] concurrency is inevitable
?
Active Object Asynchronous Method
Balking Double Checked Locking
Read Write Lock Producer consumer
Scheduler Thread Pool
CONCURRENCY IS HARD <3
37. [design-patterns] concurrency
?
Balking
var vehicle = new Vehicle();
var numberOfDrivers = 5;
var executors = Executors.newFixedThreadPool(2);
for (int i = 0; i < numberOfDrivers; i++) {
executors.submit(vehicle::drive);
}
an access to critical section
enum VehicleState {
MOVING,
STOPPED,
}
38. [design-patterns] concurrency
?
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
var sensor = new Sensor(readWriteLock.readLock(), readWriteLock.writeLock());
var sensorWriter = new SensorWriter("writer-1", sensor);
var writerThread = getWriterThread(sensorWriter);
ExecutorService executor = Executors.newFixedThreadPool(NUMBER_READERS);
var readers = IntStream.range(0, NUMBER_READERS)
.boxed().map(i -> new SensorReader("reader-" + i, sensor, CYCLES_READER)).toList();
readers.forEach(executor::submit);
Read Write Lock natural exclusivity for lock acquisition
39. Conclusion
• Code Discipline Engineering:
• pyramid of focus healthiness
• enforce iterative process by following EIPA and staying SOLID
• Design-Patterns: improve maintainability and reduce verbosity
• Don’t Repeat Yourself
• Maintainable naming standards
• Costs “reduction”
?
40. Q / A
twitter: @miragemiko
github:@mirage22
email: miro@openvalue.de
Thank YOU !