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();
        /*
         잠에서 깨어난다.
        아침을 먹는다.
        씻는다.
        걸어서 등교한다.
        잠에서 깨어난다.
        씻는다.
        자전거를 타고 등교한다.
        잠에서 깨어난다.
        아침을 먹는다.
        씻는다.
        부모님 차를 타고 등교한다. 
         * */

[ 자바8 - 스트림(Stream) ]



1. 스트림(Stream)이란?
  : 자바8부터 추가된 기능으로 "컬렉션", "배열"등의 저장 요소를 하나씩 순차적으로 참조하며
    함수형 인터페이스(람다식)을 적용해 반복적으로 처리할 수 있도록 해주는 기능이다.
    (for문,if문등으로 처리하는 것보다 한줄 두줄로 간단하게 처리가 가능하다.)

2. 스트림의 구조
  1) 스트림의 생성
  2) 중개 연산 : 스트림에서 특정 조건에 해당하는 결과의 스트림을 생성한다.
  3) 최종 연산 : 스트림의 항목들을 통해 특정 결과값을 도출한다.

  ex) Collection등의 객체집합.스트림생성().중개연산1().중개연산2().중개연산n().최종연산();

3. 사용법
  1. 스트림의 생성
    List studys = Arrays.asList("Java","Phython","Oracle","MySQL");
    studys.stream(); // 스트림 생성 방법 1
    studys.parallelStream(); // 여러 쓰레드를 통해 처리하는 병렬 스트림 생성(장단점이 있음)
    // 요소가 적거나 프로젝트 내에 사용되는 쓰레드 개수가 많을 경우 오히려 오버헤드가 생길 수 있음.

  2. 중개 연산
    1) Filter : 특정 조건에 맞는 요소들만 추려내 새로운 stream을 생성한다.
      List studys = Arrays.asList("Java","Phython","Oracle","MySQL");
      studys.stream().filter((x)->(x.contains("y"))); // (y가 포함된 Phython, MySQL를 갖는 스트림을 리턴한다.)

    2) Map
      : 각요소마다 특정 연산을 한 결과를 갖는 스트림을 생성한다. (1,2,3) 스트림에 각요소에 2씩 곱한 스트림 -> (2,4,6) 처럼 사용
      List studys = Arrays.asList("Java","Phython","Oracle","MySQL");
      studys.stream().map(x -> x.concat("END")); // 각 요소에 END문자열을 붙인 스트림을 리턴

    3) sorted : 정렬된 결과 stream을 리턴
List studys = Arrays.asList("Java","Phython","Oracle","MySQL");
studys.stream().sorted(); // 오름차순 정렬
studys.stream().sorted(Comparator.reverseOrder()); // 역순 정렬
studys.stream().sorted((a,b) -> { // Comparator 직접 구현을 통한 sorted메서드 사용
return Integer.compare(a.length(), b.length());
});

    4) distinct : stream내의 중복을 제거
       studys.stream().distinct();

    5) limit : stream요소에서 n개까지의 항목을 포함한 stream을 리턴함
       skip : stream요소에서 앞 n개를 제외하고 stream을 리턴함

       studys.stream().limit(3);
 studys.stream().skip(3);

    6) mapToLong, mapToInt, mapToDouble : 기존 stream요소를 Long, Int, Double형 항목을 갖는 스트림으로 리턴

       studys.stream().mapToLong((num)->(Long.parseLong(num)));

    7) 최종 연산
// 요소의 출력
// 1) forEach : 반복을 돌며 처리
studys.stream().forEach(System.out::println);
// 2) reduce : 각 항목을 순회하며 결과를 누적하여 반환
studys.stream().reduce((a,b) -> a + "," + b); // Java,Phython,Oracle,MySQL 리턴(누적한 문자열)
// 3) findFirst(), findAny() : stream의 첫번째 항목요소를 Optional 타입으로 반환한다.
// 두 최종연산 모두 비어있는 스트림에서 빈 Optional객체를 리턴함
// 병렬 스트림의 경우 findAny()메서드를 사용해야 정확한 연산 결과를 반환할 수 있다.
Optional result1 = studys.stream().findFirst();
// OptionalInt result2 = studys.stream().findFirst();
// result2.getAsInt();

// 4) 요소의 검사
// 1. anyMatch() : 해당 스트림의 일부 요소가 특정 조건을 만족할 때 true반환
// 2. allmatch() : 해당 스트림 모든 요소가 특정 조건을 만족할 때 true반환
// 3. noneMatch() : 해당 스트림 모든 요소가 특정 조건을 만족하지 않을 때 true반환
studys.stream().allMatch((x)->(x.contains("y"))); // 모든요소가 y를 포함할 때 true아니면 false

// 5) 요소의 통계
// count(), min(), max()
studys.stream().count();
studys.stream().min(Comparator.naturalOrder());

// 6) 요소의 연산
// sum(), avaerage()
studys.stream().mapToInt((x)->(Integer.parseInt(x))).sum();

// 7) stream을 List등의 컬렉션으로 리턴 : collect()
List str = studys.stream().collect(Collectors.toList());

// 각 요소의 길이가 짝수이면 true, List<짝수문자열항목>으로, 홀수이면 false, List<홀수문자열항목>으로 저장
Map<Boolean, List> temp = studys.stream().collect(Collectors.partitioningBy((x)->(x.length()%2==0)));
List eventStr = temp.get(true);  // 짝수집합
List oddStr = temp.get(false); // 홀수집합

 

-----------------------------------------------------------------------------------------------------------------------------------

List str = Arrays.asList("ao","b","co");
str.stream().filter(x->x.contains("o"));
str.stream().mapToInt(x->Integer.parseInt(x));
str.stream().limit(2);
str.stream().skip(1);
str.stream().sorted();
str.stream().sorted(Comparator.reverseOrder());
str.stream().findFirst().get();
str.stream().allMatch(x->x.contains("o"));
str.stream().collect(Collectors.toList());
Map<Boolean,List> temp = str.stream().collect(Collectors.partitioningBy(x->x.length()%2==0));
List evenStr = temp.get(true);
List oddStr = temp.get(false);
str.stream().forEach(System.out::printf);
str.stream().count();
str.stream().reduce((a,b)->(a+b));

'스터디 > Java' 카테고리의 다른 글

직렬화(serializable)와 serialVersionID란?  (0) 2018.10.22
자바 정규표현식  (0) 2018.10.19

[ 직렬화(serializable)와 serialVersionID란? ]



객체를 파일에 쓰거나, 파일에서 객체로 읽어오거나 혹은 네트워크를 통해 객체를 전송할 때 serializable 인터페이스를 구현(직렬화)하는 것을 본적 있을 것이다.


그림 직렬화란 무엇일까?

쉽게 말해


직렬화??

 - 객체를 전송하는 것은 파일로도 네트워크를 통해 서버로, 프로그램과 프로그램 사이에 많을 것이고

   또한, 서로 다른 언어를 사용하는 이들이 데이터를 주고 받으려면 주고받는 쪽이 모두 이해할 수 있는 언어가 필요할 것이다.

예를들자면

A -> B로 보낼 때 A는 프랑스 사람이고 B는 한국 사람인데 둘 다 서로의 언어는 모르지만 둘 다 영어는 할 줄 안다치면

둘이 데이터를 주고받을 때 객체를 영어로 변환(직렬화)하는 것이다. 그리고 주고 받으면 자신들의 언어로 역직렬화 하는 것!


그럼 비유를 마저 들어

serialVersionID는 무엇일까?

- 서로 데이터를 주고 받는 과정에서 여러군대와 주고받다보면 어떤 클래스인지 정확히 구분이 어려운 경우가 있을 것이다.

  즉, 주고받은 클래스 객체를 고유하게 구분하는 코드와 같은 것!


따라서, 주로 JAVA DTO 클래스에 implements Serializable했을 때 

final static Long serialVersionID = ~~ 라고 지정을 하지 않으면 노란색 삼각형으로 warning이 발생하는 것을 볼 수 있다.

하지만, 구현하지 않을 경우는 자바 컴파일러가 자동으로 default로 생성해주게 된다.

하지만, 경고는 보기 싫으니까 클래스 명에서 ctrl + 1눌러 serialVersionID를 자동으로 생성해주도록 하자.

(간혹 자동생성이 안뜨는 경우... 자동생성 플러그인을 이용하자.)


 

'스터디 > Java' 카테고리의 다른 글

스트림(Stream) - Java8  (0) 2019.07.11
자바 정규표현식  (0) 2018.10.19

+ Recent posts