객체 지향 프로그래밍 입문 수강 정리

2025, Mar 09    
oop java

강의정보

  • 인프런 강의 주소
  • 강사 : 최범균님
  • 총 2시간 32분
  • 선수지식 : 자바 기본
  • 난이도 : 초급
  • 수강료 : 유료

비용

  • 비용발생 주요 원인
    • 코드 분석 시간 증가 → 코드 변경 시간 증가
  • 소프트웨어의 가치는 = 변화
    • 변화하는 세상속에서 가치있게 사용성있게 만드는 것 . “keep being useful in a changing world” (by jessica kerr)
  • 비용과 변화
    • 낮은 비용으로 변화할 수 있어야 함
      • 방법
        • 패러다임 : 객체지향, 함수형, 리액티브..
        • 코드, 설계, 아키텍처 : dry, tdd, solid, ddd, 클린아키텍처, msa…
        • 업무 프로세스/ 문화 : 애자일, devops

객체

  • 객체 지향 vs 절차 지향
    • 절차 지향 : 데이터를 여러 프로시저가 공유하는 방식
      • 단점: 시간이 흐를수록 수정이 어렵다.
    • 객체 지향 : 데이터와 프로시저를 객체 단위로 묶고, 다른 객체는 다른 객체를 바로 조회할 수 없다.
      • 객체는 프로시저 호출을 통해서 데이터에 접근하게 된다.
  • 객체의 핵심 → 기능으로 정의된다. 어떤 기능을 제공하는가.
    • 내부적으로 가진 필드(데이터)로 정의하지 않는다. 기능으로 정의한다.
      • 예를 들어서, 회원 객체는 암호 변경하기 기능으로 정의한다.
  • 객체와 객체
    • 객체끼리는 기능을 사용해서 연결한다. (기능 사용 = 메서드 호출)
  • 메시지
    • 객체끼리의 상호 작용 = 메시지를 주고받는다 와 같다.
      • 즉, 메서드를 호출하는 메시지, 리턴하는 메시지, 익셉션 메시지 등등을 포함해서 표현하는 것
  • 일반 엔티티+ vo 객체는 = 데이터 개념의 클래스로만 보고, 객체라고 하지 않는다.
  • 메서드를 이용해서 기능을 명세한다
    • 이름, 파라미터, 결과로 구성하는 것

캡슐화

  • 정의
    • 데이터와 관련 기능을 묶는 것을 말한다
    • 객체가 기능을 어떻게 구현했는지, 데이터의 상세 내용을 외부에 감추는 것
    • 정보 은닉 의미 포함
    • 캡슐화를 통해 기능을 사용하는 코드에 영향을 주지 않고 (또는 최소화) 내부 구현을 변경할 수 있는 유연함을 갖게 한다.
  • 캡슐화하는 이유는?
    • 외부에 영향 없이 객체 내부 구현 변경을 가능하게 하기 위해서
    • 캡슐화된 코드만 봐도 기능에 대한 (의도) 이해를 높일 수 있게 된다.
  • 캡슐화하지 않으면
    • 반복되는 비지니스 코드가 여러곳에 산재되어 있게 되고, 변경되는 요구사항을 따라갈 수 없다.
    • 요구사항의 변화가 데이터 구조/사용에 변화를 발생시킨다.
  • 캡슐화한다는 것은?
    • 기능을 제공하고 구현 상세를 안쪽으로 감춘다는 것
    • 캡슐화된 객체
      public class Account {
      		private Membership membership;
      		private Date expDate;
        		
      		public boolean hasRegularPermission() {
      			return memberhsip == REGULAR && expDate.isAfter(now())
      		}
        
      }
    
  • 캡슐화 규칙
    • Tell, Don’s Ask 데이터 달라하지 말고 해달라고 하기
      • acc.getMembership() == REGULAR (x) → acc.hasRegularPermission()
    • Demeter’s Law
      • 메서드에서 생성한 객체의 메서드만 호출하는 것
      • 파라미터로 받은 객체의 메서드만 호출하는 것
      • 필드로 참조하는 객체의 메서드만 호출하는 것
        ex1) 
        acc.getExpDate().isAfer(now) (x) -> acc.isExpired()
              
        ex2)
        Date date = acc.getExpDate();
        data.isAfter(now); 
        --> acc.isValid(now);
      

다형성과 추상화

  • 다형성
    • 여러 모습을 갖는것
    • 객제 지향에서는 한 객체가 여러 타입을 갖는 것
      • 즉, 한 객체가 여러 타입의 기능을 제공
      • 타입 상속으로 다형성 구현
        • 하위 타입은 상위 타입도 됨
  • 추상화
    • 데이터나 프로세스 등을 의미가 비슷한 개념이나 의미있는 표현으로 정의하는 과정
    • 방법
      • 두 가지 방식의 추상화
      • 특정한 성질 또는 공통 성질 뽑아서 일반화
      • 추상 타입을 이용한 프로그래밍을 한다.
        • 추상타입은 구현을 감춤으로써 기능의 구현이 아닌 의도를 더 잘 드러나게 해준다.
    • 서로 다른 구현을 추상화하는 것
    • 타입 추상화
      • 여러 구현 클래스를 대표하는 상위 타입 도출
        • 흔히 인터페이스 타입으로 추상화
          • 인터페이스는 기능에 대한 의미를 제공할 뿐, 구현은 하지 않는다. (어떻게 구현할지 알수 없다)
        • 추상화 타입과 구현은 타입 상속으로 연결
    • 장점
      • 유연해진다.
    • 단점
      • 아직 존재하지 않는 기능에 대한 이른 추상화는 주의한다. → 잘못된 추상화 가능성 및 복잡도만 증가하므로 변경,확장이 발생할때 추상화를 시도한다.
      • 추상 타입이 증가하면 그만큼 복잡도도 증가한다.
    • 추상화가 필요한 시점은?
      • 추상화는 의존 대상이 변경하는 시점에 한다.
    • 추상화를 잘 하려면 “구현을 한 이유가 무엇 때문인지 생각해야 한다”
    • opc를 따르는 구로조 구현하도록 한다.
      • 수정엔 닫혀있고확장엔 열려있도록

상속보단 조립

  • 상속 단점
    • 상위 클래스 변경 어려움
      • 상위 클래스를 변경하면, 변경의 여파가 계층을 따라 하위 클래스에 영향이 전파됨
      • 앞으로 어떤 하위 클래스가 추가될지 알수 없으므로, 하위클래스가 증가할 수록 상위 클래스를 변경하기 어려움 → 즉 캡슐화가 약해진다.
    • 클래스 증가
      • 상속받은 하위 클래스들끼리의 조합이 필요한 요구사항이 생길때, 또 다른 하위클래스가 양산되게 되므로 애매한 상황 발생
    • 상속 오용
      • 상속 오용을 막기 위해 조립을 사용한다.
      • 오용 예
        • Container가 ArrayList의 진짜 하위타입이 아닌데, 잘못사용함으로써 put 메서드를 오용하게 된다.
          public class Container extends ArrayList<Luggage> {
          	private int maxSize;
                    	
          	private int currentSize;
                    	
          	..
          	public void put(Luggage lug) {
          		if (!canContain(lug))
          				throw new NotEnoughSpaceException();
          		super.add(lug);
          		currentSize += lug.size();
          	}
                    
          }
        
  • 조립
    • 여러 객체를 묶어서 더 복잡한 기능을 제공할때 사용한다. 상속보다는..조립을 통해 기능 재사용을 하도록 한다.
    • 보통 필드로 다른 객체를 참조하는 방식으로 조립 또는 객체를 필요시점에 생성하거나 구하도록 한다.
  • 상속하기에 앞서 조립으로 풀 수 없는지 검토하고 진짜 하위 타입인 경우에만 상속을 사용한다.

기능과 책임 분리

  • 분리한 하위기능을 누가 제공할지 결정하는 것 = 객체 지향
    • 기능은 곧 책임
    • 분리한 각 기능을 알맞게 분리
  • 하지만 클래스나 메서드가 커지면, 절차 지향과 동일한 문제가 발생한다
    • 큰 클래스 ⇒ 많은 필드를 많은 메서드가 공유
    • 큰 메서드 ⇒ 많은 변수를 많은 코드가 공유
    • 여러 기능이 한 클래스/메서드에 섞여있을 가능성
  • 책임에 따라 알맞게 코드 분리 필요
    • 방법
      • 패턴 적용
        • 전형적인 역할 분리
          • 간단한 웹의 경우 : 컨트롤러, 서비스, dao
          • 복잡한 도메인 : 엔티티, 밸류, 리포지토리, 도메인 서비스
          • AOP : aspect(공통 기능)
          • GoF : 팩토리, 빌더, 전략, 템플릿 메서드, 프록시/데코레이터 등
      • 계산 기능 분리
      • 외부 연동 분리
      • 조건별 분리는 추상화
    • 주의: 의도가 잘 드러나는 이름사용
    • 장점
      • 역할분리가 잘되면 테스트도 용이해진다.

의존과 DI

  • 의존이란?
    • 기능 구현을 위해 다른 구성요소를 사용하는 갓 : 객체 생성이나 메서드 호출, 데이터 사용 등
    • 의존은 변경이 전파될 가능성을 의미한다.
      • 즉, 의존하는 대상이 바뀌면 자신도 바뀔 가능성이 높아진다.
        • 호출하는 메서드의 파라미터가 변경되거나, 호출하는 메서드가 발생할 수 있는 익셉션 타입이 추가된다거나
  • 순환 의존
    • 변경 연쇄 전파 가능성이 있다는 것으로
    • 클래스, 패키지, 모듈 등 모든 수전에서 순환 의존이 없도록 해야한다.
  • 의존하는 대상은 적을 수록 좋다.
    • 대상이 많다는 것은? = 하나의 클래스에서 많은 기능을 제공하는 경우
      • 각 기능마다 의존하는 대상이 다를 수 있다.
      • 한 기능 변경이 다른 기능에 영향을 줄수 있다.
      • 테스트하기도 어려워진다.
      • 이 경우, 기능별로 분리를 고려해본다.
    • 묶어보기 로 검토해본다
      • 몇 가지 의존 대상을 단일 기능을 묶어서 추상화해서 제공하면, 의존 대상을 줄 일 수 있게 된다.
    • 의존 대상 객체를 직접 생성한다면?
      • 직접 생성할 경우, 생성 클래스가 바뀌면 의존하는 코드도 바꿔야하므로.. (추상화의 단점)
      • 의존 대상 객체를 직접 생성하지 않고 사용하는 방식을 사용해본다.
        • 팩토리, 빌더
        • DI
        • 서비스 로케이터
  • DI(의존성 주입)
    • 외부에서 의존 객체를 주입한다는 것 = 생성자나 메서드를 이용해서 주입
    • 조립기 사용(assembler) = 스프링 프레임워크 같은 것을 사용한다는 것
      • 조립기가 객체 생성 및 의존 주입을 처리한다 (예 : 스프링 프레임워크)
    • 장점
      • 상위 타입을 사용할 경우, 의존대상이 바뀌면 조립기(설정)만 변경하면 된다.
      • 의존하는 객체를 실체 구현안해도 대역 객체를 사용해서 테스트 가능
    • 의존 객체를 주입받아서 장점을 누리도록 코딩하는 습관을 갖자.

DIP

  • 고수준 모듈, 저수준 모듈
    • 고수준 모듈
      • 의미있는 단일 기능을 제공
      • 상위 수준의 정책 구현
    • 저수준 모듈
      • 고수준 모듈의 기능을 구현하기 위해 필요한 하위 기능의 실제 구현
    • 예시:
      • 고수준: 도면 이미지를 저장하고, 측정 정보를 저장하고, 도면 수정 의뢰를 한다
      • 저수준: NAS에 이미지를 저장하고, MEAS_INFO 테이블에 저장하고, BP_MOD_REQ 테이블에 저장한다.
  • 고수준이 저수준에 직접 의존하면?
    • 그렇게 되면, 저수준 모듈이 변경되었을때 고수준 모듈에도 영향이 미치게 되고 코드 변경이 필요하게 된다.
  • 위의 상황을 방지하기 위한 방법 ⇒ 의존성 역전의 원칙
    • 고수준 모듈은 저수준 모듈의 구현에 의존하면 안된다
    • 저수준 모듈이 고수준 모듈에서 정의한 추상타입에 의존해야 한다.
    • 추상화는 고수준 관점에서 해야한다.
      • 고수준 입장에서 저수준 모듈을 추상화해야하며, 구현 입장에서 추상화하지 말아야한다.
  • dip 장점
    • 고수준 모듈의 변경을 최소화할 수 있고
    • 저수준 모듈의 변경 유연성을 높여준다.
  • 부단한 추상화 노력 필요
    • 처음부터 좋은 설계가 나오지 않음 → 요구사항/업무 이해가 높아지면서 저수준 모듈을 인지하고 상위 수준 관점에서 저수준 모듈에 대한 추상화를 시도해야한다.

수강 후기

  • 실습은 없지만, 현장에서 고민하는 것을 예시로 들면서 설명을 들을 수 있어서, 기억에 많이 남게 된다!!
  • 다시 한번 기억을 더듬으면서 복습하는 시간을 가질 수 있었다.
  • 내년에 또 복습해야겠다.