(C++ 소프트웨어 디자인 책 정리)
앞서 구현한 Visitor 패턴을 std::variant, std::visit을 사용해서 구현해보자.
std::variant
std::variant 는 변수를 여러 타입을 선택하여 만든 후 선택한 타입 중 하나만 담을 수 있다.
아래 main() 예시를 보자.
int main()
{
//0으로 초기화한 'int'를 담고 있는 기본 variant를 생성한다.
std::variant<int, double, std::string> v{};
v = 42; //'int' 형 에 대입
v = "Hyojin" // 문자열 string 에 대입
v = 3.14 // 'double' 대입
int tmp = std::get<int>(v); // 값에 직접 접근한다 (참조형으로 return)
int pTmp = std::get_if<int>(&v); // 값 pointer로 접근한다. (pointer return)
return 0;
}
std::visit
std::visit 은 필요한 타입 디스패치를 수행한다.
아래 예시를 보자
struct Move
{
void operator()( Circle const& c) const
{ /* 원을 움직이는 논리 구현 */ }
void operator()( Square const& s) const
{ /* 사각형을 움직이는 논리 구현 */ }
};
int main(void)
{
using Shape = std::variant<Circle, Square>;
using Shapes = std::vector<Shape>;
for (auto const& item : Shapes) {
// execute operator() of move structure is according to shapes
std::visit(Move{}, item);
}
}
std::visit 에 호출하고자 하는 클래스(연산)를 왼쪽에, 타입(Arg)를 오른쪽에 입력 후 실행하면 연산 클래스의 operator()이 실행 된다.
이렇게 하면 별도의 visitor 클래스를 만들고 상속할 필요 없이 간단히 visitor 패턴을 구현할 수 있다.
즉 요지는 만들고자 하는 연산의 구조체(또는 클래스)를 선언 후 해당 operator()를 선언 후 연산구조만 만들면 끝이다!
이전 Circle, Square, Triapezoid 클래스는 별도의 클래스를 상속 받을 필요가 없어졌다!!
이전에 구현한 visitor 패턴에서 아래 Move 연산을 구조체로 선언, std::variant, std::visit 사용하여 visitor 패턴을 구현하였다.
/* === Shape.h 파일 === */
// ... 생략 ...
using Shape = std::variant<Circle, Square, Trapezoid>;
// ... 생략 ...
/* === Shape.h END === */
/* === Move.h 파일 === */
// ... 생략 ...
class Move
{
private:
double movingX_;
double movingY_;
public:
explicit Move( double x, double y )
: movingX_(x), movingY_(y) {}
void operator()( Circle& c) {
c.setCenter(movingX_, movingY_);
std::cout << " Circle move x " << movingX_ << " move y " << movingY_ << std::endl;
}
void operator()( Square& s) {
s.setCenter(movingX_, movingY_);
std::cout << " Square move x " << movingX_ << " move y " << movingY_ << std::endl;
}
void operator()( Trapezoid& t) {
t.setCenter(movingX_, movingY_);
std::cout << " Trapezoid move x " << movingX_ << " move y " << movingY_ << std::endl;
}
};
// ... 생략 ...
/* === Move.h END === */
/* === Circle.h === */
// ... 생략 ...
class Circle
{
private:
double radius_;
Point center_{};
public:
explicit Circle( double radius ) {
if (radius < 0) {
std::cout << "Circle radius is invalid " << std::endl;
} else {
std::cout << "Circle radius is valid " << std::endl;
this->radius_ = radius;
}
}
void draw() const {
std::cout << " circle drawing~!" << std::endl;
}
Point getCenter() { return center_; }
void setCenter( double x, double y) {
center_.x = x;
center_.y = y;
}
double getRadius() { return radius_; }
};
/* === Cicle.h END === */
아래와 같이 main 함수를 구현하여 visitor 패턴을 완성한다.
int main()
{
Shapes shapes;
shapes.emplace_back( Circle{3} );
shapes.emplace_back( Square{2} );
shapes.emplace_back( Trapezoid{} );
#if 1
for (auto& item : shapes) {
std::visit(Move{1,2}, item);
}
#endif
return 0;
}
github 코드: https://github.com/KJT9109/designPattern/tree/master/VisitorPattern_with_variant
'Language > C++ Design Pattern' 카테고리의 다른 글
Visitor 패턴 (1) | 2025.02.02 |
---|---|
DIP 원칙 (0) | 2025.01.30 |
LSP 원칙 (0) | 2025.01.30 |
OCP 원칙 (0) | 2025.01.29 |
ISP 원칙 (0) | 2025.01.29 |