[CS] 디자인 패턴

박상민·2023년 8월 17일
0

Computer Science

목록 보기
25/29

❗️팩토리 패턴

💡 객체의 생성을 캡슐화하는 패턴

객체를 사용하는 코드에서 객체 생성 부분을 떼어네 추상화한 패턴이자 상속 관계에 있는 두 클래스에서 상위 클래스가 중요한 뼈대를 결정하고, 하위 클래스에서 객체 생성에 대한 구체적인 내용을 결정하는 패턴이다.

상위 클래스와 하위 클래스가 분리되기 때문에 느슨한 결합을 가지며 상위 클래스에서는 인스턴스 생성 방식에 대해 전혀 알 필요가 없기 때문에 더 많은 유연성을 가지게 된다.

객체 생성 로직이 따로 떨어져 있기 때문에 코드를 리팩토링 하는 경우에도 한 곳만 고칠 수 있기 떄문에 유지 보수성이 증가한다.

팩토리 패턴은 객체 생성에 대한 로직은 상위 클래스에서 작성하고, 하위 클래스에서 객체 생성에 대한 구체적인 내용을 결정한다.

❗️프록시 패턴

프록시는 대리인이라는 뜻으로, 무엇인가를 대신 처리하는 의미이다. 실제 객체 앞단에 위치하여 해당 객체의 행위(Method)를 대리하는 역할을 한다. 프록시 객체를 통해 대상 객체에 접근하기 때문에 실제 객체가 메모리에 존재하지 않아도 기본적인 정보를 참조하거나 설정할 수 있고, 또한 실제 객체의 기능이 반드시 필요한 시점까지 객체의 생성을 미룰 수 있다.

  • 💡 대표적인 프록시 3가지

    1. 가상 프록시
      꼭 필요로 하는 시점까지 객체의 생성을 연기하고, 해당 객체가 생성된 것처럼 동작하도록 만들고 싶을 때 사용하는 패턴이다. 프록시 클래스에서 자잘한 작업들을 처리하고 리소스가 많이 요구되는 작업들이 필요할 때만 주체 클래스를 사용하도록 구현한다.
    2. 원격 프록시
      원격 객체에 대한 접근을 제어 로컬 환경에 존재하며, 원격 객체에 대한 대변자 역할을 하는 객체, 서로 다른 주소 공간에 잇는 객체에 대해 마치 같은 주소 공간에 있는 것처럼 동작하게 만드는 패턴이다. 예시로 Google Docs가 있다.
    3. 보호 프록시
      주체 클래스에 대한 접근을 제어하기 위한 경우에 객체에 대한 접근 권한을 제어하거나 객체마다 접근 권한을 달리하고 싶을 때 사용하는 패턴으로 프록시 클래스에서 클라이언트가 주체 클래스에 대한 접근을 허용할지 말지 결정하도록 할 수 있다.
  • 💡 프록시 패턴 장점

    • 사이즈가 큰 객체가 로딩되기 전에도 프록시를 통해 참조할 수 있다.
    • 실제 객체의 public, protected 메소드들을 숨기고 인터페이스를 통해 노출시킬 수 있다.
    • 로컬에 있지 않고 떨어져 있는 객체를 사용할 수 있다.
    • 원래 객체의 접근에 대해서 사전처리를 할 수 있다.
  • 💡 프록시 패턴 단점

    • 객체를 생성할 때 한 단계를 더 거치게 되므로, 빈번한 객체 생성이 필요한 경우 성능이 저하될 수 있다.
    • 프록시 내부에서 객체 생성을 위해 스레드가 생성, 동기화가 구현되야 하는 경우 성능이 저하될 수 있다.
    • 로직이 난해해져 가독성이 떨어질 수 있다.
  • 💡 프록시 패턴 예제

    public interface Image {
        void displayImage();
    }
    public class RealImage implements Image {
        private String fileName;
    
        public RealImage(String fileName) {
            this.fileName = fileName;
            loadFromDisk(fileName);
        }
    
        private void loadFromDisk(String fileName) {
            System.out.println("Loading " + fileName);
        }
    
        @Override
        public void display() {
            System.out.println("Displaying " + fileName);
        }
    }
    public class ProxyImage implements Image {
        private RealImage realImage;
        private String fileName;
    
        public ProxyImage(String fileName) {
            this.fileName = fileName;
        }
    
        @Override
        public void displayImage() {
            if (realImage == null) {
                realImage = new RealImage(fileName);
            }
            realImage.displayImage();
        }
    }
    public class ProxyMain {
        public static void main(String[] args) {
            Image image1 = new ProxyImage("test1.jpg");
            Image image2 = new ProxyImage("test2.jpg");
    
            image1.displayImage();
            image2.displayImage();
        }
    }

    ❗️싱글톤 패턴

    전역변수를 사용하지 않고 객체를 하나만 생성한다.
    생성된 객체를 어디에서든지 참조할 수 있다.
    생성자가 여러 차례 호출되어도 하나의 객체만 존재한다.
    최초 생성 이후에 호출된 생성자는 최초 생성자가 생성한 객체를 리턴한다.
    환경설정 관리 클래스나 커넥션 풀과 같이 pool 형태로 관리되는 공통 클래스에서 사용되는 것이 일반적이다.

  • 💡 싱글톤 패턴의 특징

    • 고정된 메모리 영역을 얻으면서 동시에 한번만 new를 사용하여 메모리 낭비를 방지할 수 있다.

    • 싱글톤으로 만들어진 인스턴스는 전역으로 사용되며, 다른 클래스의 인스턴스들이 데이터를 공유하고 변경할 수 있다.

    • 많은 일을 위임하거나 공유하는 경우 Coupling이 많아지고 결합도가 높아진다.

    • Java에서의 싱글톤 패턴

      public class Singleton {
          private static class singleInstanceHolder {
              private static final Singleton INSTANCE = new Singleton();
          }
          public static synchronized Singleton getInstance() {
              return singleInstanceHolder.INSTANCE;
          }
      }
      
      public class HelloWorld { 
           public static void main(String[] args) { 
              Singleton a = Singleton.getInstance(); 
              Singleton b = Singleton.getInstance(); 
              System.out.println(a.hashCode());
              System.out.println(b.hashCode());  
              if (a == b) {
               System.out.println(true); 
              } 
           }
      }
  • 💡 싱글톤 패턴의 단점

    • TDD를 할 때 독립적인 인스턴스를 만들기가 어렵다.

      💡 TDD를 할 때 단위 테스트를 주로 하는데, 단위 테스트는 테스트가 서로 독립적이어야 하며 테스트를 어떤 순서로든 실행할 수 있어야 한다. 하지만 싱글톤 패턴은 미리 생성된 하나의 인스턴스를 기반으로 구현하는 패턴이므로 각 테스트마다 독립적인 인스턴스를 만들기가 어렵다.

    • 모듈 간의 결합도를 높힐 수 있다.

      💡 의존성 주입 (DI, Dendency Injection)을 통해 모듈 간 결합을 조금 더 느슨하게 만들어 해결할 수 있다.

  • 💡 싱글톤 패턴에 주의할 점
    상태를 가진 객체를 Singleton으로 만들면 안된다. 앱 내의 단 한개의 인스턴스가 존재하고, 이를 전역에서 접근할 수 있다면 각기 다른 스레드에서 객체의 상태를 마구잡이로 변경시킬 여지가 있다. 상태가 공유되는 것은 위험하기 때문에 무상태 객체 혹은 설계상 유일해야하는 시스템 컴포넌트를 Singleton으로 만들어야 한다.

profile
💡 클린코드를 지향하는 Backend Developer

1개의 댓글

comment-user-thumbnail
2023년 8월 17일

잘 읽었습니다. 좋은 정보 감사드립니다.

답글 달기