[ 팩토리 메서드(Factory Method) 패턴 ]
: 객체 생성을 Client단에서 하지 않고 별도로 객체를 생성하는 서브클래스(Factory Class)를 만들어 객체를 생성함으로써 결합도를 낮추고 코드 중복을 낮추는 디자인 패턴.

다음 코드는 Client단에서 직접 객체를 생성할 때 발생할 수 있는 문제점을 코드로 나타낸 예시이다.

조건에 따라서, 분기하며 객체를 생성할 경우 아래처럼 if 혹은 switch로 객체를 생성하는대 이때 해당 switch문은 여러군대서 작성될 수 있고
따라서, 객체를 생성할 때마다 중복 코드가 발생할 수 있게 된다.
그뿐만 아니라, 직접 객체를 생성함으로써 결합도도 높아져 클래스 수정시 많은 부분을 수정이 필요하게 된다.

public interface Animal {
    public String getName();
}

public class Dog implements Animal{

    private String name;

    public Dog(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }
}

public class Cat implements Animal {
    private String name;

    public Cat(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return this.name;
    }
}

public class Rabbit implements Animal{
    private String name;

    public Rabbit(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return this.name;
    }
}

main> main에 있는 switch문 여러군대서 중복되는 문제점 및 testClass와의 결합도가 높아지는 문제점이 있다.

@Test
    public void test6() throws Exception {

        String objName = "DOG";
        Animal obj = null;
        switch(objName) {
        case "DOG":
            obj = new Dog(objName);
            break;
        case "CAT":
            obj = new Cat(objName);
            break;
        case "RABBIT":
            obj = new Rabbit(objName);
            break;
        default:
            break;
        }

        System.out.println(obj.getName());
    }

위의 문제있는 코드를 '팩토리 메서드'패턴을 이용해 수정한 예)

  • 객체를 생성하는 것을 AnimalFactory라는 서브클래스에 위임한다.

    public class AnimalFactory {

      public static Animal createAnimalObj(String name) {
          Animal retAnimal = null;
          switch(name) {
          case "DOG":
              retAnimal = new Dog(name);
              break;
          case "CAT":
              retAnimal = new Cat(name);
              break;
          case "RABBIT":
              retAnimal = new Rabbit(name);
              break;
          }
          return retAnimal;
      }

    }

  • main

    @Test

      public void test6() throws Exception {        
          **Animal animalObj = AnimalFactory.createAnimalObj(objName);**
          System.out.println(animalObj.getName());
      }

1. Strategy Pattern

Created: Oct 08, 2019 10:27 AM
Tags: strategy pattern,전략 패턴

[ Strategy Pattern ]

  • : 자주 변동이 생기거나 기존 구조에 새로 추가될 경우 해당 알고리즘을 "캡슐화"해 주입함으로써

  • 클라이언트와는 독립적으로 알고리즘을 교환할 수 있게되는 디자인 패턴

    public abstract class Animal {

      // 각 객체에서 공통으로 사용될 이름 변수
      String name;
    
      public Animal(String name) {
          this.name = name;
      }
    
      public void doBehavior() {
          // Dog, Eagle, Penguin 각 객체 모두 공통으로 동일한 내용으로 호출되는 메서드
          doEat();
    
          // 각 동물 객체별로 재구현이 필요한 이동관련 메서드
          doMove();
    
          // 각 동물 객체별로 재구현이 필요한 울음소리 메서드
          doCry();
    
          if(canFly()) { // hook method로 각 객체에서 재구현해 실행여부를 결정(optional)
              doFly();
          }
      }
    
      public void doEat() {
          System.out.println(name + "(은) 얌얌 밥을 먹습니다.");
      }
    
      public abstract void doMove();
    
      public abstract void doCry();
    
      public boolean canFly() {
          return false;
      }
    
      public abstract void doFly();

    }

    public class Dog extends Animal{

      public Dog(String name) {
          super(name);
      }
    
      @Override
      public void doMove() {
          System.out.println(name + "(은) 네발로 기어갑니다.");
      }
    
      @Override
      public void doCry() {
          System.out.println(name + "(은) 왈왈 짓습니다.");
      }
    
      @Override
      public void doFly() {
          // TODO Auto-generated method stub
      }

    }

    public Eagle(String name) {

          super(name);
      }
    
      @Override
      public void doMove() {
          System.out.println(name + "(은) 날아갑니다.");
      }
    
      @Override
      public void doCry() {
          System.out.println(name + "(은) 쒱~ 하고 웁니다.");
      }
    
      @Override
      public void doFly() {
          System.out.println(name +"(은) 날게가 있어 날 수 있습니다.");
      }
    
      // hook method를 true로 바꿔 doFly()가 동작할 수 있도록 한다.
      @Override
      public boolean canFly() {
          return true;
      }

    }

    public class Penguin extends Animal{

      public Penguin(String name) {
          super(name);
      }
    
      @Override
      public void doMove() {
          System.out.println(name + "(은) 뒤뚱뒤뚱 걷습니다.");
      }
    
      @Override
      public void doCry() {
          System.out.println(name + "(은) 펭귄~하고 웁니다.");
      }
    
      @Override
      public void doFly() {
          System.out.println(name + "(은) 날개가 없어 날 수는 없습니다.");
      }
    
      @Override
      public boolean canFly() {
          return true;
      }

    }

    public class CommonModelTest {

      private static final Logger logger = LoggerFactory.getLogger(CommonModelTest.class);
    @Test
    public void test1() {
        Dog dog = new Dog("강아지");
        dog.doBehavior();

        Eagle eagle = new Eagle("독수리");
        eagle.doBehavior();

        Penguin penguin = new Penguin("펭귄");
        penguin.doBehavior();
        /*
          강아지(은) 얌얌 밥을 먹습니다.
            강아지(은) 네발로 기어갑니다.
            강아지(은) 왈왈 짓습니다.
            독수리(은) 얌얌 밥을 먹습니다.
            독수리(은) 날아갑니다.
            독수리(은) 쒱~ 하고 웁니다.
            독수리(은) 날게가 있어 날 수 있습니다.
            펭귄(은) 얌얌 밥을 먹습니다.
            펭귄(은) 뒤뚱뒤뚱 걷습니다.
            펭귄(은) 펭귄~하고 웁니다.
            펭귄(은) 날개가 없어 날 수는 없습니다.
         * */
    }    


}

위의 코드는 Dog, Eagle, Penguin 동물 객체의 행동을 구현하였는데, 동물의 공통 동작을 정하기 위해서 Template Method Pattern을 이용해서 구성하였다.

  1. 먼저, Dog, Eagle, Penguin은 공통적으로 doEat 밥을 먹는 메서드를 Animal에 구현하였고
  2. 울음소리를 나타내는 doCry는 Animal을 상속받는 각 동물객체에서 재정의하도록 하였다.
  3. 마지막으로 doFly는 강아지는 날 수 없음으로 Optional하게 선택할 수 있도록 Hook Method인 canFly()를 넣어 각 동물객체에서 재정의하여 doFly를 수행할지 여부를 지정할 수 있도록 하여 큰 틀을 구성하였다.

→ 이때, doFly 메서드의 구현방식을 변경하고(날 수 있는 녀석, 없는 녀석)

→ doLive라는 어디에 서식하는지에 대한 메서드를 추가한다고 했을 때

위의 코드의 문제점은 무엇일까?

—> 1. 먼저, doFly 메서드는 Dog한테는 해당사항이 없음에도 override하고 있는 문제점

  1. doFly의 상세 구현 내용을 변경하기 위해 각 객체별로 접근해서 구현 내용을 일일이 바까주어

    야 하는 점.(지금은 객체가 3개뿐이지만, doFly메서드를 사용하는게 20,30개가 넘는다면?)

→ 따라서, 이럴 경우에는 doFly메서드 알고리즘을 별도로 "캡슐화"해서 주입하도록 해 언제든지 알고리즘을 교환하는 방식을 통해 클라이언트 실행단과의 종속성을 제거할 수 있다.

수정 방향)

  1. doFly메서드에 대해 "날수 있는", "날수 없는"의 알고리즘을 분리한다.
  2. 각 객체에 주입한다.

공통사항을 관리하는 Animal 추상 클래스 : 아까와 동일하다.

public abstract class Animal {
    // 각 객체에서 공통으로 사용될 이름 변수
    String name;

    public Animal(String name) {
        this.name = name;
    }

    public void doBehavior() {
        // Dog, Eagle, Penguin 각 객체 모두 공통으로 동일한 내용으로 호출되는 메서드
        doEat();

        // 각 동물 객체별로 재구현이 필요한 이동관련 메서드
        doMove();

        // 각 동물 객체별로 재구현이 필요한 울음소리 메서드
        doCry();

        if(canFly()) { // hook method로 각 객체에서 재구현해 실행여부를 결정(optional)
            doFly();
        }
    }

    public void doEat() {
        System.out.println(name + "(은) 얌얌 밥을 먹습니다.");
    }

    public abstract void doMove();

    public abstract void doCry();

    public boolean canFly() {
        return false;
    }

    public abstract void doFly();
}

DoFly 인터페이스

: 날수 있는 알고리즘, 날수없는 알고리즘을 관리할 DoFly 인터페이스

public interface DoFly {
    // 나는 것과 관련된 메서드
    public void fly();
}

CanFly 클래스

: DoFly 인터페이스를 구현해 날 수 있는 알고리즘을 구현한 클래스로 ⇒ 알고리즘을 "캡슐화"했다.

public class CanFly implements DoFly{

    private String name;

    public CanFly(String name) {
        this.name = name;
    }

    @Override
    public void fly() {
        System.out.println("날 수 있습니다.");
    }

}

CanNotFly 클래스

: 위와 마찬가지로 날 수 없는 알고리즘을 캡슐화

public class CanNotFly implements DoFly{

    private String name;

    public CanNotFly(String name) {
        this.name = name;
    }

    @Override
    public void fly() {
        System.out.println("날개는 있지만 날  수 없습니다.");
    }

}

public class CanNotFly implements DoFly{

    private String name;

    public CanNotFly(String name) {
        this.name = name;
    }

    @Override
    public void fly() {
        System.out.println("날개는 있지만 날  수 없습니다.");
    }

}

Dog 클래스

: 강아지 객체로 Animal을 상속했으며 강아지는 날수없음으로 Animal의 canFly() hook method를 구현하지 않았다.(default false)

public class Dog extends Animal{

    public Dog(String name) {
        super(name);
    }

    @Override
    public void doMove() {
        System.out.println(name + "(은) 네발로 기어갑니다.");
    }

    @Override
    public void doCry() {
        System.out.println(name + "(은) 왈왈 짓습니다.");
    }

    @Override
    public void doFly() {
        // TODO Auto-generated method stub
    }

}

Eagle 클래스

: 아까는 doFly() 내부에 직접 상세구현했다면 이번엔 DoFly fly 를 선언해 생성자에서 CanFly 알고리즘을 주입하는 방식으로 바깠다.

펭귄의 경우 날 수 없음으로 CanNotFly가 주입될 것(즉, 알고리즘 교환이 용이하다.)

public class Eagle extends Animal {

    DoFly fly;

    public Eagle(String name) {
        super(name);
        fly = new CanFly(name); // 각 객체에서 구현한 것을 주입한다.(날 수 있는 알고리즘 객체를 넣음)
    }

    @Override
    public void doMove() {
        System.out.println(name + "(은) 날아갑니다.");
    }

    @Override
    public void doCry() {
        System.out.println(name + "(은) 쒱~ 하고 웁니다.");
    }

    @Override
    public void doFly() {
        fly.fly();
    }

    // hook method를 true로 바꿔 doFly()가 동작할 수 있도록 한다.
    @Override
    public boolean canFly() {
        return true;
    }

}

Penguin 클래스

public class Penguin extends Animal{

    DoFly fly;

    public Penguin(String name) {
        super(name);
        this.fly = new CanNotFly(name); // 펭귄은 날수 없는 알고리즘을 주입함
    }

    @Override
    public void doMove() {
        System.out.println(name + "(은) 뒤뚱뒤뚱 걷습니다.");
    }

    @Override
    public void doCry() {
        System.out.println(name + "(은) 펭귄~하고 웁니다.");
    }

    @Override
    public void doFly() {
        fly.fly();
    }

    @Override
    public boolean canFly() {
        return true;
    }

}

실행 main 클래스

public class CommonModelTest {
    private static final Logger logger = LoggerFactory.getLogger(CommonModelTest.class);


    @Test
    public void test1() {
        Dog dog = new Dog("강아지");
        dog.doBehavior();

        Eagle eagle = new Eagle("독수리");
        eagle.doBehavior();

        Penguin penguin = new Penguin("펭귄");
        penguin.doBehavior();
        /*
         *  강아지(은) 얌얌 밥을 먹습니다.
            강아지(은) 네발로 기어갑니다.
            강아지(은) 왈왈 짓습니다.
            독수리(은) 얌얌 밥을 먹습니다.
            독수리(은) 날아갑니다.
            독수리(은) 쒱~ 하고 웁니다.
            날 수 있습니다.
            펭귄(은) 얌얌 밥을 먹습니다.
            펭귄(은) 뒤뚱뒤뚱 걷습니다.
            펭귄(은) 펭귄~하고 웁니다.
            날개는 있지만 날  수 없습니다.
         * */
    }    


}

2. State Pattern

Created: Oct 15, 2019 10:51 AM
Tags: strategy pattern,상태 패턴,스테이트 패턴

  • 스테이트 패턴(State Pattern)

: 실세계의 많은 객체는 "상태"에 따라 서로 다른 업무를 처리하는 경우가 많다.
이때, 가장 직관적인 방법은 수행하는 행동(메서드)마다 상태를 조건으로 체크해서 조건별로 수행하는 코드를 다르게 작성하는 것이다.
이럴경우 문제는 메서드마다 조건문이 중복으로 들어가게 되고, 추가로 상태코드가 추가될 경우 모든 메서드를 수정해야하며, 가독성 또한 잃게 된다.

이런 문제점을 개선하기 위해 **"변경되는 사항"**인 "**상태**"를 캡슐화하고 해당 상태객체를 관리할 수 있는 인터페이스를 만들어 수행하는 메서드에서 상태에 따른 구체적인 구현을 하는 것이 아닌 메서드별로 상태객체를 관리하는 인터페이스에만 의존하게 되어 상태가 추가되더라고 메서드 내에서 구현이 변경되지 않을 수 있게 된다. 구현은 각 상태 객체가 담당하게 되어 조건식 등이 불필요해 진다.

스테이트 패턴의 필요성을 설명하는 예>

: 다음은 형광등 객체를 구현한 것으로 상태(OFF/ON)에 따라 turnOn메서드와 turnOff메서드의 처리가 다르게 되어 있다.

다행히 메서드가 두개여서 망정이지 더 많은 메서드가 존재시 매 메서드마다 상태를 조건문 분기하며 처리시 가독성도 떨어지고, 상태가 하나 추가되어도 모든 메서드를 전부 수정해야 하는 문제점이 발생한다.

    public class Light {
        private static final int OFF = 0;
        private static final int ON = 1;
        private int state;

        public Light() {
            state = OFF; // 최초 OFF 상태
        }

        public void setState(int state) {
            this.state = state;
        }

        public void turnOn() {
            if(ON == state) {
                System.out.println("이미 켜져있습니다.");
            } else { // OFF 상태
                System.out.println("불이 켜졌습니다.");
                state = ON; // OFF -> ON으로 상태 변경
            }
        }

        public void turnOff() {
            if(OFF == state) {
                System.out.println("이미 불이 꺼져있습니다.");
            } else {
                System.out.println("불이 꺼졌습니다.");
                state = OFF; // ON -> OFF
            }
        }
    }

main

        Light light = new Light(); // state == OFF
            light.turnOff(); 
            light.turnOn();
            light.turnOff();
            /*
            이미 불이 꺼져있습니다.
            불이 켜졌습니다.
            불이 꺼졌습니다.
             * */

만약, 이 예제에서 state가 sleeping(취침모드)가 추가된다면 어떻게 수정될까?

    public class Light {
        private static final int OFF = 0;
        private static final int ON = 1;
        private static final int SLEEPING = 2;
        private int state;

        public Light() {
            state = OFF; // 최초 OFF 상태
        }

        public void setState(int state) {
            this.state = state;
        }

        public void turnOn() {
            if(ON == state) {
                System.out.println("취침모드로 전환합니다.");
                state = SLEEPING;
            } else if(SLEEPING == state) {
                System.out.println("불이 켜졌습니다.");
                state = ON;
            } else { // OFF 상태
                System.out.println("불이 켜졌습니다.");
                state = ON; // OFF -> ON으로 상태 변경
            }
        }

        public void turnOff() {
            if(OFF == state) {
                System.out.println("이미 불이 꺼져있습니다.");
            } else { // ON or SLEEPING
                System.out.println("불이 꺼졌습니다.");
                state = OFF; // ON or SLEEPING -> OFF
            }
        }
    }

main

            Light light = new Light(); // state == OFF
            light.turnOff(); 
            light.turnOn();
            light.turnOn();
            light.turnOff();
            /*
            이미 불이 꺼져있습니다.
            불이 켜졌습니다.
            불이 꺼졌습니다.
             * */

상태가 하나 추가됐을 뿐인데 turnOn과 turnOff모두 조건이 추가되고 if문으로 인해 가독성도 떨어지게 되었다.

스테이트 패턴을 적용해 수정해보자.

상태들을 관리할 State 인터페이스

    public interface State { // State를 관리할 인터페이스
        public void turnOn(Light light);
        public void turnOff(Light light);
    }

On상태 클래스

    public class On implements State{
      // 각 상태별로 메서드를 구현시킴으로 별도의 조건문 분기가 필요치 않다.
        @Override
        public void turnOn(Light light) {
            System.out.println("취침모드로 전환됩니다.");
            light.setState(new Sleeping());

        }

        @Override
        public void turnOff(Light light) {
            System.out.println("불이 꺼집니다.");
            light.setState(new On());
        }

    }

Off상태 클래스

    public class Off implements State{

        @Override
        public void turnOn(Light light) {
            System.out.println("불이 켜집니다.");
            light.setState(new On());

        }

        @Override
        public void turnOff(Light light) {
            System.out.println("이미 불이 꺼져있습니다.");
        }

    }

Sleeping 상태 클래스

    public class Sleeping implements State {

        @Override
        public void turnOn(Light light) {
            System.out.println("불이 켜집니다.");
            light.setState(new On());
        }

        @Override
        public void turnOff(Light light) {
            System.out.println("불이 꺼집니다.");
            light.setState(new Off());
        }

    }

Light 클래스

    public class Light {
        private State state;

        public Light() {
            state = new Off(); // 최초 OFF 상태
        }

        public void setState(State state) {
            this.state = state;
        }

        public void turnOn() {
            state.turnOn(this); // interface인 State에 의존함으로 상태가 추가되어도 코드가 변경될 일이 없다.
            // turnOn메서드가 수행되고 Light객체의 상태를 변경시켜주기 위해 this로 Light객체를 전달한다.
        }

        public void turnOff() {
            state.turnOff(this);
        }
    }

main

            Light light = new Light(); // state == OFF
            light.turnOff(); 
            light.turnOn();
            light.turnOn();
            light.turnOff();
            light.setState(new On()); // 이렇게 중간에 state를 지정해줄 수도 있음
            light.turnOn();
            /*
            이미 불이 꺼져있습니다.
            불이 켜집니다.
            취침모드로 전환됩니다.
            불이 꺼집니다.
            취침모드로 전환됩니다.
             * */

이렇게 state pattern을 적용하게 되면 추가적으로 상태가 추가되더라도 Light 클래스 자체는 더이상 수정이 필요하지 않을뿐 아니라 가독성 또한 좋게 된다.

하지만, 이 코드에서도 약간 아쉬운 점이 있는데 각 상태 메서드 내에서 메서드를 수행하고 상태를 변경하기 위해 setState하는 과정에서 상태 객체를 매번 생성하게 된다.

이는 메모리 낭비하게 되어 성능을 저하시킬 수 있음으로 각 상태 객체를 "싱글톤"으로 수정하도록 하자.

최종 state pattern Ex)

On 클래스

    public class On implements State{

        private static On on = null;

        private On() {} // private로 생성을 제한

        public static On getInstance() {
            if(on == null) {
                on = new On();
            }
            return on;
        }

        @Override
        public void turnOn(Light light) {
            System.out.println("취침모드로 전환됩니다.");
            light.setState(Sleeping.getInstance());

        }

        @Override
        public void turnOff(Light light) {
            System.out.println("불이 꺼집니다.");
            light.setState(On.getInstance());
        }

    }

Off 클래스

    public class Off implements State{

        private static Off off = null;

        private Off() {}

        public static Off getInstance() {
            if(off == null)
                off = new Off();

            return off;
        }

        @Override
        public void turnOn(Light light) {
            System.out.println("불이 켜집니다.");
            light.setState(On.getInstance());

        }

        @Override
        public void turnOff(Light light) {
            System.out.println("이미 불이 꺼져있습니다.");
        }

    }

Sleeping  클래스

    public class Sleeping implements State {

        private static Sleeping sleeping = null;

        private Sleeping() {}

        public static Sleeping getInstance() {
            if(sleeping == null)
                sleeping = new Sleeping();

            return sleeping;
        }

        @Override
        public void turnOn(Light light) {
            System.out.println("불이 켜집니다.");
            light.setState(On.getInstance());
        }

        @Override
        public void turnOff(Light light) {
            System.out.println("불이 꺼집니다.");
            light.setState(Off.getInstance());
        }

    }

Light 클래스

    public class Light {
        private State state;

        public Light() {
            state = Off.getInstance(); // 최초 OFF 상태
        }

        public void setState(State state) {
            this.state = state;
        }

        public void turnOn() {
            state.turnOn(this); // interface인 State에 의존함으로 상태가 추가되어도 코드가 변경될 일이 없다.
            // turnOn메서드가 수행되고 Light객체의 상태를 변경시켜주기 위해 this로 Light객체를 전달한다.
        }

        public void turnOff() {
            state.turnOff(this);
        }
    }

main

            Light light = new Light(); // state == OFF
            light.turnOff(); 
            light.turnOn();
            light.turnOn();
            light.turnOff();
            light.setState(On.getInstance()); // 이렇게 중간에 state를 지정해줄 수도 있음
            light.turnOn();
            /*
            이미 불이 꺼져있습니다.
            불이 켜집니다.
            취침모드로 전환됩니다.
            불이 꺼집니다.
            취침모드로 전환됩니다.
             * */

스테이트 패턴은 기본 골격 구성이 스트레티지 패턴(전략패턴)과 같은데 둘의 차이점은

스트레티지 패턴 ⇒ 자주 변동사항이 생기는 메서드 및 항목을 캡슐화

스테이트 패턴 ⇒ 조건 상태를 캡슐화

하는게 차이점이다.

3. Template Method Pattern

Created: Oct 16, 2019 12:09 PM
Tags: template method pattern,템플릿 메서드 패턴

템플릿 메서드 패턴(Temlate Method Pattern)

=> 객체마다 대부분의 기능이 유사하되, 특정 부분만 구현이 다른경우 유용한 패턴. ( 대부분 기능이 유사할 때 발생하는 중복 문제 해결하며 다른 부분만 구현 )

Ex) 민수, 영희, 철수의 등교 패턴을 구현한 객체가 있다 했을 때

민수는 1. 잠에서 깨어나
2. 아침을 먹고
3. 씻고
4. 걸어서 등교
영희는 1. 잠에서 깨어나
2. 씻고
3. 자전거를 타고 등교
철수는 1. 잠에서 깨어나
2. 아침을 먹고
3. 씻고
4. 부모님 차를 타고 등교

라고 했을 때, 잠에서 깨어나, 씻는 부분은 공통으로 반복되고 아침을 먹는 유무, 등교방식만 변하는 것을 알 수 있다.

이때, 큰 틀은 유지하면서 다른 부분만 각 객체에서 구현하도록 할 때 템플릿 메서드 패턴이 유용하다.

템플릿 메서드 패턴 
         : 공통 수행하는 메서드는 상위 부모 추상클래스에서 구현하며 이 메서드를 **"템플릿 메서드"**라 한다.
         템플릿 메서드 내에서 수행할 때 다른 구현부는 하위 자식 객체에서 오버라이딩해 구현하도록 하며,
    이 메서드를 "hook **method/primitive method**"라 한다.

Ex)

Person Interface

public abstract class Person {
    public void goToSchool() { // => template method
        // 1. 잠에서 깬다.(공통)
        wakeUp();

        // 2. 아침먹는건 Optional
        if(isEatBreakfast()) { // hook method
            System.out.println("아침을 먹는다.");
        }

        // 3. 씻는다.(공통)
        washBody();

        // 4. 등교한다.(사람 객체마다 다른 부분) => hood method
        moveToSchool();
    }

    public void wakeUp() {
        System.out.println("잠에서 깨어난다.");
    }

    public boolean isEatBreakfast() { // 일종의 hook method
        return false; // 재구현하지 않으면 false
    }

    private void washBody() {
        System.out.println("씻는다.");
    }

    // hook method로 각 자식 객체(사람객체)마다 재구현해준다.
    public abstract void moveToSchool();
}

MinSoo 클래스

public class MinSoo extends Person{

    // 민수는 아침을 먹음으로 재구현
    @Override
    public boolean isEatBreakfast() {
        return true; // true 로 변경 아침먹는 부분 수행하도록...
    }

    @Override
    public void moveToSchool() {
        System.out.println("걸어서 등교한다.");
    }

}

YoungHee 클래스

public class YoungHee extends Person {

    // 아침은 안먹음으로 isEatBreakfast 구현하지 않으면 부모것 사용해 false

    @Override
    public void moveToSchool() {
        System.out.println("자전거를 타고 등교한다.");
    }

}

ChulSoo 클래스

public class ChulSoo extends Person {

    @Override
    public boolean isEatBreakfast() { // 아침 먹음
        return true;
    }

    @Override
    public void moveToSchool() {
        System.out.println("부모님 차를 타고 등교한다.");
    }

}

main

    MinSoo p1 = new MinSoo();
        p1.goToSchool();
        YoungHee p2 = new YoungHee();
        p2.goToSchool();
        ChulSoo p3 = new ChulSoo();
        p3.goToSchool();
        /*
         잠에서 깨어난다.
        아침을 먹는다.
        씻는다.
        걸어서 등교한다.
        잠에서 깨어난다.
        씻는다.
        자전거를 타고 등교한다.
        잠에서 깨어난다.
        아침을 먹는다.
        씻는다.
        부모님 차를 타고 등교한다. 
         * */

+ Recent posts