ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아이템20. 추상 클래스보다 인터페이스를 우선하라.
    Java/이펙티브 자바 2023. 8. 31. 11:51

    핵심 정리 - 인터페이스의 장점

    - 자바 8부터 인터페이스도 디폴트 메서드를 제공할 수 있다. (완벽 공략 3)

    public interface TimeClient {
    
        void setTime(int hour, int minute, int second);
        void setDate(int day, int month, int year);
        void setDateAndTime(int day, int month, int year, int hour, int minute, int second);
    
        LocalDateTime getLocalDateTime();
    
        static ZoneId getZonedId(String zoneString) {
            try {
                return ZoneId.of(zoneString);
            } catch (DateTimeException e) {
                System.err.println("Invalid time zone : " + zoneString + "; using default time zone instead");
                return ZoneId.systemDefault();
            }
        }
    
        default ZonedDateTime getZonedDateTime(String zoneString) {
            return ZonedDateTime.of(getLocalDateTime(), getZonedId(zoneString));
        }
    }

        ㆍ인스턴스의 필드를 사용하는 경우 default 메서드를 사용할 수 없다. - 추상 클래스를 사용해야 한다

     

    - 기존 클래스도 손쉽게 새로운 인터페이스를 구현해 넣을 수 있다.

    - 인터페이스는 믹스인(mixtin) 정의에 안성맞춤이다. (선택적인 기능 추가)

        ㆍ부가적인 implements

    - 계층구조가 없는 타입 프레임워크를 만들 수 있다.

    public interface SingerSongwriter extends Singer, Songwriter {
        
        AutoClip strum();
        void actSensitive();
    }

    - 래퍼 클래스와 함께 사용하면 인터페이스는 기능을 향상 시키는 안전하고 강력한 수단이 된다. (아이템 18)

    - 구현이 명백한 것은 인터페이스의 디폴트 메서드를 사용해 프로그래머의 일감을 덜어 줄 수 있다.

     

    핵심 정리  - 인터페이스와 추상 골격(skeletal) 클래스

    - 인터페이스와 추상 클래스의 장점을 모두 취할 수 있다.

        ㆍ인터페이스 - 디폴트 메서드 구현

        ㆍ추상 골격 클래스 - 나머지 메서드 구현

        ㆍ템플릿 메서드 패턴

    - 다중 상속을 시뮬레이트할 수 있다.

    public class MyCat extends AbstractCat implements Flyable {
        private MyFlayable myFlayable = new MyFlayable();
    
        @Override
        protected String sound() {
            return "인싸 고양이 두 마리가 나가신다!";
        }
    
        @Override
        protected String name() {
            return "유미";
        }
    
        public static void main(String[] args) {
            MyCat myCat = new MyCat();
            System.out.println(myCat.sound());
            System.out.println(myCat.name());
            myCat.fly();
        }
    
        @Override
        public void fly() {
            this.myFlayable.fly();
        }
    
        private class MyFlayable extends AbstractFlyable {	// 다중 상속
            @Override
            public void fly() {
                System.out.println("날아라.");
            }
        }
    }

    - 골격 구현은 상속용 클래스이기 때문에 아이템 19를 따라야 한다.

     

    완벽 공략

    - p132, 템플릿 메서드 패턴

    - p135, 디폴트 메서드는 equals, hashCode, toString 같은 Object 메서드를 재정의 할 수 없기 때문이다.

     

    완벽 공략 - 템플릿 메서드 패턴

    : 알고리즘 구조를 서브 클래스가 확장할 수 있도록 템플릿으로 제공하는 방법

    - 추상 클래스는 템플릿을 제공하고 하위 클래스는 구체적인 알고리즘을 제공한다.

     

    템플릿 메서드 패턴

    public abstract class FileProcessor {
        private String path;
    
        public FileProcessor(String path) {
            this.path = path;
        }
    
        public final int process(BiFunction<Integer, Integer, Integer> operator) {
            try(BufferedReader reader = new BufferedReader(new FileReader(path))) {
                int result = 0;
                String line  = null;
                while((line = reader.readLine()) != null) {
                    result = getResult(result, Integer.parseInt(line)); // 템플릿 메서드 패턴
                }
                return result;
            } catch (IOException e) {
                throw new IllegalArgumentException(path + "에 해당하는 파일이 없습니다.", e);
            }
        }
    
        protected abstract int getResult(int result, int number);
    }
    public class Plus extends FileProcessor {
        public Plus(String path) {
            super(path);
        }
    
        @Override
        protected int getResult(int result, int number) {
            return result + number;
        }
    }
    public static void main(String[] args) {
        FileProcessor fileProcessor = new Plus("number.txt");
        System.out.println(fileProcessor.process());
    }

     

    템플릿 콜백 패턴

    public class FileProcessor {
        private String path;
    
        public FileProcessor(String path) {
            this.path = path;
        }
    
        public final int process(BiFunction<Integer, Integer, Integer> operator) {
            try(BufferedReader reader = new BufferedReader(new FileReader(path))) {
                int result = 0;
                String line  = null;
                while((line = reader.readLine()) != null) {
                    result = operator.apply(result, Integer.parseInt(line));    // 템플릿 콜백 패턴
                }
                return result;
            } catch (IOException e) {
                throw new IllegalArgumentException(path + "에 해당하는 파일이 없습니다.", e);
            }
        }
    
    }
    public static void main(String[] args) {
        FileProcessor fileProcessor = new FileProcessor("number.txt");
        System.out.println(fileProcessor.process(Integer::sum));
    }

     

    완벽 공략 - 디폴트 메서드와 Object 메서드

    : 인터페이스의 디폴트 메서드로 Object 메서드를 재정의 할 수 없는 이유

    // 틀린 예제
    public interface MyInterface {
        default String toString() {
            return "myString";
        }
        default int hashCode() {
            return 10;
        }
        default boolean equals(Object o) {
            return true;
        } 
        
    }

    - 디폴트 메서드 핵심 목적은 "인터페이스의 진화".

    - 두가지 규칙만 유지한다.

        ㆍ"클래스가 인터페이스를 이긴다."

    public class MyClass extends Object implements MyInterface {
        
    }
    
    // 만약 MyInterface의 메서드를 사용한다면 위와 같은 법칙을 어기는 것이다.

        ㆍ"더 구체적인 인터페이스가 이긴다."

    - 토이 예제에나 어울리는 기능이다. 실용적이지 않다

    - 불안정하다.

     

    댓글

Designed by Tistory.