1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
| // Visitor 패턴의 우아함
// 1. Visitor 인터페이스
interface ShapeVisitor {
void visit(Circle circle);
void visit(Rectangle rectangle);
void visit(Triangle triangle);
void visit(CompoundShape compoundShape);
}
// 2. Element 인터페이스
interface Shape {
void accept(ShapeVisitor visitor);
String getName();
}
// 3. Concrete Elements
class Circle implements Shape {
private final double radius;
private final Point center;
public Circle(double radius, Point center) {
this.radius = radius;
this.center = center;
}
@Override
public void accept(ShapeVisitor visitor) {
visitor.visit(this); // Double Dispatch
}
@Override
public String getName() {
return "Circle";
}
public double getRadius() { return radius; }
public Point getCenter() { return center; }
}
class Rectangle implements Shape {
private final double width;
private final double height;
private final Point topLeft;
public Rectangle(double width, double height, Point topLeft) {
this.width = width;
this.height = height;
this.topLeft = topLeft;
}
@Override
public void accept(ShapeVisitor visitor) {
visitor.visit(this);
}
@Override
public String getName() {
return "Rectangle";
}
public double getWidth() { return width; }
public double getHeight() { return height; }
public Point getTopLeft() { return topLeft; }
}
class Triangle implements Shape {
private final Point[] vertices;
public Triangle(Point p1, Point p2, Point p3) {
this.vertices = new Point[]{p1, p2, p3};
}
@Override
public void accept(ShapeVisitor visitor) {
visitor.visit(this);
}
@Override
public String getName() {
return "Triangle";
}
public Point[] getVertices() { return vertices.clone(); }
}
// Composite 패턴과 결합
class CompoundShape implements Shape {
private final List<Shape> children;
private final String name;
public CompoundShape(String name) {
this.name = name;
this.children = new ArrayList<>();
}
public void addShape(Shape shape) {
children.add(shape);
}
@Override
public void accept(ShapeVisitor visitor) {
visitor.visit(this);
// 자식 Shape들도 방문
for (Shape child : children) {
child.accept(visitor);
}
}
@Override
public String getName() {
return name;
}
public List<Shape> getChildren() {
return new ArrayList<>(children);
}
}
// 4. Concrete Visitors - 다양한 연산 구현
class AreaCalculatorVisitor implements ShapeVisitor {
private double totalArea = 0;
private final Map<String, Double> areaByShape = new HashMap<>();
@Override
public void visit(Circle circle) {
double area = Math.PI * circle.getRadius() * circle.getRadius();
totalArea += area;
areaByShape.put("Circle_" + System.identityHashCode(circle), area);
System.out.printf("📐 Circle area: %.2f (radius: %.2f)\n", area, circle.getRadius());
}
@Override
public void visit(Rectangle rectangle) {
double area = rectangle.getWidth() * rectangle.getHeight();
totalArea += area;
areaByShape.put("Rectangle_" + System.identityHashCode(rectangle), area);
System.out.printf("📐 Rectangle area: %.2f (%.2f x %.2f)\n",
area, rectangle.getWidth(), rectangle.getHeight());
}
@Override
public void visit(Triangle triangle) {
// 신발끈 공식 (Shoelace formula) 사용
Point[] vertices = triangle.getVertices();
double area = Math.abs(
(vertices[0].x * (vertices[1].y - vertices[2].y) +
vertices[1].x * (vertices[2].y - vertices[0].y) +
vertices[2].x * (vertices[0].y - vertices[1].y)) / 2.0
);
totalArea += area;
areaByShape.put("Triangle_" + System.identityHashCode(triangle), area);
System.out.printf("📐 Triangle area: %.2f\n", area);
}
@Override
public void visit(CompoundShape compoundShape) {
System.out.printf("📐 Compound shape '%s' contains %d children\n",
compoundShape.getName(), compoundShape.getChildren().size());
}
public double getTotalArea() {
return totalArea;
}
public Map<String, Double> getAreaByShape() {
return new HashMap<>(areaByShape);
}
public void reset() {
totalArea = 0;
areaByShape.clear();
}
}
class DrawingVisitor implements ShapeVisitor {
private final StringBuilder canvas = new StringBuilder();
private int indentLevel = 0;
@Override
public void visit(Circle circle) {
addIndent();
canvas.append(String.format("🔵 Drawing Circle: center(%.1f, %.1f), radius=%.1f\n",
circle.getCenter().x, circle.getCenter().y, circle.getRadius()));
}
@Override
public void visit(Rectangle rectangle) {
addIndent();
canvas.append(String.format("⬜ Drawing Rectangle: top-left(%.1f, %.1f), size=%.1fx%.1f\n",
rectangle.getTopLeft().x, rectangle.getTopLeft().y,
rectangle.getWidth(), rectangle.getHeight()));
}
@Override
public void visit(Triangle triangle) {
addIndent();
Point[] vertices = triangle.getVertices();
canvas.append(String.format("🔺 Drawing Triangle: vertices[(%.1f,%.1f), (%.1f,%.1f), (%.1f,%.1f)]\n",
vertices[0].x, vertices[0].y,
vertices[1].x, vertices[1].y,
vertices[2].x, vertices[2].y));
}
@Override
public void visit(CompoundShape compoundShape) {
addIndent();
canvas.append(String.format("📦 Drawing Compound Shape: '%s'\n", compoundShape.getName()));
indentLevel++;
}
private void addIndent() {
for (int i = 0; i < indentLevel; i++) {
canvas.append(" ");
}
}
public String getCanvas() {
return canvas.toString();
}
public void reset() {
canvas.setLength(0);
indentLevel = 0;
}
}
class SVGExportVisitor implements ShapeVisitor {
private final StringBuilder svg = new StringBuilder();
private boolean headerAdded = false;
public SVGExportVisitor() {
addSVGHeader();
}
@Override
public void visit(Circle circle) {
if (!headerAdded) addSVGHeader();
svg.append(String.format(
" <circle cx=\"%.1f\" cy=\"%.1f\" r=\"%.1f\" fill=\"blue\" stroke=\"black\" stroke-width=\"1\"/>\n",
circle.getCenter().x, circle.getCenter().y, circle.getRadius()
));
}
@Override
public void visit(Rectangle rectangle) {
if (!headerAdded) addSVGHeader();
svg.append(String.format(
" <rect x=\"%.1f\" y=\"%.1f\" width=\"%.1f\" height=\"%.1f\" fill=\"red\" stroke=\"black\" stroke-width=\"1\"/>\n",
rectangle.getTopLeft().x, rectangle.getTopLeft().y,
rectangle.getWidth(), rectangle.getHeight()
));
}
@Override
public void visit(Triangle triangle) {
if (!headerAdded) addSVGHeader();
Point[] vertices = triangle.getVertices();
String points = String.format("%.1f,%.1f %.1f,%.1f %.1f,%.1f",
vertices[0].x, vertices[0].y,
vertices[1].x, vertices[1].y,
vertices[2].x, vertices[2].y);
svg.append(String.format(
" <polygon points=\"%s\" fill=\"green\" stroke=\"black\" stroke-width=\"1\"/>\n",
points
));
}
@Override
public void visit(CompoundShape compoundShape) {
svg.append(String.format(" <!-- Compound Shape: %s -->\n", compoundShape.getName()));
}
private void addSVGHeader() {
svg.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
svg.append("<svg width=\"800\" height=\"600\" xmlns=\"http://www.w3.org/2000/svg\">\n");
headerAdded = true;
}
public String getSVG() {
return svg.toString() + "</svg>\n";
}
}
class ValidationVisitor implements ShapeVisitor {
private final List<String> errors = new ArrayList<>();
private final List<String> warnings = new ArrayList<>();
@Override
public void visit(Circle circle) {
if (circle.getRadius() <= 0) {
errors.add("Circle has invalid radius: " + circle.getRadius());
}
if (circle.getRadius() > 1000) {
warnings.add("Circle has very large radius: " + circle.getRadius());
}
}
@Override
public void visit(Rectangle rectangle) {
if (rectangle.getWidth() <= 0 || rectangle.getHeight() <= 0) {
errors.add("Rectangle has invalid dimensions: " +
rectangle.getWidth() + "x" + rectangle.getHeight());
}
if (rectangle.getWidth() * rectangle.getHeight() > 1000000) {
warnings.add("Rectangle has very large area");
}
}
@Override
public void visit(Triangle triangle) {
Point[] vertices = triangle.getVertices();
// 삼각형의 세 점이 일직선상에 있는지 확인
double area = Math.abs(
(vertices[0].x * (vertices[1].y - vertices[2].y) +
vertices[1].x * (vertices[2].y - vertices[0].y) +
vertices[2].x * (vertices[0].y - vertices[1].y)) / 2.0
);
if (area < 0.001) {
errors.add("Triangle vertices are collinear");
}
}
@Override
public void visit(CompoundShape compoundShape) {
if (compoundShape.getChildren().isEmpty()) {
warnings.add("Compound shape '" + compoundShape.getName() + "' is empty");
}
}
public List<String> getErrors() {
return new ArrayList<>(errors);
}
public List<String> getWarnings() {
return new ArrayList<>(warnings);
}
public boolean isValid() {
return errors.isEmpty();
}
public void reset() {
errors.clear();
warnings.clear();
}
}
// Point 클래스
class Point {
final double x, y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
}
// 사용 예시
class VisitorPatternDemo {
public static void main(String[] args) {
// 도형 생성
Circle circle = new Circle(5.0, new Point(10, 10));
Rectangle rectangle = new Rectangle(8.0, 6.0, new Point(0, 0));
Triangle triangle = new Triangle(
new Point(0, 0), new Point(4, 0), new Point(2, 3)
);
// 복합 도형 생성
CompoundShape group = new CompoundShape("Main Group");
group.addShape(circle);
group.addShape(rectangle);
group.addShape(triangle);
System.out.println("=== Visitor Pattern Demo ===\n");
// 1. 넓이 계산
System.out.println("1. Area Calculation:");
AreaCalculatorVisitor areaCalculator = new AreaCalculatorVisitor();
group.accept(areaCalculator);
System.out.printf("Total area: %.2f\n\n", areaCalculator.getTotalArea());
// 2. 그리기
System.out.println("2. Drawing:");
DrawingVisitor drawer = new DrawingVisitor();
group.accept(drawer);
System.out.println(drawer.getCanvas());
// 3. SVG 내보내기
System.out.println("3. SVG Export:");
SVGExportVisitor svgExporter = new SVGExportVisitor();
group.accept(svgExporter);
System.out.println(svgExporter.getSVG());
// 4. 유효성 검증
System.out.println("4. Validation:");
ValidationVisitor validator = new ValidationVisitor();
group.accept(validator);
if (validator.isValid()) {
System.out.println("[OK] All shapes are valid");
} else {
System.out.println("[Error] Validation errors found:");
validator.getErrors().forEach(error -> System.out.println(" - " + error));
}
if (!validator.getWarnings().isEmpty()) {
System.out.println("[Warning] Warnings:");
validator.getWarnings().forEach(warning -> System.out.println(" - " + warning));
}
}
}
|