Design patterns
방문자 패턴
수색…
C ++의 방문객 패턴 예제
대신에
struct IShape
{
virtual ~IShape() = default;
virtual void print() const = 0;
virtual double area() const = 0;
virtual double perimeter() const = 0;
// .. and so on
};
방문객을 사용할 수 있습니다 :
// The concrete shapes
struct Square;
struct Circle;
// The visitor interface
struct IShapeVisitor
{
virtual ~IShapeVisitor() = default;
virtual void visit(const Square&) = 0;
virtual void visit(const Circle&) = 0;
};
// The shape interface
struct IShape
{
virtual ~IShape() = default;
virtual void accept(IShapeVisitor&) const = 0;
};
이제 구체적인 모양 :
struct Point {
double x;
double y;
};
struct Circle : IShape
{
Circle(const Point& center, double radius) : center(center), radius(radius) {}
// Each shape has to implement this method the same way
void accept(IShapeVisitor& visitor) const override { visitor.visit(*this); }
Point center;
double radius;
};
struct Square : IShape
{
Square(const Point& topLeft, double sideLength) :
topLeft(topLeft), sideLength(sideLength)
{}
// Each shape has to implement this method the same way
void accept(IShapeVisitor& visitor) const override { visitor.visit(*this); }
Point topLeft;
double sideLength;
};
방문객 :
struct ShapePrinter : IShapeVisitor
{
void visit(const Square&) override { std::cout << "Square"; }
void visit(const Circle&) override { std::cout << "Circle"; }
};
struct ShapeAreaComputer : IShapeVisitor
{
void visit(const Square& square) override
{
area = square.sideLength * square.sideLength;
}
void visit(const Circle& circle) override
{
area = M_PI * circle.radius * circle.radius;
}
double area = 0;
};
struct ShapePerimeterComputer : IShapeVisitor
{
void visit(const Square& square) override { perimeter = 4. * square.sideLength; }
void visit(const Circle& circle) override { perimeter = 2. * M_PI * circle.radius; }
double perimeter = 0.;
};
그리고 그것을 사용하십시오 :
const Square square = {{-1., -1.}, 2.};
const Circle circle{{0., 0.}, 1.};
const IShape* shapes[2] = {&square, &circle};
ShapePrinter shapePrinter;
ShapeAreaComputer shapeAreaComputer;
ShapePerimeterComputer shapePerimeterComputer;
for (const auto* shape : shapes) {
shape->accept(shapePrinter);
std::cout << " has an area of ";
// result will be stored in shapeAreaComputer.area
shape->accept(shapeAreaComputer);
// result will be stored in shapePerimeterComputer.perimeter
shape->accept(shapePerimeterComputer);
std::cout << shapeAreaComputer.area
<< ", and a perimeter of "
<< shapePerimeterComputer.perimeter
<< std::endl;
}
예상 출력 :
Square has an area of 4, and a perimeter of 8
Circle has an area of 3.14159, and a perimeter of 6.28319
설명 :
void Square::accept(IShapeVisitor& visitor) const override { visitor.visit(*this); }
의 정지형this
알려져 있으며, 따라서 선택 (컴파일시) 과부하는void IVisitor::visit(const Square&);
.square.accept(visitor);
호출을 사용하면virtual
통한 동적 디스패치는 호출 할accept
을 파악하는 데 사용됩니다.
장점 :
- 새 방문자를 추가하기 만하면
IShape
클래스에 새로운 기능 (SerializeAsXml
, ...)을 추가 할 수 있습니다.
단점 :
- 새로운 콘크리트 모양 (
Triangle
, ...)을 추가하려면 모든 방문자를 수정해야합니다.
모든 기능을 넣어의 다른 virtual
의 방법 IShape
반대 장단점이 있습니다 : 새로운 기능을 추가하면 기존의 모든 모양을 수정할 필요로하지만, 새로운 형태를 추가하면 기존 클래스에 영향을주지 않습니다.
자바의 방문자 패턴 예제
Visitor
패턴을 사용하면 클래스의 구조를 수정하지 않고 새 작업 또는 메서드를 클래스 집합에 추가 할 수 있습니다.
이 패턴은 객체를 확장하지 않고 객체에 특정 작업을 집중 시키거나 객체를 수정하지 않고 작업 할 때 특히 유용합니다.
위키 백과의 UML 다이어그램 :
코드 스 니펫 :
import java.util.HashMap;
interface Visitable{
void accept(Visitor visitor);
}
interface Visitor{
void logGameStatistics(Chess chess);
void logGameStatistics(Checkers checkers);
void logGameStatistics(Ludo ludo);
}
class GameVisitor implements Visitor{
public void logGameStatistics(Chess chess){
System.out.println("Logging Chess statistics: Game Completion duration, number of moves etc..");
}
public void logGameStatistics(Checkers checkers){
System.out.println("Logging Checkers statistics: Game Completion duration, remaining coins of loser");
}
public void logGameStatistics(Ludo ludo){
System.out.println("Logging Ludo statistics: Game Completion duration, remaining coins of loser");
}
}
abstract class Game{
// Add game related attributes and methods here
public Game(){
}
public void getNextMove(){};
public void makeNextMove(){}
public abstract String getName();
}
class Chess extends Game implements Visitable{
public String getName(){
return Chess.class.getName();
}
public void accept(Visitor visitor){
visitor.logGameStatistics(this);
}
}
class Checkers extends Game implements Visitable{
public String getName(){
return Checkers.class.getName();
}
public void accept(Visitor visitor){
visitor.logGameStatistics(this);
}
}
class Ludo extends Game implements Visitable{
public String getName(){
return Ludo.class.getName();
}
public void accept(Visitor visitor){
visitor.logGameStatistics(this);
}
}
public class VisitorPattern{
public static void main(String args[]){
Visitor visitor = new GameVisitor();
Visitable games[] = { new Chess(),new Checkers(), new Ludo()};
for (Visitable v : games){
v.accept(visitor);
}
}
}
설명:
-
Visitable
(Element
)은 인터페이스Visitable
인터페이스 메소드는 일련의 클래스에 추가되어야합니다. -
Visitor
에 작업을 수행하는 방법을 포함하는 인터페이스이다Visitable
소자. -
GameVisitor
는Visitor
인터페이스 (ConcreteVisitor
)를 구현하는 클래스입니다. - 각
Visitable
요소는 동의Visitor
와의 관련 방법 호출Visitor
인터페이스를. -
Game
을Element
로 취급하고Chess,Checkers and Ludo
같은 구체적인 게임을ConcreteElements
로 취급 할 수 있습니다.
위의 예에서 Chess, Checkers and Ludo
는 세 가지 게임 (및 Visitable
클래스)입니다. 좋은 하루에 나는 각 게임의 통계를 기록하는 시나리오를 만났습니다. 따라서 통계 기능을 구현하기 위해 개별 클래스를 수정하지 않고도 각 책임의 구조를 수정하지 않고도 GameVisitor
클래스에서 책임을 중앙 집중화 할 수 있습니다.
산출:
Logging Chess statistics: Game Completion duration, number of moves etc..
Logging Checkers statistics: Game Completion duration, remaining coins of loser
Logging Ludo statistics: Game Completion duration, remaining coins of loser
유스 케이스 / 적용 분야 :
- 구조에서 그룹화 된 여러 유형의 객체에 대해 유사한 작업을 수행해야합니다.
- 서로 다른 많은 관련없는 작업을 실행해야합니다. 객체를 Operation과 Object 구조로 구분합니다.
- 객체 구조를 변경하지 않고 새로운 작업을 추가해야합니다.
- 클래스 를 변경하거나 파생시키지 않고 관련 작업을 단일 클래스로 수집하십시오.
- 소스가 없거나 소스를 변경할 수없는 클래스 라이브러리에 함수를 추가하십시오.
추가 참조 :
C ++의 방문자 예제
// A simple class hierarchy that uses the visitor to add functionality.
//
class VehicleVisitor;
class Vehicle
{
public:
// To implement the visitor pattern
// The class simply needs to implement the accept method
// That takes a reference to a visitor object that provides
// new functionality.
virtual void accept(VehicleVisitor& visitor) = 0
};
class Plane: public Vehicle
{
public:
// Each concrete representation simply calls the visit()
// method on the visitor object passing itself as the parameter.
virtual void accept(VehicleVisitor& visitor) {visitor.visit(*this);}
void fly(std::string const& destination);
};
class Train: public Vehicle
{
public:
virtual void accept(VehicleVisitor& visitor) {visitor.visit(*this);}
void locomote(std::string const& destination);
};
class Automobile: public Vehicle
{
public:
virtual void accept(VehicleVisitor& visitor) {visitor.visit(*this);}
void drive(std::string const& destination);
};
class VehicleVisitor
{
public:
// The visitor interface implements one method for each class in the
// hierarchy. When implementing new functionality you just create the
// functionality required for each type in the appropriate method.
virtual void visit(Plane& object) = 0;
virtual void visit(Train& object) = 0;
virtual void visit(Automobile& object) = 0;
// Note: because each class in the hierarchy needs a virtual method
// in visitor base class this makes extending the hierarchy ones defined
// hard.
};
예제 사용법 :
// Add the functionality `Move` to an object via a visitor.
class MoveVehicleVisitor
{
std::string const& destination;
public:
MoveVehicleVisitor(std::string const& destination)
: destination(destination)
{}
virtual void visit(Plane& object) {object.fly(destination);}
virtual void visit(Train& object) {object.locomote(destination);}
virtual void visit(Automobile& object) {object.drive(destination);}
};
int main()
{
MoveVehicleVisitor moveToDenver("Denver");
Vehicle& object = getObjectToMove();
object.accept(moveToDenver);
}
대형 물체 이송
방문자 패턴은 구조를 탐색하는 데 사용할 수 있습니다.
class GraphVisitor;
class Graph
{
public:
class Node
{
using Link = std::set<Node>::iterator;
std::set<Link> linkTo;
public:
void accept(GraphVisitor& visitor);
};
void accept(GraphVisitor& visitor);
private:
std::set<Node> nodes;
};
class GraphVisitor
{
std::set<Graph::Node*> visited;
public:
void visit(Graph& graph)
{
visited.clear();
doVisit(graph);
}
bool visit(Graph::Node& node)
{
if (visited.find(&node) != visited.end()) {
return false;
}
visited.insert(&node);
doVisit(node);
return true;
}
private:
virtual void doVisit(Graph& graph) = 0;
virtual void doVisit(Graph::Node& node) = 0;
};
void accept(GraphVisitor& visitor)
{
// Pass the graph to the visitor.
visitor.visit(*this);
// Then do a depth first search of the graph.
// In this situation it is the visitors responsibility
// to keep track of visited nodes.
for(auto& node: nodes) {
node.accept(visitor);
}
}
void Graph::Node::accept(GraphVisitor& visitor)
{
// Tell the visitor it is working on a node and see if it was
// previously visited.
if (visitor.visit(*this)) {
// The pass the visitor to all the linked nodes.
for(auto& link: linkTo) {
link->accept(visitor);
}
}
}