@booklib/skills 1.2.0 → 1.3.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/CONTRIBUTING.md +122 -0
- package/README.md +20 -2
- package/ROADMAP.md +36 -0
- package/animation-at-work/evals/evals.json +44 -0
- package/animation-at-work/examples/after.md +64 -0
- package/animation-at-work/examples/before.md +35 -0
- package/animation-at-work/scripts/audit_animations.py +295 -0
- package/bin/skills.js +552 -42
- package/clean-code-reviewer/SKILL.md +109 -1
- package/clean-code-reviewer/evals/evals.json +121 -3
- package/clean-code-reviewer/examples/after.md +48 -0
- package/clean-code-reviewer/examples/before.md +33 -0
- package/clean-code-reviewer/references/api_reference.md +158 -0
- package/clean-code-reviewer/references/practices-catalog.md +282 -0
- package/clean-code-reviewer/references/review-checklist.md +254 -0
- package/clean-code-reviewer/scripts/pre-review.py +206 -0
- package/data-intensive-patterns/evals/evals.json +43 -0
- package/data-intensive-patterns/examples/after.md +61 -0
- package/data-intensive-patterns/examples/before.md +38 -0
- package/data-intensive-patterns/scripts/adr.py +213 -0
- package/data-pipelines/evals/evals.json +45 -0
- package/data-pipelines/examples/after.md +97 -0
- package/data-pipelines/examples/before.md +37 -0
- package/data-pipelines/scripts/new_pipeline.py +444 -0
- package/design-patterns/evals/evals.json +46 -0
- package/design-patterns/examples/after.md +52 -0
- package/design-patterns/examples/before.md +29 -0
- package/design-patterns/scripts/scaffold.py +807 -0
- package/domain-driven-design/SKILL.md +120 -0
- package/domain-driven-design/evals/evals.json +48 -0
- package/domain-driven-design/examples/after.md +80 -0
- package/domain-driven-design/examples/before.md +43 -0
- package/domain-driven-design/scripts/scaffold.py +421 -0
- package/effective-java/evals/evals.json +46 -0
- package/effective-java/examples/after.md +83 -0
- package/effective-java/examples/before.md +37 -0
- package/effective-java/scripts/checkstyle_setup.py +211 -0
- package/effective-kotlin/evals/evals.json +45 -0
- package/effective-kotlin/examples/after.md +36 -0
- package/effective-kotlin/examples/before.md +38 -0
- package/effective-python/evals/evals.json +44 -0
- package/effective-python/examples/after.md +56 -0
- package/effective-python/examples/before.md +40 -0
- package/effective-python/references/api_reference.md +218 -0
- package/effective-python/references/practices-catalog.md +483 -0
- package/effective-python/references/review-checklist.md +190 -0
- package/effective-python/scripts/lint.py +173 -0
- package/kotlin-in-action/evals/evals.json +43 -0
- package/kotlin-in-action/examples/after.md +53 -0
- package/kotlin-in-action/examples/before.md +39 -0
- package/kotlin-in-action/scripts/setup_detekt.py +224 -0
- package/lean-startup/evals/evals.json +43 -0
- package/lean-startup/examples/after.md +80 -0
- package/lean-startup/examples/before.md +34 -0
- package/lean-startup/scripts/new_experiment.py +286 -0
- package/microservices-patterns/SKILL.md +140 -0
- package/microservices-patterns/evals/evals.json +45 -0
- package/microservices-patterns/examples/after.md +69 -0
- package/microservices-patterns/examples/before.md +40 -0
- package/microservices-patterns/scripts/new_service.py +583 -0
- package/package.json +2 -8
- package/refactoring-ui/evals/evals.json +45 -0
- package/refactoring-ui/examples/after.md +85 -0
- package/refactoring-ui/examples/before.md +58 -0
- package/refactoring-ui/scripts/audit_css.py +250 -0
- package/skill-router/SKILL.md +142 -0
- package/skill-router/evals/evals.json +38 -0
- package/skill-router/examples/after.md +63 -0
- package/skill-router/examples/before.md +39 -0
- package/skill-router/references/api_reference.md +24 -0
- package/skill-router/references/routing-heuristics.md +89 -0
- package/skill-router/references/skill-catalog.md +156 -0
- package/skill-router/scripts/route.py +266 -0
- package/storytelling-with-data/evals/evals.json +47 -0
- package/storytelling-with-data/examples/after.md +50 -0
- package/storytelling-with-data/examples/before.md +33 -0
- package/storytelling-with-data/scripts/chart_review.py +301 -0
- package/system-design-interview/evals/evals.json +45 -0
- package/system-design-interview/examples/after.md +94 -0
- package/system-design-interview/examples/before.md +27 -0
- package/system-design-interview/scripts/new_design.py +421 -0
- package/using-asyncio-python/evals/evals.json +43 -0
- package/using-asyncio-python/examples/after.md +68 -0
- package/using-asyncio-python/examples/before.md +39 -0
- package/using-asyncio-python/scripts/check_blocking.py +270 -0
- package/web-scraping-python/evals/evals.json +46 -0
- package/web-scraping-python/examples/after.md +109 -0
- package/web-scraping-python/examples/before.md +40 -0
- package/web-scraping-python/scripts/new_scraper.py +231 -0
- /package/{effective-python-skill → effective-python}/SKILL.md +0 -0
- /package/{effective-python-skill → effective-python}/ref-01-pythonic-thinking.md +0 -0
- /package/{effective-python-skill → effective-python}/ref-02-lists-and-dicts.md +0 -0
- /package/{effective-python-skill → effective-python}/ref-03-functions.md +0 -0
- /package/{effective-python-skill → effective-python}/ref-04-comprehensions-generators.md +0 -0
- /package/{effective-python-skill → effective-python}/ref-05-classes-interfaces.md +0 -0
- /package/{effective-python-skill → effective-python}/ref-06-metaclasses-attributes.md +0 -0
- /package/{effective-python-skill → effective-python}/ref-07-concurrency.md +0 -0
- /package/{effective-python-skill → effective-python}/ref-08-robustness-performance.md +0 -0
- /package/{effective-python-skill → effective-python}/ref-09-testing-debugging.md +0 -0
- /package/{effective-python-skill → effective-python}/ref-10-collaboration.md +0 -0
|
@@ -0,0 +1,807 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
GoF Pattern Scaffold — generates complete, idiomatic boilerplate for design patterns.
|
|
4
|
+
Usage: python scaffold.py <pattern> <ClassName> [--lang python|kotlin|java]
|
|
5
|
+
|
|
6
|
+
Supported patterns: strategy, observer, factory, decorator, command, singleton
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from string import Template
|
|
13
|
+
|
|
14
|
+
# ---------------------------------------------------------------------------
|
|
15
|
+
# Pattern templates per language
|
|
16
|
+
# ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
PATTERNS: dict[str, dict] = {}
|
|
19
|
+
|
|
20
|
+
# ---- STRATEGY --------------------------------------------------------------
|
|
21
|
+
PATTERNS["strategy"] = {
|
|
22
|
+
"python": Template('''\
|
|
23
|
+
#!/usr/bin/env python3
|
|
24
|
+
"""Strategy pattern — ${Name}."""
|
|
25
|
+
from abc import ABC, abstractmethod
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Interface
|
|
29
|
+
class ${Name}Strategy(ABC):
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def execute(self, data: str) -> str: ...
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# Concrete strategy A
|
|
35
|
+
class ${Name}StrategyA(${Name}Strategy):
|
|
36
|
+
def execute(self, data: str) -> str:
|
|
37
|
+
return f"[StrategyA] {data.upper()}"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# Concrete strategy B
|
|
41
|
+
class ${Name}StrategyB(${Name}Strategy):
|
|
42
|
+
def execute(self, data: str) -> str:
|
|
43
|
+
return f"[StrategyB] {data[::-1]}"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# Context
|
|
47
|
+
class ${Name}Context:
|
|
48
|
+
def __init__(self, strategy: ${Name}Strategy) -> None:
|
|
49
|
+
self._strategy = strategy
|
|
50
|
+
|
|
51
|
+
def set_strategy(self, strategy: ${Name}Strategy) -> None:
|
|
52
|
+
self._strategy = strategy
|
|
53
|
+
|
|
54
|
+
def run(self, data: str) -> str:
|
|
55
|
+
return self._strategy.execute(data)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# Usage example
|
|
59
|
+
if __name__ == "__main__":
|
|
60
|
+
ctx = ${Name}Context(${Name}StrategyA())
|
|
61
|
+
print(ctx.run("hello")) # [StrategyA] HELLO
|
|
62
|
+
ctx.set_strategy(${Name}StrategyB())
|
|
63
|
+
print(ctx.run("hello")) # [StrategyB] olleh
|
|
64
|
+
'''),
|
|
65
|
+
"kotlin": Template('''\
|
|
66
|
+
package com.example.patterns
|
|
67
|
+
|
|
68
|
+
// Interface
|
|
69
|
+
interface ${Name}Strategy {
|
|
70
|
+
fun execute(data: String): String
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Concrete A
|
|
74
|
+
class ${Name}StrategyA : ${Name}Strategy {
|
|
75
|
+
override fun execute(data: String) = "[StrategyA] ${dollar}{data.uppercase()}"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Concrete B
|
|
79
|
+
class ${Name}StrategyB : ${Name}Strategy {
|
|
80
|
+
override fun execute(data: String) = "[StrategyB] ${dollar}{data.reversed()}"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Context
|
|
84
|
+
class ${Name}Context(private var strategy: ${Name}Strategy) {
|
|
85
|
+
fun setStrategy(s: ${Name}Strategy) { strategy = s }
|
|
86
|
+
fun run(data: String): String = strategy.execute(data)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
fun main() {
|
|
90
|
+
val ctx = ${Name}Context(${Name}StrategyA())
|
|
91
|
+
println(ctx.run("hello"))
|
|
92
|
+
ctx.setStrategy(${Name}StrategyB())
|
|
93
|
+
println(ctx.run("hello"))
|
|
94
|
+
}
|
|
95
|
+
'''),
|
|
96
|
+
"java": Template('''\
|
|
97
|
+
package com.example.patterns;
|
|
98
|
+
|
|
99
|
+
// Interface
|
|
100
|
+
public interface ${Name}Strategy {
|
|
101
|
+
String execute(String data);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Concrete A
|
|
105
|
+
class ${Name}StrategyA implements ${Name}Strategy {
|
|
106
|
+
public String execute(String data) { return "[StrategyA] " + data.toUpperCase(); }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Concrete B
|
|
110
|
+
class ${Name}StrategyB implements ${Name}Strategy {
|
|
111
|
+
public String execute(String data) {
|
|
112
|
+
return "[StrategyB] " + new StringBuilder(data).reverse();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Context
|
|
117
|
+
class ${Name}Context {
|
|
118
|
+
private ${Name}Strategy strategy;
|
|
119
|
+
public ${Name}Context(${Name}Strategy s) { this.strategy = s; }
|
|
120
|
+
public void setStrategy(${Name}Strategy s) { this.strategy = s; }
|
|
121
|
+
public String run(String data) { return strategy.execute(data); }
|
|
122
|
+
|
|
123
|
+
public static void main(String[] args) {
|
|
124
|
+
var ctx = new ${Name}Context(new ${Name}StrategyA());
|
|
125
|
+
System.out.println(ctx.run("hello"));
|
|
126
|
+
ctx.setStrategy(new ${Name}StrategyB());
|
|
127
|
+
System.out.println(ctx.run("hello"));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
'''),
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# ---- OBSERVER --------------------------------------------------------------
|
|
134
|
+
PATTERNS["observer"] = {
|
|
135
|
+
"python": Template('''\
|
|
136
|
+
#!/usr/bin/env python3
|
|
137
|
+
"""Observer pattern — ${Name}."""
|
|
138
|
+
from abc import ABC, abstractmethod
|
|
139
|
+
from typing import List
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class ${Name}Observer(ABC):
|
|
143
|
+
@abstractmethod
|
|
144
|
+
def update(self, event: str, payload: object) -> None: ...
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class ${Name}Subject:
|
|
148
|
+
def __init__(self) -> None:
|
|
149
|
+
self._observers: List[${Name}Observer] = []
|
|
150
|
+
|
|
151
|
+
def subscribe(self, obs: ${Name}Observer) -> None:
|
|
152
|
+
self._observers.append(obs)
|
|
153
|
+
|
|
154
|
+
def unsubscribe(self, obs: ${Name}Observer) -> None:
|
|
155
|
+
self._observers.remove(obs)
|
|
156
|
+
|
|
157
|
+
def notify(self, event: str, payload: object = None) -> None:
|
|
158
|
+
for obs in self._observers:
|
|
159
|
+
obs.update(event, payload)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class ${Name}LogObserver(${Name}Observer):
|
|
163
|
+
def update(self, event: str, payload: object) -> None:
|
|
164
|
+
print(f"[LOG] event={event!r} payload={payload!r}")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class ${Name}MetricsObserver(${Name}Observer):
|
|
168
|
+
def __init__(self) -> None:
|
|
169
|
+
self.counts: dict[str, int] = {}
|
|
170
|
+
|
|
171
|
+
def update(self, event: str, payload: object) -> None:
|
|
172
|
+
self.counts[event] = self.counts.get(event, 0) + 1
|
|
173
|
+
print(f"[METRICS] {event} count={self.counts[event]}")
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
if __name__ == "__main__":
|
|
177
|
+
subject = ${Name}Subject()
|
|
178
|
+
subject.subscribe(${Name}LogObserver())
|
|
179
|
+
metrics = ${Name}MetricsObserver()
|
|
180
|
+
subject.subscribe(metrics)
|
|
181
|
+
subject.notify("created", {"id": 1})
|
|
182
|
+
subject.notify("updated", {"id": 1, "field": "name"})
|
|
183
|
+
subject.notify("created", {"id": 2})
|
|
184
|
+
'''),
|
|
185
|
+
"kotlin": Template('''\
|
|
186
|
+
package com.example.patterns
|
|
187
|
+
|
|
188
|
+
interface ${Name}Observer {
|
|
189
|
+
fun update(event: String, payload: Any?)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
class ${Name}Subject {
|
|
193
|
+
private val observers = mutableListOf<${Name}Observer>()
|
|
194
|
+
fun subscribe(o: ${Name}Observer) { observers.add(o) }
|
|
195
|
+
fun unsubscribe(o: ${Name}Observer) { observers.remove(o) }
|
|
196
|
+
fun notify(event: String, payload: Any? = null) =
|
|
197
|
+
observers.forEach { it.update(event, payload) }
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
class ${Name}LogObserver : ${Name}Observer {
|
|
201
|
+
override fun update(event: String, payload: Any?) =
|
|
202
|
+
println("[LOG] event=$event payload=$payload")
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
class ${Name}MetricsObserver : ${Name}Observer {
|
|
206
|
+
private val counts = mutableMapOf<String, Int>()
|
|
207
|
+
override fun update(event: String, payload: Any?) {
|
|
208
|
+
counts[event] = (counts[event] ?: 0) + 1
|
|
209
|
+
println("[METRICS] $event count=${dollar}{counts[event]}")
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
fun main() {
|
|
214
|
+
val subject = ${Name}Subject()
|
|
215
|
+
subject.subscribe(${Name}LogObserver())
|
|
216
|
+
subject.subscribe(${Name}MetricsObserver())
|
|
217
|
+
subject.notify("created", mapOf("id" to 1))
|
|
218
|
+
subject.notify("updated", mapOf("id" to 1))
|
|
219
|
+
}
|
|
220
|
+
'''),
|
|
221
|
+
"java": Template('''\
|
|
222
|
+
package com.example.patterns;
|
|
223
|
+
import java.util.ArrayList;
|
|
224
|
+
import java.util.HashMap;
|
|
225
|
+
import java.util.List;
|
|
226
|
+
import java.util.Map;
|
|
227
|
+
|
|
228
|
+
public interface ${Name}Observer {
|
|
229
|
+
void update(String event, Object payload);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
class ${Name}Subject {
|
|
233
|
+
private final List<${Name}Observer> observers = new ArrayList<>();
|
|
234
|
+
public void subscribe(${Name}Observer o) { observers.add(o); }
|
|
235
|
+
public void unsubscribe(${Name}Observer o) { observers.remove(o); }
|
|
236
|
+
public void notify(String event, Object payload) {
|
|
237
|
+
observers.forEach(o -> o.update(event, payload));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
class ${Name}LogObserver implements ${Name}Observer {
|
|
242
|
+
public void update(String event, Object payload) {
|
|
243
|
+
System.out.printf("[LOG] event=%s payload=%s%n", event, payload);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
class ${Name}MetricsObserver implements ${Name}Observer {
|
|
248
|
+
private final Map<String, Integer> counts = new HashMap<>();
|
|
249
|
+
public void update(String event, Object payload) {
|
|
250
|
+
counts.merge(event, 1, Integer::sum);
|
|
251
|
+
System.out.printf("[METRICS] %s count=%d%n", event, counts.get(event));
|
|
252
|
+
}
|
|
253
|
+
public static void main(String[] args) {
|
|
254
|
+
var s = new ${Name}Subject();
|
|
255
|
+
s.subscribe(new ${Name}LogObserver());
|
|
256
|
+
s.subscribe(new ${Name}MetricsObserver());
|
|
257
|
+
s.notify("created", Map.of("id", 1));
|
|
258
|
+
s.notify("updated", Map.of("id", 1));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
'''),
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
# ---- FACTORY ---------------------------------------------------------------
|
|
265
|
+
PATTERNS["factory"] = {
|
|
266
|
+
"python": Template('''\
|
|
267
|
+
#!/usr/bin/env python3
|
|
268
|
+
"""Factory Method pattern — ${Name}."""
|
|
269
|
+
from abc import ABC, abstractmethod
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class ${Name}Product(ABC):
|
|
273
|
+
@abstractmethod
|
|
274
|
+
def operation(self) -> str: ...
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class ${Name}ConcreteProductA(${Name}Product):
|
|
278
|
+
def operation(self) -> str:
|
|
279
|
+
return "${Name}ConcreteProductA result"
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class ${Name}ConcreteProductB(${Name}Product):
|
|
283
|
+
def operation(self) -> str:
|
|
284
|
+
return "${Name}ConcreteProductB result"
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
class ${Name}Creator(ABC):
|
|
288
|
+
@abstractmethod
|
|
289
|
+
def create_product(self) -> ${Name}Product: ...
|
|
290
|
+
|
|
291
|
+
def deliver(self) -> str:
|
|
292
|
+
product = self.create_product()
|
|
293
|
+
return f"Creator: {product.operation()}"
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class ${Name}CreatorA(${Name}Creator):
|
|
297
|
+
def create_product(self) -> ${Name}Product:
|
|
298
|
+
return ${Name}ConcreteProductA()
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
class ${Name}CreatorB(${Name}Creator):
|
|
302
|
+
def create_product(self) -> ${Name}Product:
|
|
303
|
+
return ${Name}ConcreteProductB()
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def client(creator: ${Name}Creator) -> None:
|
|
307
|
+
print(creator.deliver())
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
if __name__ == "__main__":
|
|
311
|
+
client(${Name}CreatorA())
|
|
312
|
+
client(${Name}CreatorB())
|
|
313
|
+
'''),
|
|
314
|
+
"kotlin": Template('''\
|
|
315
|
+
package com.example.patterns
|
|
316
|
+
|
|
317
|
+
interface ${Name}Product { fun operation(): String }
|
|
318
|
+
|
|
319
|
+
class ${Name}ProductA : ${Name}Product {
|
|
320
|
+
override fun operation() = "${Name}ProductA result"
|
|
321
|
+
}
|
|
322
|
+
class ${Name}ProductB : ${Name}Product {
|
|
323
|
+
override fun operation() = "${Name}ProductB result"
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
abstract class ${Name}Creator {
|
|
327
|
+
abstract fun createProduct(): ${Name}Product
|
|
328
|
+
fun deliver() = "Creator: ${dollar}{createProduct().operation()}"
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
class ${Name}CreatorA : ${Name}Creator() {
|
|
332
|
+
override fun createProduct() = ${Name}ProductA()
|
|
333
|
+
}
|
|
334
|
+
class ${Name}CreatorB : ${Name}Creator() {
|
|
335
|
+
override fun createProduct() = ${Name}ProductB()
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
fun main() {
|
|
339
|
+
println(${Name}CreatorA().deliver())
|
|
340
|
+
println(${Name}CreatorB().deliver())
|
|
341
|
+
}
|
|
342
|
+
'''),
|
|
343
|
+
"java": Template('''\
|
|
344
|
+
package com.example.patterns;
|
|
345
|
+
|
|
346
|
+
public interface ${Name}Product { String operation(); }
|
|
347
|
+
|
|
348
|
+
class ${Name}ProductA implements ${Name}Product {
|
|
349
|
+
public String operation() { return "${Name}ProductA result"; }
|
|
350
|
+
}
|
|
351
|
+
class ${Name}ProductB implements ${Name}Product {
|
|
352
|
+
public String operation() { return "${Name}ProductB result"; }
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
abstract class ${Name}Creator {
|
|
356
|
+
public abstract ${Name}Product createProduct();
|
|
357
|
+
public String deliver() { return "Creator: " + createProduct().operation(); }
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
class ${Name}CreatorA extends ${Name}Creator {
|
|
361
|
+
public ${Name}Product createProduct() { return new ${Name}ProductA(); }
|
|
362
|
+
}
|
|
363
|
+
class ${Name}CreatorB extends ${Name}Creator {
|
|
364
|
+
public ${Name}Product createProduct() { return new ${Name}ProductB(); }
|
|
365
|
+
public static void main(String[] args) {
|
|
366
|
+
System.out.println(new ${Name}CreatorA().deliver());
|
|
367
|
+
System.out.println(new ${Name}CreatorB().deliver());
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
'''),
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
# ---- DECORATOR -------------------------------------------------------------
|
|
374
|
+
PATTERNS["decorator"] = {
|
|
375
|
+
"python": Template('''\
|
|
376
|
+
#!/usr/bin/env python3
|
|
377
|
+
"""Decorator pattern — ${Name}."""
|
|
378
|
+
from abc import ABC, abstractmethod
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class ${Name}Component(ABC):
|
|
382
|
+
@abstractmethod
|
|
383
|
+
def execute(self) -> str: ...
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
class ${Name}ConcreteComponent(${Name}Component):
|
|
387
|
+
def execute(self) -> str:
|
|
388
|
+
return "base-result"
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
class ${Name}Decorator(${Name}Component, ABC):
|
|
392
|
+
def __init__(self, wrapped: ${Name}Component) -> None:
|
|
393
|
+
self._wrapped = wrapped
|
|
394
|
+
|
|
395
|
+
def execute(self) -> str:
|
|
396
|
+
return self._wrapped.execute()
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
class ${Name}LoggingDecorator(${Name}Decorator):
|
|
400
|
+
def execute(self) -> str:
|
|
401
|
+
result = super().execute()
|
|
402
|
+
print(f"[LOG] result={result!r}")
|
|
403
|
+
return result
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
class ${Name}CachingDecorator(${Name}Decorator):
|
|
407
|
+
def __init__(self, wrapped: ${Name}Component) -> None:
|
|
408
|
+
super().__init__(wrapped)
|
|
409
|
+
self._cache: str | None = None
|
|
410
|
+
|
|
411
|
+
def execute(self) -> str:
|
|
412
|
+
if self._cache is None:
|
|
413
|
+
self._cache = super().execute()
|
|
414
|
+
print("[CACHE] miss — computed")
|
|
415
|
+
else:
|
|
416
|
+
print("[CACHE] hit")
|
|
417
|
+
return self._cache
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
if __name__ == "__main__":
|
|
421
|
+
base = ${Name}ConcreteComponent()
|
|
422
|
+
cached = ${Name}CachingDecorator(base)
|
|
423
|
+
logged = ${Name}LoggingDecorator(cached)
|
|
424
|
+
print(logged.execute())
|
|
425
|
+
print(logged.execute()) # second call: cache hit
|
|
426
|
+
'''),
|
|
427
|
+
"kotlin": Template('''\
|
|
428
|
+
package com.example.patterns
|
|
429
|
+
|
|
430
|
+
interface ${Name}Component { fun execute(): String }
|
|
431
|
+
|
|
432
|
+
class ${Name}ConcreteComponent : ${Name}Component {
|
|
433
|
+
override fun execute() = "base-result"
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
open class ${Name}Decorator(private val wrapped: ${Name}Component) : ${Name}Component {
|
|
437
|
+
override fun execute() = wrapped.execute()
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
class ${Name}LoggingDecorator(wrapped: ${Name}Component) : ${Name}Decorator(wrapped) {
|
|
441
|
+
override fun execute(): String {
|
|
442
|
+
val r = super.execute()
|
|
443
|
+
println("[LOG] result=$r")
|
|
444
|
+
return r
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
class ${Name}CachingDecorator(wrapped: ${Name}Component) : ${Name}Decorator(wrapped) {
|
|
449
|
+
private var cache: String? = null
|
|
450
|
+
override fun execute(): String {
|
|
451
|
+
if (cache == null) { cache = super.execute(); println("[CACHE] miss") }
|
|
452
|
+
else println("[CACHE] hit")
|
|
453
|
+
return cache!!
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
fun main() {
|
|
458
|
+
val comp = ${Name}LoggingDecorator(${Name}CachingDecorator(${Name}ConcreteComponent()))
|
|
459
|
+
println(comp.execute())
|
|
460
|
+
println(comp.execute())
|
|
461
|
+
}
|
|
462
|
+
'''),
|
|
463
|
+
"java": Template('''\
|
|
464
|
+
package com.example.patterns;
|
|
465
|
+
|
|
466
|
+
public interface ${Name}Component { String execute(); }
|
|
467
|
+
|
|
468
|
+
class ${Name}ConcreteComponent implements ${Name}Component {
|
|
469
|
+
public String execute() { return "base-result"; }
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
abstract class ${Name}Decorator implements ${Name}Component {
|
|
473
|
+
protected final ${Name}Component wrapped;
|
|
474
|
+
protected ${Name}Decorator(${Name}Component wrapped) { this.wrapped = wrapped; }
|
|
475
|
+
public String execute() { return wrapped.execute(); }
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
class ${Name}LoggingDecorator extends ${Name}Decorator {
|
|
479
|
+
${Name}LoggingDecorator(${Name}Component w) { super(w); }
|
|
480
|
+
public String execute() {
|
|
481
|
+
String r = super.execute();
|
|
482
|
+
System.out.println("[LOG] result=" + r);
|
|
483
|
+
return r;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
class ${Name}CachingDecorator extends ${Name}Decorator {
|
|
488
|
+
private String cache;
|
|
489
|
+
${Name}CachingDecorator(${Name}Component w) { super(w); }
|
|
490
|
+
public String execute() {
|
|
491
|
+
if (cache == null) { cache = super.execute(); System.out.println("[CACHE] miss"); }
|
|
492
|
+
else System.out.println("[CACHE] hit");
|
|
493
|
+
return cache;
|
|
494
|
+
}
|
|
495
|
+
public static void main(String[] args) {
|
|
496
|
+
var c = new ${Name}LoggingDecorator(new ${Name}CachingDecorator(new ${Name}ConcreteComponent()));
|
|
497
|
+
System.out.println(c.execute());
|
|
498
|
+
System.out.println(c.execute());
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
'''),
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
# ---- COMMAND ---------------------------------------------------------------
|
|
505
|
+
PATTERNS["command"] = {
|
|
506
|
+
"python": Template('''\
|
|
507
|
+
#!/usr/bin/env python3
|
|
508
|
+
"""Command pattern — ${Name}."""
|
|
509
|
+
from abc import ABC, abstractmethod
|
|
510
|
+
from typing import List
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
class ${Name}Command(ABC):
|
|
514
|
+
@abstractmethod
|
|
515
|
+
def execute(self) -> None: ...
|
|
516
|
+
|
|
517
|
+
@abstractmethod
|
|
518
|
+
def undo(self) -> None: ...
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
class ${Name}Receiver:
|
|
522
|
+
def __init__(self) -> None:
|
|
523
|
+
self.state: List[str] = []
|
|
524
|
+
|
|
525
|
+
def add(self, item: str) -> None:
|
|
526
|
+
self.state.append(item)
|
|
527
|
+
print(f"Added {item!r}. State: {self.state}")
|
|
528
|
+
|
|
529
|
+
def remove(self, item: str) -> None:
|
|
530
|
+
self.state.remove(item)
|
|
531
|
+
print(f"Removed {item!r}. State: {self.state}")
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
class ${Name}AddCommand(${Name}Command):
|
|
535
|
+
def __init__(self, receiver: ${Name}Receiver, item: str) -> None:
|
|
536
|
+
self._receiver = receiver
|
|
537
|
+
self._item = item
|
|
538
|
+
|
|
539
|
+
def execute(self) -> None:
|
|
540
|
+
self._receiver.add(self._item)
|
|
541
|
+
|
|
542
|
+
def undo(self) -> None:
|
|
543
|
+
self._receiver.remove(self._item)
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
class ${Name}Invoker:
|
|
547
|
+
def __init__(self) -> None:
|
|
548
|
+
self._history: List[${Name}Command] = []
|
|
549
|
+
|
|
550
|
+
def run(self, cmd: ${Name}Command) -> None:
|
|
551
|
+
cmd.execute()
|
|
552
|
+
self._history.append(cmd)
|
|
553
|
+
|
|
554
|
+
def undo_last(self) -> None:
|
|
555
|
+
if self._history:
|
|
556
|
+
self._history.pop().undo()
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
if __name__ == "__main__":
|
|
560
|
+
receiver = ${Name}Receiver()
|
|
561
|
+
invoker = ${Name}Invoker()
|
|
562
|
+
invoker.run(${Name}AddCommand(receiver, "apple"))
|
|
563
|
+
invoker.run(${Name}AddCommand(receiver, "banana"))
|
|
564
|
+
invoker.undo_last()
|
|
565
|
+
'''),
|
|
566
|
+
"kotlin": Template('''\
|
|
567
|
+
package com.example.patterns
|
|
568
|
+
|
|
569
|
+
interface ${Name}Command { fun execute(); fun undo() }
|
|
570
|
+
|
|
571
|
+
class ${Name}Receiver {
|
|
572
|
+
val state = mutableListOf<String>()
|
|
573
|
+
fun add(item: String) { state.add(item); println("Added $item. State=$state") }
|
|
574
|
+
fun remove(item: String) { state.remove(item); println("Removed $item. State=$state") }
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
class ${Name}AddCommand(private val r: ${Name}Receiver, private val item: String) : ${Name}Command {
|
|
578
|
+
override fun execute() = r.add(item)
|
|
579
|
+
override fun undo() = r.remove(item)
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
class ${Name}Invoker {
|
|
583
|
+
private val history = mutableListOf<${Name}Command>()
|
|
584
|
+
fun run(cmd: ${Name}Command) { cmd.execute(); history.add(cmd) }
|
|
585
|
+
fun undoLast() { history.removeLastOrNull()?.undo() }
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
fun main() {
|
|
589
|
+
val r = ${Name}Receiver(); val inv = ${Name}Invoker()
|
|
590
|
+
inv.run(${Name}AddCommand(r, "apple"))
|
|
591
|
+
inv.run(${Name}AddCommand(r, "banana"))
|
|
592
|
+
inv.undoLast()
|
|
593
|
+
}
|
|
594
|
+
'''),
|
|
595
|
+
"java": Template('''\
|
|
596
|
+
package com.example.patterns;
|
|
597
|
+
import java.util.ArrayDeque;
|
|
598
|
+
import java.util.ArrayList;
|
|
599
|
+
import java.util.Deque;
|
|
600
|
+
import java.util.List;
|
|
601
|
+
|
|
602
|
+
public interface ${Name}Command { void execute(); void undo(); }
|
|
603
|
+
|
|
604
|
+
class ${Name}Receiver {
|
|
605
|
+
List<String> state = new ArrayList<>();
|
|
606
|
+
void add(String i) { state.add(i); System.out.println("Added " + i + " state=" + state); }
|
|
607
|
+
void remove(String i) { state.remove(i); System.out.println("Removed " + i + " state=" + state); }
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
class ${Name}AddCommand implements ${Name}Command {
|
|
611
|
+
private final ${Name}Receiver r; private final String item;
|
|
612
|
+
${Name}AddCommand(${Name}Receiver r, String item) { this.r = r; this.item = item; }
|
|
613
|
+
public void execute() { r.add(item); }
|
|
614
|
+
public void undo() { r.remove(item); }
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
class ${Name}Invoker {
|
|
618
|
+
private final Deque<${Name}Command> history = new ArrayDeque<>();
|
|
619
|
+
public void run(${Name}Command c) { c.execute(); history.push(c); }
|
|
620
|
+
public void undoLast() { if (!history.isEmpty()) history.pop().undo(); }
|
|
621
|
+
public static void main(String[] args) {
|
|
622
|
+
var r = new ${Name}Receiver(); var inv = new ${Name}Invoker();
|
|
623
|
+
inv.run(new ${Name}AddCommand(r, "apple"));
|
|
624
|
+
inv.run(new ${Name}AddCommand(r, "banana"));
|
|
625
|
+
inv.undoLast();
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
'''),
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
# ---- SINGLETON -------------------------------------------------------------
|
|
632
|
+
PATTERNS["singleton"] = {
|
|
633
|
+
"python": Template('''\
|
|
634
|
+
#!/usr/bin/env python3
|
|
635
|
+
"""Singleton pattern — ${Name}.
|
|
636
|
+
|
|
637
|
+
WARNING: Singleton is heavily overused. Prefer dependency injection wherever
|
|
638
|
+
possible. Use this pattern only for truly process-wide unique resources such
|
|
639
|
+
as a logging registry or a hardware interface.
|
|
640
|
+
"""
|
|
641
|
+
import threading
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
class ${Name}(metaclass=type):
|
|
645
|
+
_instance: "${Name} | None" = None
|
|
646
|
+
_lock: threading.Lock = threading.Lock()
|
|
647
|
+
|
|
648
|
+
def __new__(cls) -> "${Name}":
|
|
649
|
+
if cls._instance is None:
|
|
650
|
+
with cls._lock:
|
|
651
|
+
if cls._instance is None:
|
|
652
|
+
cls._instance = super().__new__(cls)
|
|
653
|
+
cls._instance._initialized = False
|
|
654
|
+
return cls._instance
|
|
655
|
+
|
|
656
|
+
def __init__(self) -> None:
|
|
657
|
+
if self._initialized:
|
|
658
|
+
return
|
|
659
|
+
self._initialized = True
|
|
660
|
+
self._data: dict = {}
|
|
661
|
+
|
|
662
|
+
# --- public API ---
|
|
663
|
+
def set(self, key: str, value: object) -> None:
|
|
664
|
+
self._data[key] = value
|
|
665
|
+
|
|
666
|
+
def get(self, key: str) -> object:
|
|
667
|
+
return self._data.get(key)
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
# Alternative: module-level instance (simpler, idiomatic Python)
|
|
671
|
+
class _${Name}Impl:
|
|
672
|
+
def __init__(self) -> None:
|
|
673
|
+
self._data: dict = {}
|
|
674
|
+
def set(self, key: str, value: object) -> None: self._data[key] = value
|
|
675
|
+
def get(self, key: str) -> object: return self._data.get(key)
|
|
676
|
+
|
|
677
|
+
# Prefer this over the class-based singleton above
|
|
678
|
+
${lname}_instance = _${Name}Impl()
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
if __name__ == "__main__":
|
|
682
|
+
a = ${Name}()
|
|
683
|
+
b = ${Name}()
|
|
684
|
+
a.set("x", 42)
|
|
685
|
+
assert b.get("x") == 42, "Must be same instance"
|
|
686
|
+
assert a is b
|
|
687
|
+
print("Singleton check passed — a is b:", a is b)
|
|
688
|
+
'''),
|
|
689
|
+
"kotlin": Template('''\
|
|
690
|
+
package com.example.patterns
|
|
691
|
+
|
|
692
|
+
// Kotlin object declaration is the idiomatic singleton.
|
|
693
|
+
// WARNING: Hard to mock in tests — prefer constructor injection.
|
|
694
|
+
object ${Name} {
|
|
695
|
+
private val data = mutableMapOf<String, Any?>()
|
|
696
|
+
fun set(key: String, value: Any?) { data[key] = value }
|
|
697
|
+
fun get(key: String): Any? = data[key]
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
fun main() {
|
|
701
|
+
${Name}.set("x", 42)
|
|
702
|
+
println("x = ${dollar}{${Name}.get("x")}")
|
|
703
|
+
}
|
|
704
|
+
'''),
|
|
705
|
+
"java": Template('''\
|
|
706
|
+
package com.example.patterns;
|
|
707
|
+
import java.util.HashMap;
|
|
708
|
+
import java.util.Map;
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Thread-safe lazy singleton via initialization-on-demand holder.
|
|
712
|
+
* WARNING: Singleton is frequently overused; prefer dependency injection.
|
|
713
|
+
*/
|
|
714
|
+
public final class ${Name} {
|
|
715
|
+
private final Map<String, Object> data = new HashMap<>();
|
|
716
|
+
|
|
717
|
+
private ${Name}() {}
|
|
718
|
+
|
|
719
|
+
private static final class Holder {
|
|
720
|
+
private static final ${Name} INSTANCE = new ${Name}();
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
public static ${Name} getInstance() { return Holder.INSTANCE; }
|
|
724
|
+
|
|
725
|
+
public void set(String key, Object value) { data.put(key, value); }
|
|
726
|
+
public Object get(String key) { return data.get(key); }
|
|
727
|
+
|
|
728
|
+
public static void main(String[] args) {
|
|
729
|
+
${Name}.getInstance().set("x", 42);
|
|
730
|
+
System.out.println("x = " + ${Name}.getInstance().get("x"));
|
|
731
|
+
System.out.println("same? " + (${Name}.getInstance() == ${Name}.getInstance()));
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
'''),
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
SINGLETON_WARNING = """\
|
|
738
|
+
WARNING: You generated a Singleton. Effective Design Patterns cautions that
|
|
739
|
+
Singleton is the most overused pattern in the GoF catalogue. Problems include:
|
|
740
|
+
|
|
741
|
+
- Global mutable state makes code hard to test and reason about.
|
|
742
|
+
- Hidden dependencies violate the explicit-dependencies principle.
|
|
743
|
+
- Concurrency issues arise when state is mutated from multiple threads.
|
|
744
|
+
|
|
745
|
+
Consider instead:
|
|
746
|
+
- Dependency Injection: pass the shared object as a constructor parameter.
|
|
747
|
+
- Module-level instance (Python): a single module import is naturally unique.
|
|
748
|
+
- IoC containers (Spring, Guice, Hilt): manage lifetime explicitly.
|
|
749
|
+
|
|
750
|
+
Use Singleton only for truly process-wide resources with no reasonable
|
|
751
|
+
alternative (e.g., a hardware driver, a logging sink, an OS-level handle).
|
|
752
|
+
"""
|
|
753
|
+
|
|
754
|
+
EXT = {"python": "py", "kotlin": "kt", "java": "java"}
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
def write(path: Path, content: str) -> None:
|
|
758
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
759
|
+
path.write_text(content)
|
|
760
|
+
print(f" Created: {path}")
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
def scaffold(pattern: str, class_name: str, lang: str, output_dir: Path) -> None:
|
|
764
|
+
if pattern == "singleton":
|
|
765
|
+
print("\n" + "=" * 70)
|
|
766
|
+
print(SINGLETON_WARNING.strip())
|
|
767
|
+
print("=" * 70 + "\n")
|
|
768
|
+
|
|
769
|
+
lang_templates = PATTERNS[pattern]
|
|
770
|
+
if lang not in lang_templates:
|
|
771
|
+
print(f"ERROR: lang '{lang}' not supported for pattern '{pattern}'.", file=sys.stderr)
|
|
772
|
+
sys.exit(1)
|
|
773
|
+
|
|
774
|
+
tmpl = lang_templates[lang]
|
|
775
|
+
ext = EXT[lang]
|
|
776
|
+
filename = f"{class_name}_{pattern}.{ext}"
|
|
777
|
+
ctx = {"Name": class_name, "lname": class_name.lower(), "dollar": "$"}
|
|
778
|
+
content = tmpl.substitute(ctx)
|
|
779
|
+
write(output_dir / filename, content)
|
|
780
|
+
|
|
781
|
+
print(f"\nGenerated {pattern} pattern for '{class_name}' ({lang}): {output_dir / filename}")
|
|
782
|
+
print("\nNext steps:")
|
|
783
|
+
print(f" 1. Rename {class_name}Strategy/Observer/etc. to match your domain.")
|
|
784
|
+
print(f" 2. Replace the placeholder execute() logic with real behaviour.")
|
|
785
|
+
print(f" 3. Wire into your application via dependency injection.\n")
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
def main() -> None:
|
|
789
|
+
supported = sorted(PATTERNS.keys())
|
|
790
|
+
parser = argparse.ArgumentParser(
|
|
791
|
+
description="Scaffold GoF design pattern boilerplate."
|
|
792
|
+
)
|
|
793
|
+
parser.add_argument("pattern", choices=supported, help=f"Pattern: {', '.join(supported)}")
|
|
794
|
+
parser.add_argument("class_name", metavar="ClassName", help="Base name for generated classes")
|
|
795
|
+
parser.add_argument("--lang", choices=["python", "kotlin", "java"], default="python")
|
|
796
|
+
parser.add_argument("--output-dir", default=".", type=Path)
|
|
797
|
+
args = parser.parse_args()
|
|
798
|
+
|
|
799
|
+
if not args.class_name[0].isupper():
|
|
800
|
+
print(f"ERROR: ClassName should be PascalCase (got '{args.class_name}').", file=sys.stderr)
|
|
801
|
+
sys.exit(1)
|
|
802
|
+
|
|
803
|
+
scaffold(args.pattern, args.class_name, args.lang, args.output_dir)
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
if __name__ == "__main__":
|
|
807
|
+
main()
|