|
| 1 | +--- |
| 2 | +title: Visitor |
| 3 | +category: Behavioral |
| 4 | +language: ko |
| 5 | +tag: |
| 6 | + - Gang of Four |
| 7 | +--- |
| 8 | + |
| 9 | +## 의도 |
| 10 | + |
| 11 | + |
| 12 | +객체 구조의 요소들에 대해 수행할 작업을 나타냅니다. 방문자(Visitor)는 |
| 13 | +연산하는 요소의 클래스를 변경하지 않고 새 연산을 정의할 수 있게 해줍니다. |
| 14 | +## 설명 |
| 15 | + |
| 16 | +살제 예제 |
| 17 | + |
| 18 | +> 육군 부대의 편성을 생각해보세요. 지휘관은 아래에 2명의 중사를 휘하에 두고 있고 각 중사들은 |
| 19 | +> 3명의 병사를 휘하에 두고 있습니다. 위계에 따라 방문자(Visitor) 패턴이 구현되는 것을 함께 생각하면, |
| 20 | +> 우리는 지휘관, 중사 병사 또는 그들 모두와 상호작용하는 새로운 게체를 쉽게 만들 수 있습니다. |
| 21 | +
|
| 22 | +쉽게 말하자면 |
| 23 | + |
| 24 | +> 방문자(Visitor) 패턴은 데이터 구조의 노드에서 수행할 수 있는 작업들을 정의합니다. |
| 25 | +
|
| 26 | +Wikipedia에 의하면 |
| 27 | + |
| 28 | +> 객체 지향 프로그래밍 및 소프트웨어 엔지니어링에에서, 방문자(Visitor) 패턴은 |
| 29 | +> 알고리즘이 동작하는 객체 구조로부터 알고리즘을 분리하는 것, |
| 30 | +> 이 분리를 통해, 구조의 변경 없이 기본 객체 구조에 새로운 연산을 추가할 수 있도록 하는 방법입니다. |
| 31 | +
|
| 32 | +**코드 예제** |
| 33 | + |
| 34 | +위의 육군 부대를 예로 들겠습니다. 먼저 `Unit` 추상화 클래스와 `UnitVisitor` 인터페이스가 있습니다. |
| 35 | + |
| 36 | +```java |
| 37 | +public abstract class Unit { |
| 38 | + |
| 39 | + private final Unit[] children; |
| 40 | + |
| 41 | + public Unit(Unit... children) { |
| 42 | + this.children = children; |
| 43 | + } |
| 44 | + |
| 45 | + public void accept(UnitVisitor visitor) { |
| 46 | + Arrays.stream(children).forEach(child -> child.accept(visitor)); |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +public interface UnitVisitor { |
| 51 | + |
| 52 | + void visit(Soldier soldier); |
| 53 | + |
| 54 | + void visit(Sergeant sergeant); |
| 55 | + |
| 56 | + void visit(Commander commander); |
| 57 | +} |
| 58 | +``` |
| 59 | + |
| 60 | +그리고 이를 구현한 `Commander`,`Sergant`,`Soilder` 구현 클래스입니다. |
| 61 | + |
| 62 | +```java |
| 63 | +public class Commander extends Unit { |
| 64 | + |
| 65 | + public Commander(Unit... children) { |
| 66 | + super(children); |
| 67 | + } |
| 68 | + |
| 69 | + @Override |
| 70 | + public void accept(UnitVisitor visitor) { |
| 71 | + visitor.visit(this); |
| 72 | + super.accept(visitor); |
| 73 | + } |
| 74 | + |
| 75 | + @Override |
| 76 | + public String toString() { |
| 77 | + return "commander"; |
| 78 | + } |
| 79 | +} |
| 80 | + |
| 81 | +public class Sergeant extends Unit { |
| 82 | + |
| 83 | + public Sergeant(Unit... children) { |
| 84 | + super(children); |
| 85 | + } |
| 86 | + |
| 87 | + @Override |
| 88 | + public void accept(UnitVisitor visitor) { |
| 89 | + visitor.visit(this); |
| 90 | + super.accept(visitor); |
| 91 | + } |
| 92 | + |
| 93 | + @Override |
| 94 | + public String toString() { |
| 95 | + return "sergeant"; |
| 96 | + } |
| 97 | +} |
| 98 | + |
| 99 | +public class Soldier extends Unit { |
| 100 | + |
| 101 | + public Soldier(Unit... children) { |
| 102 | + super(children); |
| 103 | + } |
| 104 | + |
| 105 | + @Override |
| 106 | + public void accept(UnitVisitor visitor) { |
| 107 | + visitor.visit(this); |
| 108 | + super.accept(visitor); |
| 109 | + } |
| 110 | + |
| 111 | + @Override |
| 112 | + public String toString() { |
| 113 | + return "soldier"; |
| 114 | + } |
| 115 | +} |
| 116 | +``` |
| 117 | + |
| 118 | +그리고 `UnitVisiotr`를 구현한 `CommanderVisitor`,`SergantVisitor`,`Soilder` 구현 클래스입니다. |
| 119 | + |
| 120 | +```java |
| 121 | +@Slf4j |
| 122 | +public class CommanderVisitor implements UnitVisitor { |
| 123 | + |
| 124 | + @Override |
| 125 | + public void visit(Soldier soldier) { |
| 126 | + // Do nothing |
| 127 | + } |
| 128 | + |
| 129 | + @Override |
| 130 | + public void visit(Sergeant sergeant) { |
| 131 | + // Do nothing |
| 132 | + } |
| 133 | + |
| 134 | + @Override |
| 135 | + public void visit(Commander commander) { |
| 136 | + LOGGER.info("Good to see you {}", commander); |
| 137 | + } |
| 138 | +} |
| 139 | + |
| 140 | +@Slf4j |
| 141 | +public class SergeantVisitor implements UnitVisitor { |
| 142 | + |
| 143 | + @Override |
| 144 | + public void visit(Soldier soldier) { |
| 145 | + // Do nothing |
| 146 | + } |
| 147 | + |
| 148 | + @Override |
| 149 | + public void visit(Sergeant sergeant) { |
| 150 | + LOGGER.info("Hello {}", sergeant); |
| 151 | + } |
| 152 | + |
| 153 | + @Override |
| 154 | + public void visit(Commander commander) { |
| 155 | + // Do nothing |
| 156 | + } |
| 157 | +} |
| 158 | + |
| 159 | +@Slf4j |
| 160 | +public class SoldierVisitor implements UnitVisitor { |
| 161 | + |
| 162 | + @Override |
| 163 | + public void visit(Soldier soldier) { |
| 164 | + LOGGER.info("Greetings {}", soldier); |
| 165 | + } |
| 166 | + |
| 167 | + @Override |
| 168 | + public void visit(Sergeant sergeant) { |
| 169 | + // Do nothing |
| 170 | + } |
| 171 | + |
| 172 | + @Override |
| 173 | + public void visit(Commander commander) { |
| 174 | + // Do nothing |
| 175 | + } |
| 176 | +} |
| 177 | +``` |
| 178 | + |
| 179 | +마지막으로, 우린 방문자(Visitor)의 위력을 볼 수 있습니다. |
| 180 | + |
| 181 | +```java |
| 182 | +commander.accept(new SoldierVisitor()); |
| 183 | +commander.accept(new SergeantVisitor()); |
| 184 | +commander.accept(new CommanderVisitor()); |
| 185 | +``` |
| 186 | + |
| 187 | +프로그램 실행 결과: |
| 188 | + |
| 189 | +``` |
| 190 | +Greetings soldier |
| 191 | +Greetings soldier |
| 192 | +Greetings soldier |
| 193 | +Greetings soldier |
| 194 | +Greetings soldier |
| 195 | +Greetings soldier |
| 196 | +Hello sergeant |
| 197 | +Hello sergeant |
| 198 | +Good to see you commander |
| 199 | +``` |
| 200 | + |
| 201 | +## 클래스 다이어그램 |
| 202 | + |
| 203 | + |
| 204 | + |
| 205 | +## 적용 가능성 |
| 206 | + |
| 207 | +일반적으로 방문자(Visitor) 패턴은 다음과 같은 상황에 사용할 수 있습니다. |
| 208 | + |
| 209 | +* 객체 구조에 다양한 인터페이스를 가진 객체의 클래스가 많이 포함되어 있고, 객체들의 구현 클래스에 따라 연산되게 하고 싶을 때 사용할 수 있습니다. |
| 210 | +* 객체 구조에 있는 객체들에 대해 개별적이고 서로 연관이 없는 연산들을 수행하고 싶을 때, 이러한 연산들로 인해 클래스가 "오염(Polluting)"되는 것을 방지하고자 합니다. 이럴 때, 방문자(Visitor)를 사용해 관련 연산을 하나의 클래스에 정의하여 유지할 수 있습니다. 객체 구조가 여러 응용 프로그램에서 공유되는 경우에는 방문자(Visitor)를 사용해 필요한 응용 프로그램에만 연산을 추가할 수 있습니다. |
| 211 | +* 객체 구조를 정의하는 클래스들은 거의 변경되지 않지만, 종종 새로운 연산을 정의해야 할 때가 있습니다. 객체 구조 클래스들을 변경하려면 모둔 방문자(Visitor)에 데한 인터페이스를 재정의해야 하므로 비용이 많이 들수 있습니다. 객체 구조 클래스가 자주 변경되는 경우 해당 클래스의 연산을 정의하는 것이 더 나을 수 있습니다. |
| 212 | + |
| 213 | +## 튜토리얼 |
| 214 | + |
| 215 | +* [Refactoring Guru](https://refactoring.guru/design-patterns/visitor) |
| 216 | +* [Dzone](https://dzone.com/articles/design-patterns-visitor) |
| 217 | +* [Sourcemaking](https://sourcemaking.com/design_patterns/visitor) |
| 218 | + |
| 219 | +## 실제 사례 |
| 220 | + |
| 221 | +* [Apache Wicket](https://github.com/apache/wicket) 구성 요소, [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java)를 참조하세요. |
| 222 | +* [javax.lang.model.element.AnnotationValue](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/AnnotationValue.html) 그리고 [AnnotationValueVisitor](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/AnnotationValueVisitor.html) |
| 223 | +* [javax.lang.model.element.Element](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/Element.html) 그리고 [Element Visitor](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/ElementVisitor.html) |
| 224 | +* [java.nio.file.FileVisitor](http://docs.oracle.com/javase/8/docs/api/java/nio/file/FileVisitor.html) |
| 225 | + |
| 226 | +## 크레딧 |
| 227 | + |
| 228 | +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) |
| 229 | +* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) |
| 230 | +* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) |
0 commit comments