[Design Pattern] 행위(Behavioral) 패턴
120885 단어 Design PatternDesign Pattern
13. 책임 연쇄 패턴 (Chain Of Responsibility) 패턴
- 요청을 보내는 쪽(sender)과 요청을 처리하는 쪽(receiver)의 분리하는 패턴
- 클라이언트의 요청을 처리하기 위해 객체들을 체인 형태로 연결하여 결합력을 낮추기 위해 사용한다.
- 핸들러 체인을 사용해서 요청을 처리한다.
구현 방법
// Handler
public abstract class RequestHandler {
private RequestHandler nextHandler;
public RequestHandler() {
this(null);
}
public RequestHandler(RequestHandler nextHandler) {
this.nextHandler = nextHandler;
}
public void handle(String request) {
if (nextHandler != null) {
nextHandler.handle(request);
}
}
}
// ConcreteHandler 1
public class Logging1RequestHandler extends RequestHandler {
public LoggingRequestHandler(RequestHandler nextHandler) {
super(nextHandler);
}
@Override
public void handle(String request) {
System.out.println("log1...");
super.handle(request);
}
}
// ConcreteHandler 2
public class Logging2RequestHandler extends RequestHandler {
public LoggingRequestHandler(RequestHandler nextHandler) {
super(nextHandler);
}
@Override
public void handle(String request) {
System.out.println("log2...");
super.handle(request);
}
}
// Client
public class Client {
private RequestHandler requestHandler;
public Client(RequestHandler requestHandler) {
this.requestHandler = requestHandler;
}
public void invoke() {
System.out.println("start...");
requestHandler.handle("request");
}
}
public static void main(String[] args) {
RequestHandler chain = new Logging1RequestHandler(new Logging2RequestHandler());
Client client = new Client(chain);
client.invoke();
}
장단점
장점
- 클라이언트 코드를 변경하지 않고 새로운 핸들러를 체인에 추가할 수 있다.
- 각각의 체인은 자신이 해야하는 일만 한다.
- 체인을 다양한 방법으로 구성할 수 있다.
단점
- 디버깅 및 테스트가 쉽지 않을수 있다.
- 체인이 적절하게 구성되어 있지 않을 경우 요청이 올바르게 처리되지 않을수있다.
사용하는곳
- 자바 서블릿 필터
- 스프링 시큐리티 필터
14. 커맨드 (Command) 패턴
- 요청을 캡슐화 하여 호출자(invoker)와 수신자(receiver)를 분리하는 패턴.
- 요청을 처리하는 방법이 바뀌더라도, 호출자의 코드는 변경되지 않는다.
구현 방법
// Invoker
public class Button {
private Command command;
public Button(Command command) {
this.command = command;
}
public void press() {
command.execute();
}
public void changeCommand(Command command) {
this.command = command;
}
}
// Command
public interface Command {
void execute();
}
// Concrete Command 1
public class LightOnCommand implements Command {
private final Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
// Concrete Command 2
public class DoorOpenCommand implements Command {
private final Door door;
public DoorOpenCommand(Door door) {
this.door = door;
}
@Override
public void execute() {
door.open();
}
}
// Receiver 1
public class Light {
public void on() {
System.out.println("불을 켭니다.");
}
}
// Receiver 2
public class Door {
public void open() {
System.out.println("문을 엽니다.");
}
}
public static void main(String[] args) {
Button button = new Button(new LightOnCommand(new Light()));
button.press();
button.changeCommand(new DoorOpenCommand(new Door()));
button.press();
}
장단점
장점
- 기존 코드를 변경하지 않고 새로운 커맨드를 만들 수 있다.
- 수신자의 코드가 변경되어도 호출자의 코드는 변경되지 않는다.
- 커맨드 객체를 로깅, DB에 저장, 네트워크로 전송 하는 등 다양한 방법으로 활용할 수도 있다.
단점
- 코드가 복잡하고 클래스가 많아진다.
사용하는곳
- 자바 Runnable, 람다
- 스프링 SimpleJdbcInsert, SimpleJdbcCall
15. 인터프리터 (Interpreter) 패턴
- 자주 등장하는 문제를 간단한 언어로 정의하고 재사용하는 패턴.
- 반복되는 문제 패턴을 언어 또는 문법으로 정의하고 확장할 수 있다.
- Expression
- Abstract Syntax Tree(AST) 의 모든 노드에서 사용할 Interpret 작업을 정의한다.
- Terminal Expression
- 그 자체로 종료가 되는 Expression
- NoNTerminal Expression
- 재귀적으로 다른 Expression 을 참조하고 있는 Expression
구현 방법
// Expression
public interface Expression {
boolean interpret(String context);
}
// TerminalExpression
public class TerminalExpression implements Expression {
private String data;
public TerminalExpression(String data) {
this.data = data;
}
@Override
public boolean interpret(String context) {
return context.contains(data);
}
}
// NonTerminalExpression 1
public class AndExpression implements Expression {
private Expression expr1;
private Expression expr2;
public AndExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) && expr2.interpret(context);
}
}
// NonTerminalExpression 2
public class OrExpression implements Expression {
private Expression expr1;
private Expression expr2;
public OrExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) || expr2.interpret(context);
}
}
public class Client {
public static void main(String[] args) {
Expression isMale = getMaleExpression();
Expression isMarriedWoman = getMarriedWomanExpression();
System.out.println("John is male? " + isMale.interpret("John male"));
System.out.println("Julie is a married women? " + isMarriedWoman.interpret("Married Julie"));
}
public static Expression getMaleExpression() {
Expression robert = new TerminalExpression("Robert");
Expression john = new TerminalExpression("John");
return new OrExpression(robert, john);
}
public static Expression getMarriedWomanExpression() {
Expression julie = new TerminalExpression("Julie");
Expression married = new TerminalExpression("Married");
return new AndExpression(julie, married);
}
}
장단점
장점
- 자주 등장하는 문제 패턴을 언어와 문법으로 정의할 수 있다.
- 기존 코드를 변경하지 않고 새로운 Expression 을 추가할 수 있다.
단점
- 복잡한 문법을 표현하려면 Expression 과 Parser 가 복잡해진다.
사용하는곳
- Java 컴파일러, 정규표현식
- SPEL (Spring Expression Language)
16. 반복자 (Iterator) 패턴
- 집합 객체 내부 구조를 노출시키지 않고 순회 하는 방법을 제공하는 패턴.
- 집합 객체를 순회하는 클라이언트 코드를 변경하지 않고 다양한 순회 방법을 제공할 수 있다.
- Iterator: 컬렉션의 요소들을 순서대로 검색하기 위한 인터페이스
- ConcreteIterator: iterator 인터페이스 구현체
- Aggregate: 여러 요소들로 구성된 컬렉션 인터페이스
- ConcreteAggregate: Aggregate 인터페이스 구현체
구현 방법
// Iterator
public interface Iterator<T> {
boolean hasNext();
T next();
}
// ConcreteIterator
public class BookIterator implements Iterator<Book> {
private final BookAggregate bookAggregate;
private int index;
public BookIterator(BookAggregate bookAggregate) {
this.bookAggregate = bookAggregate;
this.index = 0;
}
@Override
public boolean hasNext() {
return index < bookAggregate.getLength();
}
@Override
public Book next() {
Book book = bookAggregate.getBookAt(index);
index++;
return book;
}
}
// Aggregate
public interface Aggregate {
Iterator<T> iterator();
}
// ConcreteAggregate
public class BookAggregate implements Aggregate {
private final Book[] books;
private int last = 0;
public BookAggregate(int maxsize) {
this.books = new Book[maxsize];
}
public Book getBookAt(int index) {
return books[index];
}
public void appendBook(Book book) {
this.books[last] = book;
last++;
}
public int getLength() {
return last;
}
@Override
public Iterator<T> iterator() {
return new BookIterator(this);
}
}
// Target
public class Book {
private final String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static void main(String[] args) {
BookAggregate bookAggregate = new BookAggregate(3);
bookAggregate.appendBook(new Book("자바"));
bookAggregate.appendBook(new Book("파이썬"));
bookAggregate.appendBook(new Book("golang"));
Iterator<Book> iterator = bookAggregate.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next().getName());
}
}
장단점
장점
- 집합 객체가 가지고 있는 객체들에 손쉽게 접근할 수 있다.
- 일관된 인터페이스를 사용해 여러 형태의 집합 구조를 순회할 수 있다
단점
- 클래스가 늘어나고 복잡도가 증가한다.
사용하는곳
- Java, Enumeration, Iterator
- Java StAX (Streaming API for XML)의 Iterator 기반 API
- XmlEventReader, XmlEventWriter
- Spring, CompositeIterator
17. 중재자 (Mediator) 패턴
- 여러 객체들이 소통하는 방법을 캡슐화하는 패턴.
- 여러 컴포넌트간의 결합도를 중재자를 통해 낮출 수 있다.
- M:N의 관계에서 M:1의 관계로 복잡도를 떨어뜨려 유지 보수 및 재사용, 확장성에 유리한 패턴이다.
구현 방법
// Mediator
public interface Mediator {
void addUser(Colleague user);
void sendMessage(String message, Colleague user);
}
// ConcreteMediator
public class ConcreteMediator implements Mediator {
private final List<Colleague> users;
public ConcreteMediator() {
this.users = new ArrayList<>();
}
@Override
public void addUser(Colleague user) {
this.users.add(user);
}
@Override
public void sendMessage(String message, Colleague user) {
for (Colleague u : this.users) {
if (u != user) {
u.receive(message);
}
}
}
}
// Colleague
public abstract class Colleague {
protected Mediator mediator;
protected String name;
public Colleague(Mediator mediator, String name) {
this.mediator = mediator;
this.name = name;
}
public abstract void send(String msg);
public abstract void receive(String msg);
}
// ColleagueA
public class ConcreteColleague extends Colleague {
public ConcreteColleague(Mediator mediator, String name) {
super(mediator, name);
}
@Override
public void send(String msg) {
System.out.println(this.name + ": Sending Message=" + msg);
mediator.sendMessage(msg, this);
}
@Override
public void receive(String msg) {
System.out.println(this.name + ": Received Message:" + msg);
}
}
public static void main(String[] args) {
Mediator mediator = new ConcreteMediator();
Colleague user1 = new ConcreteColleague(mediator, "aaa");
Colleague user2 = new ConcreteColleague(mediator, "bbb");
Colleague user3 = new ConcreteColleague(mediator, "ccc");
Colleague user4 = new ConcreteColleague(mediator, "ddd");
mediator.addUser(user1);
mediator.addUser(user2);
mediator.addUser(user3);
mediator.addUser(user4);
user1.send("message....");
}
장단점
장점
- 컴포넌트 코드를 변경하지 않고 새로운 중재자를 만들어 사용할 수 있다.
- 각각의 컴포넌트 코드를 보다 간결하게 유지할 수 있다.
단점
- 중재자 역할을 하는 클래스의 복잡도와 결합도가 증가한다.
사용하는곳
- Java, ExecutorService, Executor
- Spring, DispatcherServlet
퍼사드 패턴 vs 중재자 패턴
- 퍼사드 패턴은 서브 시스템 의존성을 간단한 인터페이스로 추상화하여 상호 작용을 캡슐화 하므로 단방향이다.
- 중재자 패턴은 여러 컴포넌트간의 결합도를 중재자를 통해 낮추는것으로 양방향이다.
18. 메멘토 (Memento) 패턴
- 캡슐화를 유지하면서 객체 내부 상태를 외부에 저장하는 방법.
- 객체 상태를 외부에 저장했다가 해당 상태로 다시 복구할 수 있다.
- 토큰이라고 보면 될 것 같다.
- CareTaker
- Memento 의 보관을 책임지며 메멘토의 내용을 검사하거나 그 내용을 건드리지는 않는다.
- Originator
- 자체적으로 현재의 state 를 저장하는 Memento 객체를 생성할 수 있다.
- state 를 복원하기 위해 Memento 를 사용한다.
- Memento
- Originator 의 state 에 대한 스냅샷 역할을 하는 객체
- 특정 시점의 상태 정보를 저장하므로 Immutable 해야함.
구현 방법
// CareTaker
public class Storage {
private final Stack<GameSave> history = new Stack<>();
public void save(GameSave gameSave) {
history.push(gameSave);
}
public GameSave load() {
if (!stack.empty()) {
return history.pop();
}
throw new IllegalStateException();
}
}
// Originator
public class Game {
private int level;
private int score;
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public GameSave save() {
return new GameSave(level, score);
}
public void restore(GameSave gameSave) {
this.level = gameSave.getLevel();
this.score = gameSave.getScore();
}
}
// Memento
public final class GameSave {
private final int level;
private final int score;
public GameSave(int level, int score) {
this.level = level;
this.score = score;
}
public int getLevel() {
return level;
}
public int getScore() {
return score;
}
}
public static void main(String[] args) {
Storage storage = new Storage();
Game game = new Game();
game.setLevel(100);
game.setScore(3);
GameSave gameSave = game.save();
storage.save(gameSave);
game.setLevel(200);
game.setScore(6);
GameSave loadedGameSave = storage.load();
game.restore(loadedGameSave);
System.out.println(game.getLevel()); // 100
System.out.println(game.getScore()); // 3
}
장단점
장점
- 캡슐화를 지키면서 상태 객체 상태 스냅샷을 만들 수 있다.
- 객체 상태 저장하고 또는 복원하는 역할을 CareTaker 에게 위임할 수 있다.
- 객체 상태가 바뀌어도 클라이언트 코드는 변경되지 않는다.
단점
- 많은 정보를 저장하는 Memento 를 자주 생성하는 경우 메모리 사용량에 많은 영향을 줄 수 있다.
19. 옵저버 (Observer) 패턴
- 다수의 객체가 특정 객체 상태 변화를 감지하고 알림을 받는 패턴.
- 발행(publish)-구독(subscribe) 패턴을 구현할 수 있다.
구현 방법
// Subject
public class ChatServer {
private Map<String, List<Subscriber>> subscribers = new HashMap<>();
public void register(String subject, Subscriber subscriber) {
if (this.subscribers.containsKey(subject)) {
this.subscribers.get(subject).add(subscriber);
} else {
List<Subscriber> list = new ArrayList<>();
list.add(subscriber);
this.subscribers.put(subject, list);
}
}
public void unregister(String subject, Subscriber subscriber) {
if (this.subscribers.containsKey(subject)) {
this.subscribers.get(subject).remove(subscriber);
}
}
public void sendMessage(User sender, String subject, String message) {
if (this.subscribers.containsKey(subject)) {
String userMessage = "[ send ] " + sender.getName() + ": " + message;
this.subscribers.get(subject).forEach(s -> s.handleMessage(userMessage));
}
}
}
// Observer
public interface Subscriber {
void handleMessage(String message);
}
// ConcreteObserver
public class User implements Subscriber {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void handleMessage(String message) {
System.out.println("[handle] " + name + ": " + message);
}
}
public static void main(String[] args) {
ChatServer chatServer = new ChatServer();
User user1 = new User("aaa");
User user2 = new User("bbb");
chatServer.register("subject1", user1);
chatServer.register("subject1", user2);
chatServer.register("subject2", user1);
chatServer.register("subject2", user2);
chatServer.sendMessage(user1, "subject1", "subject1 + message1...");
chatServer.sendMessage(user2, "subject1", "subject1 + message2...");
chatServer.unregister("subject2", user1);
chatServer.sendMessage(user2, "subject2", "subject2 + message3...");
}
>> console <<
[ send ] aaa: subject1 + message1...
[handle] aaa: subject1 + message1...
[handle] bbb: subject1 + message1...
[ send ] bbb: subject1 + message2...
[handle] aaa: subject1 + message2...
[handle] bbb: subject1 + message2...
[ send ] bbb: subject2 + message3..
[handle] bbb: subject2 + message3...
장단점
장점
- 상태를 변경하는 객체(publisher)와 변경을 감지하는 객체(subscriber)의 관계를 느슨하게 유지할 수 있다.
- Subject 의 상태 변경을 주기적으로 조회하지 않고 자동으로 감지할 수 있다.
- 런타임에 옵저버를 추가하거나 제거할 수 있다.
단점
- 복잡도가 증가한다.
- 다수의 Observer 객체를 등록 이후 해지 않는다면 memory leak 이 발생할 수도 있다
사용하는곳
- Java, Observable 과 Observer (자바 9부터 deprecated)
- Java 9 이후, PropertyChangeListener, PropertyChangeEvent
- Java 9 이후, Flow API
- Spring, ApplicationContext ApplicationEvent
20. 상태 (State) 패턴
- 객체 내부 상태 변경에 따라 객체의 행동이 달라지는 패턴.
- 상태에 특화된 행동들을 분리해 낼 수 있으며, 새로운 행동을 추가하더라도 다른 행동에 영향을 주지 않는다.
- 상태를 객체화 하여 상태가 행동을 할 수 있도록 위임한다.
- 상태 처리 방법
- State 에 Context 를 필드로 주입받는다.
- State 메서드에 Context 를 전달한다.
- State 메서드에서 처리후에 다른 State 를 리턴한다.
- State 에 Context 를 필드로 주입받는다.
- State 메서드에 Context 를 전달한다.
- State 메서드에서 처리후에 다른 State 를 리턴한다.
구현 방법
// Context
public class Light {
private State state;
public Light() {
this.state = new Off();
}
public void press() {
this.state = state.press();
}
public void changeState(State state) {
this.state = state;
}
}
// State
public interface State {
State press();
}
// ConcreteState 1
public class On implements State {
public State press() {
System.out.println("On -> Off");
return new Off();
}
}
// ConcreteState 2
public class Off implements State {
public State press() {
System.out.println("Off -> On");
return new On();
}
}
public static void main(String[] args) {
Light light = new Light(); // State: Off
light.press(); // Off -> On 출력, State: On
light.press(); // On -> Off 출력, State: Off
light.changeState(new On()); // State: On
light.press(); // On -> Off 출력, State: Off
}
장단점
장점
- 상태에 따른 동작을 개별 클래스로 옮겨서 관리할 수 있다.
- 기존의 특정 상태에 따른 동작을 변경하지 않고 새로운 상태에 다른 동작을 추가할 수 있다.
- 코드 복잡도를 줄일 수 있다
단점
- 복잡도가 증가한다.
21. 전략 (Strategy) 패턴
- 여러 알고리즘을 캡슐화하고 상호 교환 가능하게 만드는 패턴.
- 컨텍스트에서 사용할 알고리즘을 클라이언트 선택한다.
- Client 가 Strategy 를 주입해주는 방식
- 생성자를 통해 주입
- 메서드 파라미터로 주입
- 생성자를 통해 주입
- 메서드 파라미터로 주입
구현 방법
// Context
public class Car {
public void drive(Speed speed) {
speed.drive();
}
}
// Strategy
public interface Speed {
void drive();
}
// ConcreteStrategy 1
public class Normal implements Speed {
@Override
public void drive() {
System.out.println("drive speed: normal...");
}
}
// ConcreteStrategy 2
public class Slow implements Speed {
@Override
public void drive() {
System.out.println("drive speed: Slow...");
}
}
public static void main(String[] args) {
Car car = new Car();
car.drive(new Normal());
car.drive(new Slow());
}
장단점
장점
- 새로운 전략을 추가하더라도 기존 코드를 변경하지 않는다.
- 상속 대신 위임을 사용할 수 있다.
- 런타임에 전략을 변경할 수 있다.
단점
- 복잡도가 증가한다.
- 클라이언트 코드가 구체적인 전략을 알아야 한다.
사용하는곳
- Java Comparator
- Spring ApplicationContext
커맨드 패턴 vs 전략 패턴
- 커맨드 패턴은
무엇
에 초점을 둔다. 어떻게 할지는 외부에서 주입해주고 무엇을 할지를 고려한다. - 전략 패턴은
어떻게
에 초점을 둔다. 하고자 하는것은 정해져있고 방법을 어떻게 할지 고려한다.
상태 패턴 vs 전략 패턴
- 상태 패턴에서 상태는 스스로를 다른 상태로 변경할 수 있지만, 전략 패턴에서 이는 불가능하다.
22. 템플릿 메서드 (Template Method) 패턴
- 알고리즘 구조를 서브 클래스가 확장할 수 있도록 템플릿으로 제공하는 방법.
- 추상 클래스는 템플릿을 제공하고 하위 클래스는 구체적인 알고리즘을 제공한다.
구현 방법
// AbstractClass
public abstract class AbstractClass {
public void execute() {
step1();
templateMethod();
step2();
}
public void step1() { ... }
public void step2() { ... }
protected abstract void templateMethod();
}
// ConcreteClass 1
public class ConcreteClass1 extends AbstractClass {
@Override
protected void templateMethod() {
// do something...
}
}
// ConcreteClass 2
public class ConcreteClass2 extends AbstractClass {
@Override
protected void templateMethod() {
// do something...
}
}
public static void main(String[] args) {
AbstractClass concrete1 = new ConcreteClass1();
AbstractClass concrete2 = new ConcreteClass2();
concrete1.execute();
concrete2.execute();
}
장단점
장점
- 템플릿 코드를 재사용하고 중복 코드를 줄일 수 있다.
- 템플릿 코드를 변경하지 않고 상속을 받아서 구체적인 알고리즘만 변경할 수 있다.
단점
- 리스코프 치환 원칙을 위반할 수도 있다.
- sub class 에서 동작을 변경할수있기 때문
- 알고리즘 구조가 복잡할 수록 템플릿을 유지하기 어려워진다.
사용하는곳
- Java, HttpServlet
- Spring
- 템플릿 메서드 패턴: Security 설정과 같은 Configuration
- 템플릿 콜백 패턴: JdbcTemplate, RestTemplate
템플릿 콜백 (Template-Callback) 패턴
- 상속 대신 익명 내부 클래스 또는 람다 표현식을 활용할 수 있다.
- 전략 패턴과 거의 유사하며 전략을 익명 내부 클래스 또는 람다 표현식으로 사용하는것만 차이가 있다.
- ConcreteCallback 을 만들지 않고 익명 내부 클래스 또는 람다 표현식을 사용하면 된다.
23. 방문자 (Visitor) 패턴
- 기존 코드를 변경하지 않고 새로운 기능을 추가하는 방법.
- 더블 디스패치 (Double Dispatch)를 활용할 수 있다.
- 알고리즘을 객체 구조에서 분리시키는 디자인 패턴이다.
- 실제 로직을 가지고 있는 객체(Visitor)가 로직을 적용할 객체(Element)를 방문하면서 실행된다.
- Element 가 추가될 가능성이 적고 Visitor 추가가 흔할 경우 유용하다.
- 실제 로직을 가지고 있는 객체(Visitor)가 로직을 적용할 객체(Element)를 방문하면서 실행된다.
구현 방법
// Element
public interface Shape {
void accept(Device device);
}
// Element A
public class Triangle implements Shape {
@Override
public void accept(Device device) {
device.print(this);
}
}
// Element B
public class Rectangle implements Shape {
@Override
public void accept(Device device) {
device.print(this);
}
}
// Visitor
public interface Device {
void print(Rectangle rectangle);
void print(Triangle triangle);
}
// ConcreteVisitor 1
public class Phone implements Device {
@Override
public void print(Rectangle rectangle) {
System.out.println("Print Rectangle to Phone");
}
@Override
public void print(Triangle triangle) {
System.out.println("Print Triangle to Phone");
}
}
// ConcreteVisitor 2
public class Pad implements Device {
@Override
public void print(Rectangle rectangle) {
System.out.println("Print Rectangle to Pad");
}
@Override
public void print(Triangle triangle) {
System.out.println("Print Triangle to Pad");
}
}
public static void main(String[] args) {
Shape triangle = new Triangle();
Device phone = new Phone();
triangle.accept(phone);
Shape rectangle = new Rectangle();
Device pad = new Pad();
rectangle.accept(pad);
}
장단점
장점
- 기존 코드를 변경하지 않고 새로운 코드를 추가할 수 있다.
- 추가 기능을 한 곳에 모아둘 수 있다.
단점
- 복잡하다.
- 새로운 Element 를 추가하거나 제거할 때 모든 Visitor 코드를 변경해야 한다.
사용하는곳
- Java FileVisitor, SimpleFileVisitor, AnnotationValueVisitor ElementVisitor
- Spring BeanDefinitionVisitor
Author And Source
이 문제에 관하여([Design Pattern] 행위(Behavioral) 패턴), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@csh0034/Design-Pattern-행위Behavioral-패턴저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)