devflow-kit 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/README.md +23 -6
- package/dist/plugins.js +67 -3
- package/package.json +2 -1
- package/plugins/devflow-accessibility/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-ambient/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-ambient/skills/ambient-router/SKILL.md +1 -1
- package/plugins/devflow-ambient/skills/ambient-router/references/skill-catalog.md +4 -0
- package/plugins/devflow-audit-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-code-review/.claude-plugin/plugin.json +1 -4
- package/plugins/devflow-code-review/agents/reviewer.md +8 -0
- package/plugins/devflow-code-review/commands/code-review-teams.md +11 -1
- package/plugins/devflow-code-review/commands/code-review.md +12 -2
- package/plugins/devflow-core-skills/.claude-plugin/plugin.json +2 -6
- package/plugins/devflow-debug/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-frontend-design/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-go/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-go/skills/go/SKILL.md +187 -0
- package/plugins/devflow-go/skills/go/references/concurrency.md +312 -0
- package/plugins/devflow-go/skills/go/references/detection.md +129 -0
- package/plugins/devflow-go/skills/go/references/patterns.md +232 -0
- package/plugins/devflow-go/skills/go/references/violations.md +205 -0
- package/plugins/devflow-implement/.claude-plugin/plugin.json +1 -3
- package/plugins/devflow-implement/agents/coder.md +11 -6
- package/plugins/devflow-java/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-java/skills/java/SKILL.md +183 -0
- package/plugins/devflow-java/skills/java/references/detection.md +120 -0
- package/plugins/devflow-java/skills/java/references/modern-java.md +270 -0
- package/plugins/devflow-java/skills/java/references/patterns.md +235 -0
- package/plugins/devflow-java/skills/java/references/violations.md +213 -0
- package/plugins/devflow-python/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-python/skills/python/SKILL.md +188 -0
- package/plugins/devflow-python/skills/python/references/async.md +220 -0
- package/plugins/devflow-python/skills/python/references/detection.md +128 -0
- package/plugins/devflow-python/skills/python/references/patterns.md +226 -0
- package/plugins/devflow-python/skills/python/references/violations.md +204 -0
- package/plugins/devflow-react/.claude-plugin/plugin.json +15 -0
- package/plugins/{devflow-core-skills → devflow-react}/skills/react/SKILL.md +1 -1
- package/plugins/{devflow-core-skills → devflow-react}/skills/react/references/patterns.md +3 -3
- package/plugins/devflow-resolve/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-rust/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-rust/skills/rust/SKILL.md +193 -0
- package/plugins/devflow-rust/skills/rust/references/detection.md +131 -0
- package/plugins/devflow-rust/skills/rust/references/ownership.md +242 -0
- package/plugins/devflow-rust/skills/rust/references/patterns.md +210 -0
- package/plugins/devflow-rust/skills/rust/references/violations.md +191 -0
- package/plugins/devflow-self-review/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-specify/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-typescript/.claude-plugin/plugin.json +15 -0
- package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/references/patterns.md +3 -3
- package/shared/agents/coder.md +11 -6
- package/shared/agents/reviewer.md +8 -0
- package/shared/skills/ambient-router/SKILL.md +1 -1
- package/shared/skills/ambient-router/references/skill-catalog.md +4 -0
- package/shared/skills/go/SKILL.md +187 -0
- package/shared/skills/go/references/concurrency.md +312 -0
- package/shared/skills/go/references/detection.md +129 -0
- package/shared/skills/go/references/patterns.md +232 -0
- package/shared/skills/go/references/violations.md +205 -0
- package/shared/skills/java/SKILL.md +183 -0
- package/shared/skills/java/references/detection.md +120 -0
- package/shared/skills/java/references/modern-java.md +270 -0
- package/shared/skills/java/references/patterns.md +235 -0
- package/shared/skills/java/references/violations.md +213 -0
- package/shared/skills/python/SKILL.md +188 -0
- package/shared/skills/python/references/async.md +220 -0
- package/shared/skills/python/references/detection.md +128 -0
- package/shared/skills/python/references/patterns.md +226 -0
- package/shared/skills/python/references/violations.md +204 -0
- package/shared/skills/react/SKILL.md +1 -1
- package/shared/skills/react/references/patterns.md +3 -3
- package/shared/skills/rust/SKILL.md +193 -0
- package/shared/skills/rust/references/detection.md +131 -0
- package/shared/skills/rust/references/ownership.md +242 -0
- package/shared/skills/rust/references/patterns.md +210 -0
- package/shared/skills/rust/references/violations.md +191 -0
- package/shared/skills/typescript/references/patterns.md +3 -3
- package/plugins/devflow-code-review/skills/react/SKILL.md +0 -276
- package/plugins/devflow-code-review/skills/react/references/patterns.md +0 -1331
- package/plugins/devflow-core-skills/skills/accessibility/SKILL.md +0 -229
- package/plugins/devflow-core-skills/skills/accessibility/references/detection.md +0 -171
- package/plugins/devflow-core-skills/skills/accessibility/references/patterns.md +0 -670
- package/plugins/devflow-core-skills/skills/accessibility/references/violations.md +0 -419
- package/plugins/devflow-core-skills/skills/frontend-design/SKILL.md +0 -254
- package/plugins/devflow-core-skills/skills/frontend-design/references/detection.md +0 -184
- package/plugins/devflow-core-skills/skills/frontend-design/references/patterns.md +0 -511
- package/plugins/devflow-core-skills/skills/frontend-design/references/violations.md +0 -453
- package/plugins/devflow-core-skills/skills/react/references/violations.md +0 -565
- package/plugins/devflow-implement/skills/accessibility/SKILL.md +0 -229
- package/plugins/devflow-implement/skills/accessibility/references/detection.md +0 -171
- package/plugins/devflow-implement/skills/accessibility/references/patterns.md +0 -670
- package/plugins/devflow-implement/skills/accessibility/references/violations.md +0 -419
- package/plugins/devflow-implement/skills/frontend-design/SKILL.md +0 -254
- package/plugins/devflow-implement/skills/frontend-design/references/detection.md +0 -184
- package/plugins/devflow-implement/skills/frontend-design/references/patterns.md +0 -511
- package/plugins/devflow-implement/skills/frontend-design/references/violations.md +0 -453
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/SKILL.md +0 -0
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/detection.md +0 -0
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/patterns.md +0 -0
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/violations.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/SKILL.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/detection.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/patterns.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/violations.md +0 -0
- /package/plugins/{devflow-code-review → devflow-react}/skills/react/references/violations.md +0 -0
- /package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/SKILL.md +0 -0
- /package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/references/violations.md +0 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# Modern Java Features (17-21+)
|
|
2
|
+
|
|
3
|
+
Deep-dive on modern Java language features. Reference from main SKILL.md.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Records (Java 16+)
|
|
8
|
+
|
|
9
|
+
### Basic Record
|
|
10
|
+
|
|
11
|
+
```java
|
|
12
|
+
// Immutable data carrier with auto-generated equals, hashCode, toString
|
|
13
|
+
public record Point(double x, double y) {}
|
|
14
|
+
|
|
15
|
+
// Usage
|
|
16
|
+
var p = new Point(3.0, 4.0);
|
|
17
|
+
double x = p.x(); // Accessor method, not getX()
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Compact Constructor (Validation)
|
|
21
|
+
|
|
22
|
+
```java
|
|
23
|
+
public record Email(String value) {
|
|
24
|
+
public Email {
|
|
25
|
+
Objects.requireNonNull(value, "email must not be null");
|
|
26
|
+
if (!value.contains("@")) {
|
|
27
|
+
throw new IllegalArgumentException("Invalid email: " + value);
|
|
28
|
+
}
|
|
29
|
+
value = value.toLowerCase().strip(); // Reassign before final assignment
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Record with Custom Methods
|
|
35
|
+
|
|
36
|
+
```java
|
|
37
|
+
public record Range(int start, int end) {
|
|
38
|
+
public Range {
|
|
39
|
+
if (start > end) throw new IllegalArgumentException("start must be <= end");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public int length() { return end - start; }
|
|
43
|
+
public boolean contains(int value) { return value >= start && value <= end; }
|
|
44
|
+
public Range overlap(Range other) {
|
|
45
|
+
int newStart = Math.max(start, other.start);
|
|
46
|
+
int newEnd = Math.min(end, other.end);
|
|
47
|
+
return newStart <= newEnd ? new Range(newStart, newEnd) : null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Records as Local Classes
|
|
53
|
+
|
|
54
|
+
```java
|
|
55
|
+
public List<String> processOrders(List<Order> orders) {
|
|
56
|
+
// Local record for intermediate computation
|
|
57
|
+
record OrderTotal(String orderId, Money total) {}
|
|
58
|
+
|
|
59
|
+
return orders.stream()
|
|
60
|
+
.map(o -> new OrderTotal(o.id(), o.calculateTotal()))
|
|
61
|
+
.filter(ot -> ot.total().isGreaterThan(Money.of(100)))
|
|
62
|
+
.map(OrderTotal::orderId)
|
|
63
|
+
.toList();
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Sealed Classes (Java 17+)
|
|
70
|
+
|
|
71
|
+
### Sealed Interface
|
|
72
|
+
|
|
73
|
+
```java
|
|
74
|
+
public sealed interface Shape permits Circle, Rectangle, Triangle {
|
|
75
|
+
double area();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public record Circle(double radius) implements Shape {
|
|
79
|
+
public double area() { return Math.PI * radius * radius; }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
public record Rectangle(double width, double height) implements Shape {
|
|
83
|
+
public double area() { return width * height; }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public record Triangle(double base, double height) implements Shape {
|
|
87
|
+
public double area() { return 0.5 * base * height; }
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Sealed Class with Abstract Methods
|
|
92
|
+
|
|
93
|
+
```java
|
|
94
|
+
public sealed abstract class Payment permits CreditCard, BankTransfer, Crypto {
|
|
95
|
+
abstract Money amount();
|
|
96
|
+
abstract String reference();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public final class CreditCard extends Payment {
|
|
100
|
+
private final String cardLast4;
|
|
101
|
+
private final Money amount;
|
|
102
|
+
// ...
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public final class BankTransfer extends Payment { /* ... */ }
|
|
106
|
+
public final class Crypto extends Payment { /* ... */ }
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Pattern Matching (Java 21+)
|
|
112
|
+
|
|
113
|
+
### Switch with Patterns
|
|
114
|
+
|
|
115
|
+
```java
|
|
116
|
+
// Exhaustive pattern matching on sealed types
|
|
117
|
+
public String describe(Shape shape) {
|
|
118
|
+
return switch (shape) {
|
|
119
|
+
case Circle c -> "Circle with radius %.2f".formatted(c.radius());
|
|
120
|
+
case Rectangle r -> "Rectangle %s x %s".formatted(r.width(), r.height());
|
|
121
|
+
case Triangle t -> "Triangle with base %.2f".formatted(t.base());
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Guarded Patterns
|
|
127
|
+
|
|
128
|
+
```java
|
|
129
|
+
public String classifyTemperature(Object obj) {
|
|
130
|
+
return switch (obj) {
|
|
131
|
+
case Integer i when i < 0 -> "Freezing";
|
|
132
|
+
case Integer i when i < 15 -> "Cold";
|
|
133
|
+
case Integer i when i < 25 -> "Comfortable";
|
|
134
|
+
case Integer i -> "Hot";
|
|
135
|
+
case Double d when d < 0.0 -> "Freezing";
|
|
136
|
+
case Double d -> "Warm-ish (%.1f)".formatted(d);
|
|
137
|
+
case String s -> "Not a temperature: " + s;
|
|
138
|
+
case null -> "No reading";
|
|
139
|
+
default -> "Unknown type";
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Record Patterns (Destructuring)
|
|
145
|
+
|
|
146
|
+
```java
|
|
147
|
+
// Nested destructuring
|
|
148
|
+
record Address(String city, String country) {}
|
|
149
|
+
record Person(String name, Address address) {}
|
|
150
|
+
|
|
151
|
+
public String greet(Object obj) {
|
|
152
|
+
return switch (obj) {
|
|
153
|
+
case Person(var name, Address(var city, _)) ->
|
|
154
|
+
"Hello %s from %s".formatted(name, city);
|
|
155
|
+
default -> "Hello stranger";
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Text Blocks (Java 15+)
|
|
163
|
+
|
|
164
|
+
```java
|
|
165
|
+
// Multi-line strings with proper indentation
|
|
166
|
+
String json = """
|
|
167
|
+
{
|
|
168
|
+
"name": "%s",
|
|
169
|
+
"email": "%s",
|
|
170
|
+
"active": true
|
|
171
|
+
}
|
|
172
|
+
""".formatted(user.name(), user.email());
|
|
173
|
+
|
|
174
|
+
String sql = """
|
|
175
|
+
SELECT u.id, u.name, u.email
|
|
176
|
+
FROM users u
|
|
177
|
+
JOIN orders o ON o.user_id = u.id
|
|
178
|
+
WHERE u.active = true
|
|
179
|
+
AND o.created_at > ?
|
|
180
|
+
ORDER BY u.name
|
|
181
|
+
""";
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Virtual Threads (Java 21+)
|
|
187
|
+
|
|
188
|
+
### Basic Virtual Thread
|
|
189
|
+
|
|
190
|
+
```java
|
|
191
|
+
// Lightweight thread - does not pin platform thread during I/O
|
|
192
|
+
Thread.startVirtualThread(() -> {
|
|
193
|
+
var result = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
|
194
|
+
process(result.body());
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Structured Concurrency (Preview)
|
|
199
|
+
|
|
200
|
+
```java
|
|
201
|
+
// All subtasks complete or cancel together
|
|
202
|
+
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
|
|
203
|
+
Subtask<User> userTask = scope.fork(() -> fetchUser(userId));
|
|
204
|
+
Subtask<List<Order>> ordersTask = scope.fork(() -> fetchOrders(userId));
|
|
205
|
+
|
|
206
|
+
scope.join().throwIfFailed();
|
|
207
|
+
|
|
208
|
+
return new UserDashboard(userTask.get(), ordersTask.get());
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### ExecutorService with Virtual Threads
|
|
213
|
+
|
|
214
|
+
```java
|
|
215
|
+
// Process thousands of concurrent I/O tasks
|
|
216
|
+
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
|
|
217
|
+
List<Future<Response>> futures = urls.stream()
|
|
218
|
+
.map(url -> executor.submit(() -> httpClient.send(
|
|
219
|
+
HttpRequest.newBuilder(URI.create(url)).build(),
|
|
220
|
+
HttpResponse.BodyHandlers.ofString()
|
|
221
|
+
)))
|
|
222
|
+
.toList();
|
|
223
|
+
|
|
224
|
+
List<Response> responses = futures.stream()
|
|
225
|
+
.map(f -> {
|
|
226
|
+
try { return f.get(); }
|
|
227
|
+
catch (Exception e) { throw new RuntimeException(e); }
|
|
228
|
+
})
|
|
229
|
+
.toList();
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Other Modern Features
|
|
236
|
+
|
|
237
|
+
### Enhanced instanceof (Java 16+)
|
|
238
|
+
|
|
239
|
+
```java
|
|
240
|
+
// Pattern variable binding eliminates explicit cast
|
|
241
|
+
if (obj instanceof String s && s.length() > 5) {
|
|
242
|
+
System.out.println(s.toUpperCase());
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Works with negation
|
|
246
|
+
if (!(obj instanceof String s)) {
|
|
247
|
+
throw new IllegalArgumentException("Expected String");
|
|
248
|
+
}
|
|
249
|
+
// s is in scope here
|
|
250
|
+
process(s);
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Helpful NullPointerExceptions (Java 14+)
|
|
254
|
+
|
|
255
|
+
```java
|
|
256
|
+
// JVM now tells you exactly which reference was null:
|
|
257
|
+
// java.lang.NullPointerException: Cannot invoke "String.length()"
|
|
258
|
+
// because the return value of "User.name()" is null
|
|
259
|
+
// Enable with: -XX:+ShowCodeDetailsInExceptionMessages (default since Java 17)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Stream Gatherers (Java 22+ Preview)
|
|
263
|
+
|
|
264
|
+
```java
|
|
265
|
+
// Custom intermediate stream operations
|
|
266
|
+
var windowedAverages = temperatures.stream()
|
|
267
|
+
.gather(Gatherers.windowSliding(5))
|
|
268
|
+
.map(window -> window.stream().mapToDouble(d -> d).average().orElse(0))
|
|
269
|
+
.toList();
|
|
270
|
+
```
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# Extended Java Patterns
|
|
2
|
+
|
|
3
|
+
Correct patterns for Java development. Reference from main SKILL.md.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Builder Pattern (Type-Safe)
|
|
8
|
+
|
|
9
|
+
```java
|
|
10
|
+
// Compile-time enforcement of required fields
|
|
11
|
+
public record EmailMessage(String to, String subject, String body, List<String> cc) {
|
|
12
|
+
|
|
13
|
+
public static Builder builder() { return new Builder(); }
|
|
14
|
+
|
|
15
|
+
public static class Builder {
|
|
16
|
+
private String to;
|
|
17
|
+
private String subject;
|
|
18
|
+
private String body;
|
|
19
|
+
private List<String> cc = List.of();
|
|
20
|
+
|
|
21
|
+
public Builder to(String to) { this.to = Objects.requireNonNull(to); return this; }
|
|
22
|
+
public Builder subject(String subject) { this.subject = Objects.requireNonNull(subject); return this; }
|
|
23
|
+
public Builder body(String body) { this.body = Objects.requireNonNull(body); return this; }
|
|
24
|
+
public Builder cc(List<String> cc) { this.cc = List.copyOf(cc); return this; }
|
|
25
|
+
|
|
26
|
+
public EmailMessage build() {
|
|
27
|
+
Objects.requireNonNull(to, "to is required");
|
|
28
|
+
Objects.requireNonNull(subject, "subject is required");
|
|
29
|
+
Objects.requireNonNull(body, "body is required");
|
|
30
|
+
return new EmailMessage(to, subject, body, cc);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Usage
|
|
36
|
+
var email = EmailMessage.builder()
|
|
37
|
+
.to("user@example.com")
|
|
38
|
+
.subject("Welcome")
|
|
39
|
+
.body("Hello!")
|
|
40
|
+
.build();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Strategy via Interfaces
|
|
46
|
+
|
|
47
|
+
```java
|
|
48
|
+
// Define strategy as functional interface
|
|
49
|
+
@FunctionalInterface
|
|
50
|
+
public interface PricingStrategy {
|
|
51
|
+
Money calculatePrice(Order order);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Implementations as lambdas or classes
|
|
55
|
+
PricingStrategy standard = order -> order.subtotal();
|
|
56
|
+
PricingStrategy discounted = order -> order.subtotal().multiply(0.9);
|
|
57
|
+
PricingStrategy tiered = order -> {
|
|
58
|
+
if (order.itemCount() > 10) return order.subtotal().multiply(0.8);
|
|
59
|
+
if (order.itemCount() > 5) return order.subtotal().multiply(0.9);
|
|
60
|
+
return order.subtotal();
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Inject strategy
|
|
64
|
+
public class OrderService {
|
|
65
|
+
private final PricingStrategy pricing;
|
|
66
|
+
|
|
67
|
+
public OrderService(PricingStrategy pricing) {
|
|
68
|
+
this.pricing = pricing;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public Money calculateTotal(Order order) {
|
|
72
|
+
return pricing.calculatePrice(order);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Repository Pattern
|
|
80
|
+
|
|
81
|
+
```java
|
|
82
|
+
public interface UserRepository {
|
|
83
|
+
Optional<User> findById(String id);
|
|
84
|
+
List<User> findByEmail(String email);
|
|
85
|
+
User save(User user);
|
|
86
|
+
void deleteById(String id);
|
|
87
|
+
boolean existsById(String id);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Implementation with constructor injection
|
|
91
|
+
public class JpaUserRepository implements UserRepository {
|
|
92
|
+
private final EntityManager em;
|
|
93
|
+
|
|
94
|
+
public JpaUserRepository(EntityManager em) {
|
|
95
|
+
this.em = em;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@Override
|
|
99
|
+
public Optional<User> findById(String id) {
|
|
100
|
+
return Optional.ofNullable(em.find(User.class, id));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@Override
|
|
104
|
+
public User save(User user) {
|
|
105
|
+
return em.merge(user);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Value Objects
|
|
113
|
+
|
|
114
|
+
```java
|
|
115
|
+
// Immutable value object with validation
|
|
116
|
+
public record Money(BigDecimal amount, Currency currency) {
|
|
117
|
+
public Money {
|
|
118
|
+
Objects.requireNonNull(amount, "amount must not be null");
|
|
119
|
+
Objects.requireNonNull(currency, "currency must not be null");
|
|
120
|
+
if (amount.scale() > currency.getDefaultFractionDigits()) {
|
|
121
|
+
throw new IllegalArgumentException("Scale exceeds currency precision");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public Money add(Money other) {
|
|
126
|
+
requireSameCurrency(other);
|
|
127
|
+
return new Money(amount.add(other.amount), currency);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public Money multiply(double factor) {
|
|
131
|
+
return new Money(
|
|
132
|
+
amount.multiply(BigDecimal.valueOf(factor))
|
|
133
|
+
.setScale(currency.getDefaultFractionDigits(), RoundingMode.HALF_UP),
|
|
134
|
+
currency
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private void requireSameCurrency(Money other) {
|
|
139
|
+
if (!currency.equals(other.currency)) {
|
|
140
|
+
throw new IllegalArgumentException(
|
|
141
|
+
"Cannot combine %s and %s".formatted(currency, other.currency)
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Event-Driven Pattern
|
|
151
|
+
|
|
152
|
+
```java
|
|
153
|
+
// Domain event as sealed interface
|
|
154
|
+
public sealed interface OrderEvent permits OrderCreated, OrderShipped, OrderCancelled {
|
|
155
|
+
String orderId();
|
|
156
|
+
Instant occurredAt();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public record OrderCreated(String orderId, Instant occurredAt, List<LineItem> items)
|
|
160
|
+
implements OrderEvent {}
|
|
161
|
+
|
|
162
|
+
public record OrderShipped(String orderId, Instant occurredAt, String trackingNumber)
|
|
163
|
+
implements OrderEvent {}
|
|
164
|
+
|
|
165
|
+
public record OrderCancelled(String orderId, Instant occurredAt, String reason)
|
|
166
|
+
implements OrderEvent {}
|
|
167
|
+
|
|
168
|
+
// Event handler with exhaustive matching
|
|
169
|
+
public class OrderEventHandler {
|
|
170
|
+
public void handle(OrderEvent event) {
|
|
171
|
+
switch (event) {
|
|
172
|
+
case OrderCreated e -> notifyWarehouse(e.items());
|
|
173
|
+
case OrderShipped e -> sendTrackingEmail(e.trackingNumber());
|
|
174
|
+
case OrderCancelled e -> processRefund(e.orderId(), e.reason());
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Stream Pipelines
|
|
183
|
+
|
|
184
|
+
```java
|
|
185
|
+
// Declarative collection processing
|
|
186
|
+
public List<String> getActiveUserEmails(List<User> users) {
|
|
187
|
+
return users.stream()
|
|
188
|
+
.filter(User::isActive)
|
|
189
|
+
.map(User::email)
|
|
190
|
+
.filter(email -> email.contains("@"))
|
|
191
|
+
.sorted()
|
|
192
|
+
.toList(); // Unmodifiable list (Java 16+)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Grouping and aggregation
|
|
196
|
+
public Map<Department, Long> countByDepartment(List<Employee> employees) {
|
|
197
|
+
return employees.stream()
|
|
198
|
+
.collect(Collectors.groupingBy(Employee::department, Collectors.counting()));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Reducing to summary
|
|
202
|
+
public record OrderSummary(int count, Money total) {}
|
|
203
|
+
|
|
204
|
+
public OrderSummary summarize(List<Order> orders) {
|
|
205
|
+
return orders.stream()
|
|
206
|
+
.reduce(
|
|
207
|
+
new OrderSummary(0, Money.ZERO),
|
|
208
|
+
(summary, order) -> new OrderSummary(
|
|
209
|
+
summary.count() + 1,
|
|
210
|
+
summary.total().add(order.total())
|
|
211
|
+
),
|
|
212
|
+
(a, b) -> new OrderSummary(a.count() + b.count(), a.total().add(b.total()))
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Dependency Injection (Manual)
|
|
220
|
+
|
|
221
|
+
```java
|
|
222
|
+
// Composition root wires everything together
|
|
223
|
+
public class Application {
|
|
224
|
+
public static void main(String[] args) {
|
|
225
|
+
var config = Config.load();
|
|
226
|
+
var dataSource = createDataSource(config);
|
|
227
|
+
var userRepo = new JpaUserRepository(dataSource);
|
|
228
|
+
var eventBus = new InMemoryEventBus();
|
|
229
|
+
var userService = new UserService(userRepo, eventBus);
|
|
230
|
+
var userController = new UserController(userService);
|
|
231
|
+
|
|
232
|
+
startServer(config.port(), userController);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Common Java Violations
|
|
2
|
+
|
|
3
|
+
Extended violation patterns for Java reviews. Reference from main SKILL.md.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Null Returns
|
|
8
|
+
|
|
9
|
+
### Returning null Instead of Optional
|
|
10
|
+
|
|
11
|
+
```java
|
|
12
|
+
// VIOLATION: Null return forces callers to null-check
|
|
13
|
+
public User findById(String id) {
|
|
14
|
+
return userMap.get(id); // Returns null if absent
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Callers must remember to check:
|
|
18
|
+
User user = findById(id);
|
|
19
|
+
user.getName(); // NullPointerException if not found
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Nullable Collections
|
|
23
|
+
|
|
24
|
+
```java
|
|
25
|
+
// VIOLATION: Returning null instead of empty collection
|
|
26
|
+
public List<Order> getOrders(String userId) {
|
|
27
|
+
List<Order> orders = orderMap.get(userId);
|
|
28
|
+
return orders; // null if user has no orders
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Callers iterate without checking:
|
|
32
|
+
for (Order o : getOrders(userId)) { // NullPointerException
|
|
33
|
+
process(o);
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Raw Types
|
|
40
|
+
|
|
41
|
+
### Unparameterized Generics
|
|
42
|
+
|
|
43
|
+
```java
|
|
44
|
+
// VIOLATION: Raw List - no type safety
|
|
45
|
+
List users = new ArrayList();
|
|
46
|
+
users.add("not a user"); // No compile error
|
|
47
|
+
users.add(42); // No compile error
|
|
48
|
+
User first = (User) users.get(0); // ClassCastException at runtime
|
|
49
|
+
|
|
50
|
+
// VIOLATION: Raw Map
|
|
51
|
+
Map cache = new HashMap();
|
|
52
|
+
cache.put(123, "value");
|
|
53
|
+
String val = (String) cache.get(123); // Unsafe cast
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Raw Type in Method Signatures
|
|
57
|
+
|
|
58
|
+
```java
|
|
59
|
+
// VIOLATION: Raw Comparable
|
|
60
|
+
public class Price implements Comparable {
|
|
61
|
+
public int compareTo(Object other) {
|
|
62
|
+
return Double.compare(this.amount, ((Price) other).amount); // Unsafe cast
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// VIOLATION: Raw Iterator
|
|
67
|
+
Iterator it = collection.iterator();
|
|
68
|
+
while (it.hasNext()) {
|
|
69
|
+
String s = (String) it.next(); // Unsafe cast
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Checked Exception Abuse
|
|
76
|
+
|
|
77
|
+
### Broad Throws Declarations
|
|
78
|
+
|
|
79
|
+
```java
|
|
80
|
+
// VIOLATION: throws Exception - hides what can actually go wrong
|
|
81
|
+
public User createUser(String name) throws Exception {
|
|
82
|
+
// Callers must catch Exception, can't handle specific failures
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// VIOLATION: Wrapping everything in checked exceptions
|
|
86
|
+
public void process(String data) throws ProcessingException {
|
|
87
|
+
try {
|
|
88
|
+
Integer.parseInt(data);
|
|
89
|
+
} catch (NumberFormatException e) {
|
|
90
|
+
throw new ProcessingException("Failed", e); // Unnecessary wrapping
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Swallowed Exceptions
|
|
96
|
+
|
|
97
|
+
```java
|
|
98
|
+
// VIOLATION: Empty catch block
|
|
99
|
+
try {
|
|
100
|
+
connection.close();
|
|
101
|
+
} catch (SQLException e) {
|
|
102
|
+
// silently ignored - resource leak hidden
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// VIOLATION: Catching and logging only
|
|
106
|
+
try {
|
|
107
|
+
processPayment(order);
|
|
108
|
+
} catch (PaymentException e) {
|
|
109
|
+
logger.error("Payment failed", e);
|
|
110
|
+
// Continues as if nothing happened - order in inconsistent state
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Mutable Data Objects
|
|
117
|
+
|
|
118
|
+
### JavaBean-Style Mutability
|
|
119
|
+
|
|
120
|
+
```java
|
|
121
|
+
// VIOLATION: Mutable DTO with setters
|
|
122
|
+
public class UserDTO {
|
|
123
|
+
private String name;
|
|
124
|
+
private String email;
|
|
125
|
+
|
|
126
|
+
public void setName(String name) { this.name = name; }
|
|
127
|
+
public void setEmail(String email) { this.email = email; }
|
|
128
|
+
public String getName() { return name; }
|
|
129
|
+
public String getEmail() { return email; }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Anyone can modify at any time:
|
|
133
|
+
dto.setName("changed"); // No control over when/where mutation happens
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Exposing Internal Mutable State
|
|
137
|
+
|
|
138
|
+
```java
|
|
139
|
+
// VIOLATION: Getter returns mutable internal list
|
|
140
|
+
public class Team {
|
|
141
|
+
private List<String> members = new ArrayList<>();
|
|
142
|
+
|
|
143
|
+
public List<String> getMembers() {
|
|
144
|
+
return members; // Caller can modify internal state
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// External code breaks encapsulation:
|
|
149
|
+
team.getMembers().clear(); // Empties the team's internal list
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Deep Inheritance
|
|
155
|
+
|
|
156
|
+
### Fragile Base Class
|
|
157
|
+
|
|
158
|
+
```java
|
|
159
|
+
// VIOLATION: Deep hierarchy creates tight coupling
|
|
160
|
+
public abstract class AbstractEntity { ... }
|
|
161
|
+
public abstract class AbstractAuditableEntity extends AbstractEntity { ... }
|
|
162
|
+
public abstract class AbstractVersionedEntity extends AbstractAuditableEntity { ... }
|
|
163
|
+
public class User extends AbstractVersionedEntity { ... }
|
|
164
|
+
|
|
165
|
+
// Changing any base class ripples through all descendants
|
|
166
|
+
// Testing requires understanding 4 levels of behavior
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Inheritance for Code Reuse
|
|
170
|
+
|
|
171
|
+
```java
|
|
172
|
+
// VIOLATION: Extending just to reuse utility methods
|
|
173
|
+
public class OrderService extends BaseService {
|
|
174
|
+
// Only extends BaseService to get logAndAudit() method
|
|
175
|
+
public void processOrder(Order order) {
|
|
176
|
+
logAndAudit("processing", order.getId()); // Inherited utility
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Should be: inject a LogAuditService instead
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Concurrency Violations
|
|
185
|
+
|
|
186
|
+
### Shared Mutable State Without Synchronization
|
|
187
|
+
|
|
188
|
+
```java
|
|
189
|
+
// VIOLATION: HashMap accessed from multiple threads
|
|
190
|
+
private Map<String, Session> sessions = new HashMap<>();
|
|
191
|
+
|
|
192
|
+
public void addSession(String id, Session s) {
|
|
193
|
+
sessions.put(id, s); // Not thread-safe
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Double-Checked Locking Done Wrong
|
|
198
|
+
|
|
199
|
+
```java
|
|
200
|
+
// VIOLATION: Missing volatile on lazily-initialized field
|
|
201
|
+
private ExpensiveObject instance;
|
|
202
|
+
|
|
203
|
+
public ExpensiveObject getInstance() {
|
|
204
|
+
if (instance == null) {
|
|
205
|
+
synchronized (this) {
|
|
206
|
+
if (instance == null) {
|
|
207
|
+
instance = new ExpensiveObject(); // Partially constructed object visible
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return instance;
|
|
212
|
+
}
|
|
213
|
+
```
|