본문 바로가기
반응형
오브젝트

2. 객체지향 프로그래밍

by brightGarden02 2022. 10. 14.

진정한 객체지향 패러다임으로의 전환은 객체에 초점을 맞출 때 얻을 수 있다

클래스가 아니다.

 

객체지향 프로그래밍을 할 때 집중해야할 두가지

1. 어떤 클래스가 필요한지 고민하기 전에 어떤 객체들이 필요한지 고민하라

(클래스는 공통적인 상태와 행동을 공유하는 객체들을 추상화한 것)

→ 시나리오를 만들자

 

2. 객체를 독립적인 존재가 아니라 기능을 구현하기 위해 협력하는 공동체의 일원으로 봐야 한다.

2-1. 객체들의 모양과 윤곽 잡기

2-2. 공통된 특성과 상태를 가진 객체들을 타입으로 분류하기

2-3. 이 타입을 기반으로 클래스를 구현하기

 

도메인: 문제를 해결하기 위해 사용자가 프로그램을 사용하는 분야

 

소프트웨어는 사용자가 원하는 어떤 문제를 해결하기 위해 만들어진다.

 

객체지향 패러다임이 강력한 이유

: 요구사항을 분석하는 초기 단계부터 프로그램을

구현하는 마지막단계까지 객체라는 동일한 추상화 기법을 사용할 수 있기 때문

 

클래스를 구현하거나 다른 개발자에 의해 개발된 클래스를 사용할 때 가장 중요한 것은

클래스의 경계를 구분 짓는 것이다.

 

클래스는 내부와 외부로 구분되며 훌륭한 클래스 설계하기 위한 핵심은

어떤 부분을 외부에 공개하고 어떤 부분을 감추는지를 결정하는 것이다.

 

 

클래스의 내부와 외부를 구분해야하는 이유:

1. 프로그래머에게 구현의 자유를 제공하기 때문

2. 경계의 명확성이 객체의 자율성을 보장하기 때문

 

 

객체

1. 객체는 상태(state)와 행동(behavior)을 함께 가지는 복학적인 존재이다.

2. 객체는 스스로 판단하고 행동하는 자율적인 존재이다.

 

 

캡슐화: 데이터와 기능을 객체 내부로 함께 묶는 것

 

 

객체 내부에 대한 접근을 통제하는 이유: 객체를 자율적인 존재로 만들기 위함.

 

 

퍼블릭 인터페이스(public interface): 외부에서 접근 가능한 부분

구현(implementation): 외부에서 접근 불가능하고 오직 내부에서만 접근 가능한 부분

→인터페이스와 구현의 분리 원칙(separation of interface and implementation)이 나중에 나옴

 

일반적으로 객체의 상태는 숨기고 행동만 외부에 공개해야 한다.

 

설계가 필요한 이유: 변경을 관리하기 위해서

 

객체의 변경을 관리할 수 있는 기법 중에서 가장 대표적인 것: 접근 제어

 

하나의 인스턴스 변수만 포함하더라도 개념을 명시적으로 표현하는 것은 전체적인

설계의 명확성과 유연성을 높이는 철걸음이다.

 

 

객체지향 프로그래밍을 작성할 때는

1. 협력의 관점에서 어떤 객체가 필요한지를 결정

2. 객체들의 공통 상태와 행위를 구현하기 위해 클래스를 작성

 

 

협력이란

객체는 다른 객체의 인터페이스에 공개된 행동을 수행하도록 요청(request)할 수 있다.

요청을 받은 객체는 자율적인 방법에 따라 요청을 처리한 후 응답(response)한다.

 

메시지와 메서드의 구분: 다형성(polymorphism) 개념 출발

Screening이 Movie에게 calculateMovieFee ‘메시지를 전송한다’

Screening은 Movie안에 calculateMovieFee 메서드가 존재하고 있는지 알지 못한다. 단지 Movie가 calculateMovieFee 메시지에 응답할 수 있다고 믿고 메시지를 전송할 뿐이다.

 

결국 메시지를 처리하는 방법을 결정하는 것은 Movie 스스로의 문제이다.

이것이 객체가 메시지를 처리하는 방법을 자율적으로 결정할 수 있다고 말한 이유

 

코드의 의존성과 실행 시점의 의존성이 다르면 다를수록 코드를 이해하기 어려워진다.

 

코드의 의존성과 실행 시점의 의존성이 다르면 다를수록 코드는 더 유연해지고 확장 가능해진다.

 

의존성과 양면성은 설계가 트레이드오프의 산물이다.

 

설계가 유연해질수록 코드를 이해하고 디버깅하기는 점점 어려워진다.

 

반면 유연성을 억제하면 코드를 이해하고 디버깅하기는 쉬워지지만 재사용성과 확장 가능성은 낮아진다.

 

객체지향 설계자로 성장하기 위해서는 항상 유연성과 가독성 사이에서 고민해야한다.

 

 

차이에 의한 프로그래밍(programming by difference)

: 부모 클래스와 다른 부분만을 추가해서 새로운 클래스를 쉽고 빠르게 만드는 방법

 

유연성이 필요한 곳에 추상화를 사용하라.

 

 

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

2강 강의

 

Data Type: 해당 메모리 주소로부터 얼마큼의 길이를 갖는지

🔥type(형)이 아니면 책임을 질 수 없고 역할을 수행할 수 없다.

 

 

주소: 메모리 주소의 별명

  • Types
    • Role: 형을 통해 역할을 묘사함
    • Responsibility: 형을 통해 로직을 표현함
    • Message: 형을 통해 메시지를 공유함
    • Protocol: 객체 간 계약을 형을 통해 공유함

static, enum, class

 

 

  • supported types
    • static: 단 한개의 인스턴스가 존재(동시성 문제를 해결해야 함)
    • enum: 제한된 수의 인스턴스가 존재(제너릭에 사용불가 없음)
    • class: 무제한의 인스턴스가 존재

 

 

유틸리티는 클래스의 인스턴스로 있으면 안됨.

 

 

Condition(어떤 상태)

1. 조건 분기는 결코 제거할 수 없다.

2. 조건 분기에 대한 전략은 두 가지 뿐이다

  • 내부에서 응집성 있게 모아두는 방식
    • 장점: 모든 경우의 수를 한 곳에서 파악할 수 있다.
    • 단점: 분기가 늘어날 때마다 코드가 변경된다.
  • 외부에 분기를 위임하고 경우의 수만큼 처리기를 만드는 방식
    • 장점: 분기가 늘어날 때 마다 처리기만 추가하면 된다.
    • 단점: 모든 경우의 수를 파악할 수 없다.

Cohesion

void processCondition(String condition) {
    if(condition.equals("a")){
        a();
    }
    else if(condition.equals("b")){
        b();
    }
    else if(condition.equals("c")){
        c();
    }
    else if(condition.equals("d")){
        d();
    }
    else if(condition.equals("e")){
        e();
    }
}

if의 코드가 라이브러리 → 클라이언트 코드로 변환

 

 

 

Injection

void main() {
    
    String v = "c";
    Runnable run = null;
    
    if(v.equals("a")){
        
        run = new Runnable() {
            public void run(){
                System.out.println("a");
        }};
    }
    else if(v.equals("b")) {
        run = new Runnable() {
            public void run(){
                System.out.println("b");
        }};
    }
    else if(v.equals("c")) {
        run = new Runnable() {
            public void run(){
                System.out.println("c");
        }};
    }
    
    processCondition(run);
}

void processCondition(Runnable condition) {
    condition.run();
}

클라이언트 코드만 건드려도 변화가 가능하다.

격리 가능.

 

 

value = responsibility

시스템의 존재 가치는 사용자에게 제공되는 기능

사용자가 사용할 기능 = 시스템의 책임

시스템 차원의 책임을 더 작은 책임으로 분할

해당 책임을 추상화하여 역할을 정의함

역할에 따라 협력이 정의됨

 

 

Theater with Reservation

Theater theater = new Theater(Money.of(100.0));

Movie movie = new Movie<AmountDiscount>(
	"spideman",
	Duration.foMinutes(120L),
	Money.of(5000.0),
	new SequenceAmountDiscount(Money.of(1000.0), 1)
	);

theater.addMovie(movie);

for(int day = 7; day < 32; day++){
	for(int hour = 10, seq = 1; hour < 24; hour += 3, seq++){
			thater.addScreening(
			movie,
			new Screening(
			seq,
			LocalDateTime.of(2019, 7, day, hour, 00, 00),
			100
			)
		);
	}
}
TicketOffice ticketOffice = new TicketOffice(Money.of(0.0));
		theater.contractTicketOffice(ticketOffice, 10.0);
		TicketSeller seller = new TicketSeller();
		seller.setTicketOffice(ticketOffice);
		
		for(Screening screening:theater.getScreening(movie)){
			customer.reverse(seller, theater, movie, screening, 2);
			boolean isOk = theater.enter(customer, 2);
			System.out.println(isOk)
			break;
		}

값이 들어가지 않도록 코드를 짠다. 객체가 들어가야한다.

 

 

  • 좋은 함수
    • 가장 좋은 함수: 인자가 없는 함수
    • 그 다음 좋은 함수: 인자가 1개 있는 함수가

 

 

어떻게 인자가 1개인 함수를 만들까

🔅모든 인자를 추상화해서 객체화 시킨다.

→ 객체지향에서의 메세지

 

 

  • 좋은 인터페이스
    • 가장 좋은 인터페이스: 메서드가 없는 인터페이스
    • 그 다음 좋은 인터페이스: 인자가 1개 있는 인터페이스

 

  • 인터페이스 2개를 implement할 경우

도메인을 기준으로 추상화를 바꾼다.

 

 

List<>는 값을 쓸때 사용한다. 사실 List<>는 쓸 필요가 없다.

Set<>를 쓰는 것은 자연스러운 것이다.

 

Set<T> discountConditions = new HashSet<>();

 

 

 

Value Object

값 객체: 박쥐 같은 녀석

  1. 불변
  2. 값이 변할 때 마다 새 객체를 리턴함

'오브젝트' 카테고리의 다른 글

3. 역할, 책임, 협력 - 3  (0) 2022.10.14
3. 역할, 책임, 협력 - 2  (0) 2022.10.14
3. 역할, 책임, 협력 - 1  (0) 2022.10.14
1. 객체, 설계  (0) 2022.10.14
오브젝트  (0) 2022.10.14

댓글


반응형
반응형