ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [디자인패턴] 비지터패턴
    기타/북스터디 2023. 1. 29. 09:58

    1. 비지터패턴(Visitor Pattern)이란?

    비지터패턴이란 기존 코드를 건드리지않고 새로운 기능을 추가할 때에 사용하는 패턴이다.

    출처: 코딩으로 학습하는 GOF의 디자인 패턴(인프런강의, 백기선님)

    Element는 기존에 존재하는 객체로 변하지 않는 객체이다.

    Visitor는 추가하고자 하는 기능을 가지고 있는 객체이다.

    비지터패턴을 적용하기 위해서는 우리가 추가하고자하는 기능을 구현하기 위해 기존에 존재하는 객체인 Elment에 accpt(Visitor)라는 메서드를 추가해주어야한다.

    accpt(Visitor) 메서드 안에서는 Visitor.visit(this);로 자기 자신(Element구현객체)을 넘겨준다.

     

     

     

    2. 비지터패턴 적용 전 코드

    Shape라는 인터페이스가 있고 Shape를 구현하는 Rectangle과 Triangle이 있다고 가정해보자.

    public interface Shape {
    }

     

    public class Rectangle implements Shape {
    
    }
    public class Triangle implements Shape {
    
    }

     

    우리는 여기서 디바이스에 각각의 Shape를 출력하는 기능을 추가한다고 가정하고 그리고 Shape는 디바이스마다 다르게 출력하는 기능이라고 가정해보자.

     

    디바이스에 Shape를 출력해야한 메서드를 인터페이스에 추상메서드로 추가하고 추상메서드를 각각의 구현체에서 구체화해볼 수 있다.

    public interface Shape {
    
      void printTo(Device device);
    }
    public class Rectangle implements Shape {
    
      @Override
      void printTo(Device device) {
        if (Device instanceof Phone) {
          System.out.println("print Rectangle to Phone");
        }  else if (Device instanceof Watch) {
          System.out.println("print Rectangle to Watch");
        }
      }
    }
    public class Triangle implements Shape {
    
      @Override
      void printTo(Device device) {
        if (Device instanceof Phone) {
          System.out.println("print Triangle to Phone");
        }  else if (Device instanceof Watch) {
          System.out.println("print Triangle to Watch");
        }
      }
    }

     

    이렇게 기능을 구현하기 위해 코드를 작성한다면 아래와 같은 문제가 발생한다.

    1. Open-Closed principle을 위배한다.

     

    2. single responsibility principle을 위배했다고 볼 수 있다.

    - 디바이스에 출력하는 행동이 Shape에서 해야할  책임일까에 대한 생각을 해봤을 때에 위배한다고 볼 수 있다.

     

    3. Shape나 Device가 추가될 때마다 코드가 늘어나고 수정해야할 부분이 늘어난다.

     

    4. 새로운 Device가 추가되었을 때에 해당 Device에 대한 printTo메서드의 조건문이 없다면 오류가 발생할 여지가 있다.

     

     

     

    3. 비지터패턴을 적용해보자.

    먼저, Elment interface인 Shape를 아래와 같이 리팩토링 할 수 있다.

    public interface Shape {
    
      void accept(Device device);
    }

     

    그 후에는 Shape를 구현하는 구현체에서 accept를 구현해주어야 한다.

    public class Rectangle implements Shape {
    
      @Override
      public void accept(Device device) {
        device.print(this);
    }
    public class Triangle implements Shape {
    
      @Override
      public void accept(Device device) {
        device.print(this);
    }

     

    이렇게 Eelment의 리팩토링을 하면 Visitor인 Device가 아래와 같이 변경된다.

    Visitor인 Device에서는 각각의 Element를 인자로 받는 추상메서드들이 추가가 된다.

    public interface Device {
    
      void print(Triangle triangle);
      
      void print(Rectangle Rectangle);
    }

     

    마지막으로, Visitor를 구현하는 ConcreteVisitor인 Phone과 Watch는 아래와 같다.

    public class Phone implements Device {
    
      @Override
      public void print(Triangle triangle) {
        System.out.println("Print Triangle to Phone");
      }
      
      @Override
      public void print(Ractangle ractangle) {
        System.out.println("Print Ractangle to Phone");
      }
    }
    public class Watch implements Device {
    
      @Override
      public void print(Triangle triangle) {
        System.out.println("Print Triangle to Watch");
      }
      
      @Override
      public void print(Ractangle ractangle) {
        System.out.println("Print Ractangle to Watch");
      }
    }

     

     

     

    4. 비지터패턴의 장점과 단점

    - 장점

    1. 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있다.

     

    - 단점

    1. 비지터패턴을 알고 있지 않으면 구조를 이해하기 어렵다.

    2. Visitor에서 각각의 Element를 인자로 가지고 있는 메서드를 오버로딩해주어야한다.

    3. Element가 추가되거나 삭제되면 코드의 변경점이 늘어난다.

    댓글

Designed by Tistory.