본문 바로가기

BOOK Review

[스프링입문을위한 자바 객체지향의 원리와 이해] 3장 ~ 5장 객체 지향

3장 자바와 객체지향

객체 지향은 현실 세계를 반영한다
  • 객체 지향의 4대 특성
    • 캡슐화(Encapsulation) - 정보 은닉
    • 상속(Inheritance  extends) - 재사용
    • 추상화(Abstration) - 모델링
    • 다형성 (Polymorphism) - 사용 편의
  • 추상화
    • 구체적인 것을 분해해서 관심 영역에 대한 특성만을 가지고 재조합 하는 것
      • ex) 은행 -> 사람 : 고객, 병원 -> 사람 : 환자
    • 사용
      • 클래스 설계에서 사용
      • 상속을 통한 추상화 및 구체화
      • 인터페이스를 통한 추상화
      • 다형성을 통한 추상화
  • 상속 : 재사용 + 확장
    • 상위 클래스의 특성을 하위 클래스가 확장하는 것
    • 상속 관계에서 만족해야할 것
      • "하위 클래스는 상위 클래스다"
      • 상속은 "is a kind of" 관계 : 하위 클래스 is a kind of 상위 클래스
    • 다중 상속의 다이아몬드 문제
      • 인어 공주 : 물고기, 사람을 모두 상속 받는 개념. 한 메서드를 호출했을 때 둘 중 어느 메서드를 택해야할지
    • 인터페이스 "be able to" 관계 : '할 수 있다' 라는 기능을 구현하도록 강제하게 됨
    • 상위 클래스는 특성이 풍성할수록(LSP 원칙) , 인터페이스는 구현을 강제할 메서드의 개수가 적을 수록 좋음(ISP 원칙)
    • 상속의 메모리 구조
      • 하위 클래스의 인스턴스가 생성될 때 상위 클래스의 인스턴스도 함께 생성됨
Penguin pororo = new Penguin();
Animal pingu = new Penguin(); // 펭귄 클래스에 단독 정의된 메서드 사용 불가
// Penguin 클래스의 객체를 갖지만 Animal로 참조되었으므로, 형변환이 없으면 사용 불가

 

 

  • 다형성 : 사용 편의성
    • 하나의 객체가 여러 가지 타입을 가질 수 있는 성질. 다형성을 통해 상위 클래스나 인터페이스를 기반으로 다양한 하위 클래스의 객체를 동일한 방식으로 처리 가능
    • 상위 클래스 타입의 객체 참조 변수를 사용하더라도 하위 클래스에서 오버라이딩한 메서드가 호출됨
      • 오버라이딩한 메소드를 자동으로 호출해줌으로서 간결한 코드 작성 가능
      •  
    • 오버라이딩 : 같은 메서드 이름, 같은 인자 목록으로 상위 클래스의 메서드를 재정의
    • 오버로딩 : 같은 메서드 이름, 다른 인자 목록으로 다수의 메서드를 중복 정의
동물[] 동물들 = new 동물[5];

동물들[0] = new 쥐();
동물들[1] = new 고양이();
동물들[2] = new 강아지();

for(int i = 0; i < 3; i++) {
	동물들[i].울어보세요();
} 

/*
* 찍찍
* 야옹
* 멍멍
*/

 

  • 캡슐화 : 정보 은닉
    • 접근 제어자
      • public : 모두가 접근 가능
      • protected : 상속 / 같은 패키지 내의 클래스에서 접근 가능
      • [default] : 같은 패키지 내
      • private : 같은 클래스 내
    • 하위 클래스에서는 상위 클래스보다 더 넓은 범위의 메서드만 작성 가능

4장 자바가  확장한 객체 지향

  • abstract 키워드 - 추상 메서드와 추상 클래스
    • 선언부는 있는데 구현부가 없는 메서드
    • 하위 클래스에게 메서드의 구현을 강제(오버라이딩 강제)
    • 추상 클래스 : 인스턴스 생성 불가 
      • 목적
        • 상속으로 구현하게 되면 상위 클래스의 기본 메서드에 구현을 하는 것이 어색할 때가 있음 (동물 클래스의 '울어보세요'는 어떻게 구현해야할지)
        • 하위 클래스에서 모두 동일한 메서드를 호출하고자 할 때 메서드의 구현을 강제할 필요가 있음
    • 추상 메서드를 포함하는 클래스는 반드시 추상 클래스여야함
  • interface 키워드
    • 추상 메서드와 public 정적 상수만 가질 수 있음
      • 자바 8 이후에는 람다 도입을 위해 객체 구현 메서드 - 디폴트 메서드 , 정적 추상(static abstract) 메서드지원
interface Speakable {
	public static final double PO = 3.14159; // static final 자바에서 자동 주입(생략 가능)
    
    public abstract void sayYes(); // abstract 자바에서 자동 주입(생략 가능)


}

 

  • 자바 구성 요소
    • 패키지 : 네임스페이스
    • 클래스 : 분류, 같은 속성과 기능을 가진 객체를 총칭하는 개념
    • 객체 : 실체, 세상에 존재하는 유일무이한 사물
    • 메서드 : 기능 / 행위 

 


5장 객체 지향 설계 5원칙 - SOLID

- SRP (Single Responsebility Principle) : 단일 책임 원칙
- OCP (Open Closed Principle) : 개방 폐쇄 원칙
- LSP (Liskov Substitution Principle) : 리스코프 치환 원칙
- ISP (Interface Segregation Principle) : 인터페이스 분리 원칙
- DIP (Dependency Inversion Principle) : 의존성 역전 원칙

 

  • 결합도와 응집도
    • 좋은 소프트웨어 설계 : 낮은 결합도, 높은 응집도
    • 결합도 : 모듈(클래스)간의 상호 의존 정도. 낮을수록 객체의 재사용이나 수정, 유지보수가 용이
    • 응집도 : 하나의 모듈 내부에 존재하는 구성 요소들의 기능적 관련성. 높을수록 하나의 책임에 집중하고 독립성이 높아져 재사용이나 유지보수가 용이
  • SRP (Single Responsebility Principle) : 단일 책임 원칙
    • "어떤 클래스를 변경해야 하는 이유는 오직 하나뿐이여야 한다"
    • 위배 사례
      • 하나의 클래스에서 사용하지 않는 여러 메서드가 구현되는 경우
      • 하나의 속성이 여러 의미를 갖는 경우 (if문이 잦음)
    • 위배하게 된다면 다른 곳에서 해당 객체의 특성을 변경 가능하다는 위험이 있음
  • OCP (Open Closed Principle) : 개방 폐쇄 원칙
    • "소프트웨어 엔티티(클래스, 모듈, 함수 등)은 확장에 대해서는 열려 있어야 하지만 변경에 대해서는 닫혀 있어야 함
    • 위배 사례
      • 다른 차를 상속받게 될 때 '자동 개방'같은 함수로 인해 차종을 바꾸니 운전자의 행동에도 변화가 오게 됨 (메서드 호출시에 함수를 바꿔야함)
      • -> 상위 클래스 또는 인터페이스를 중간에 둠으로써 운전자의 행동에 변화를 가지지 않아도 됨
    • 위배하게 된다면 유연성, 재사용성, 유지 보수성을 얻을 수 없음
  • LSP (Liskov Substitution Principle) : 리스코프 치환 원칙
    • "서브 타입은 언제나 자신의 기반타입(base type)으로 교체할 수 있어야 한다"
    • 아래 두 규칙을 만족한다면 리스코프 치환 원칙을 잘 지키는 것
      •  하위 클래스 is a kind of 상위 클래스 - 하위 분류는 상위 분류의 한 종류
      • 구현 클래스 is able to 인터페이스 - 구현 분류는 인터페이스할 수 있어야 함
    • 하위 클래스의 인스턴스는 상위형 객체 참조 변수에 대입해 상위 클래스의 인스턴스 역할을 하는데 문제가 없음
Animal penguin = new Penguin();

// Animal 의 행동을 수행하는데 문제가 없어야 함

 

 

  • ISP (Interface Segregation Principle) : 인터페이스 분리 원칙
    • "클라이언트는 자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안된다"
    • SRP와 ISP는 같은 문제에 대한 두 가지 해결책
      • 각 역할에 맞는 인터페이스를 생성하여 이를 한 클래스에서 구현
      • 인터페이스 최소주의 원칙
        • 인터페이스를 통해 메서드를 외부에 제공할 때는 최소한의 메서드만 제공
        • 그렇게 해야 각 책임에 대한 행동만을 수행할 수 있음
      • 특별한 경우가 아니라면 단일 책임 원칙을 제공하는게 더 좋은 해결책

분리되지 않은 클래스

단일 책임 원칙으로 재구현한 사례

인터페이스 분리 원칙으로 재구현한 사례

 

  • DIP (Dependency Inversion Principle) : 의존성 역전 원칙
    • "고차원 모듈은 저차원 모듈에 의존하면 안된다. 이 두 모듈 모두 다른 추상화된 것에 의존해야한다. 추상화된 것은 구체적인 것에 의존하면 안된다."
    • 구체적인 것에 의존하게 된다면 구현체가 바뀔때 마다 수정이 필요하게 됨 (OCP 위배)
      • -> 이를 위해 중간에 추상화된 인터페이스를 추가해 의존 관계를 역전 시키고 있음
      • "자기보다 변하기 쉬운 것에 의존하던 것을 추상화된 인터페이스나 상위 클래스를 두어 변화기 쉬운 것의 변화에  영향받지 않게 하는 것" = 의존 역전 원칙