콘텐츠로 이동

2023 03 24

2023-03-24

팩토리 메서드 패턴

  • 참고: https://western-sky.tistory.com/40
  • 객체 생성을 하는 클래스를 따로 두는 것
  • 상위 클래스는 하위 클래스의 구현 내용 몰라도 사용 가능
  • makeCar() 에서만 객체 생성하니, 객체 관리가 편해짐
    • 확장 가능성도 좋음
      public class Factory {
        public Car makeCar(String s) {
          if (s == "작은차") return new SmallCar();
          if (s == "버스") return new Bus();
          if (s == "스쿠터") return new Scooter();
          return null;
        }
      }
      
      public interface Car {
        void drive();
      }
      
      public class SmallCar implements Car {
      
        @Override
        public void drive() {
          System.out.println("승용차 - 2종운전 주행을 시작합니다.");
        }
      }
      
      public class Bus implements Car {
      
        @Override
        public void drive() {
          System.out.println("버스 - 1종운전 주행을 시작합니다.");
        }
      }
      
      public class Scooter implements Car{
      
        @Override
        public void drive() {
          System.out.println("스쿠터 - 스쿠터 주행을 시작합니다.");
        }
      }
      

템플릿 메서드 패턴

  • 참고: https://western-sky.tistory.com/40
  • 개요
    • 상속 시 상위 클래스의 메서드를 3종류로 나누는 방식
      1. 템플릿 메서드: 공통된 역할을 수행하는 메서드
      2. 추상 메서드: 반드시 구현할 추상 메서드
      3. 훅 메서드: 그대로 사용해도 되고, 오버라이딩 해서 사용해도 되는 메서드
  • 장점
    • 하위 클래스가 전체 로직을 변경하지 않으면서, 부분적인 수정이 가능함
    • 템플릿 메서드가 전체적인 알고리즘 구조 보호 + 코드 중복 최소화
  • 예시
    public abstract class Coffee {
      public void makeCoffee() {
        System.out.println("커피 머신 시작");
        espresso();
        putSomething();
        wrap();
      }
    
      public void espresso() {
        System.out.println("에스프레소 투입!");
      }
    
      public abstract void putSomething() {
      }
    
      public void wrap() {
        System.out.println("머그잔에 커피 투입");
      }
    }
    
    public class American extends Coffee {
      @Override
      public void putSomething() {
        System.out.println("물 넣기");
      }
    }
    
    public class CafeLatte extends Coffee {
      @Override
      public void putSomething() {
        System.out.println("우유 넣기");
      }
    }
    

스칼라 Generics

  • Covariance
    • List[Animal]에 List[Cat]을 넣을 수 있다
    • A가 B의 하위 타입이면 Class[A]는 Class[B]의 하위 타입!
      class Animal
      class Cat extends Animal
      class Dog extends Animal
      
      class CovariantList[+A]
      val animal: Animal = new Cat
      val animalList: CovariantList[Animal] = new CovariantList[Cat]
      animalList.add(animal) // 이때 반환하는 리스트는 CovariantList[Cat]에서 CovariantList[Animal]로 변경!!!
      // add를 해주는 메서드를 생성해줄 때, 반환값을 더 제네릭한 부모 타입으로 반환해주도록 코드를 작성하자
      
  • Invariance
    • A가 B의 하위 타입이라고 하더라도 Class[A]와 Class[B]는 아무 상관이 없어
    • List[Animal]에 List[Cat]을 왜 넣어;;
    • 고정된 타입에 고정된 값을 넣자
  • Contravariance

    • A가 B의 하위 타입이면, Class[B]는 Class[A]의 하위타입...?
    • 유연하게 사용될 경우가 있다네...
      class MyClass[-A]
      val a: MyClass[Any] = new MyClass[Any]
      val b: MyClass[String] = a // MyClass[Any]가 MyClass[String]의 상위타입
      
    • Comparator가 대표적
      trait Comparator[-T] {
        def compare(a: T, b: T): Int
      }
      
      Comparator[Cat] = new Compartor[Animal] // 대입이 가능해짐. => Animal에서 정의한거 가져다가 쓸 수 있어서 유연한 듯?