객체지향의 사실과 오해

박상훈·2022년 4월 14일
0
post-thumbnail

ch 1.협력하는 객체들의 공동체


사이드 프로젝트 사원증(출입증)에 통합 결제 시스템 ?
충전하고 사용하는 형태
회사에서 충전 해주는 금액이 커지길 바란다는 의미로 서비스 이름 정해보면 ?
익숙한 단어에 입에 감길만한 단어

커피를 주제로 바라보는 객체에 대한 진실
손님, 캐시어, 바리스타는 협력을 통해 요청, 응답한다
손님은 주문 캐시어는 결제, 커피 전달 바리스타는 커피 제조
객체지향의 중요한 개념 세가지 역할, 책임, 협력이 위 모든곳에 적용된다
손님은 캐시어나 바리스타가 다른사람으로 대체되어도 주문을 받고 커피를 제조해주면
상관없다 이와같이 대체 가능한 역할과 책임은 다형성과도 깊이 연관된다

어플리케이션이 가지는 다양하고 수 많은 기능들을 하나의 객체가 모두 담당할 수 없다
여러 객체들이 각 각의 기능들을 담당하고 협력하여 어플리케이션을 구성한다

외부의 요청이 무엇인지를 표현하는 메시지
(메시지)요청을 처리하기 위한 구체적인 방법 메서드
이 둘을 분리하는 것은 객체의 자율성을 높이는 핵심 메커니즘
이는 캡슐화 라는 개념과 깊이 연관됨

객체는 충분히 협력적이여야 한다

모든걸 혼자 처리할 수 있는 전지전능한 객체는 내부적인 복잡도에 의해 혼자 자멸한다

객체는 충분히 자율적이여야 한다

자기 스스로의 원칙에 따라 어떤 일을 하거나 자기 스스로를 통제하여 절제하는 것

객체지향의 본질

1.객체지향이란 시스템을 상호작용하는 자율적인 객체들의 공동체로 바라보고 객체를 이용해
시스템을 분할하는 방법
2.자율적인 객체란 상태와 행위를 함께 지니며 스스로 자기 자신을 책임지는 객체
3.시스템의 행위를 구현하기 위해 다른 객체와 협력, 각 객체는 협력 내에서 정해진 역할을
수행하며 역할은 관련된 책임의 집합
4.객체는 다른 객체와 협력하기 위해 메시지 전송, 메시지를 수신한 객체는 처리하기 적합한 메서드를 자율적으로 선택

ch 2.이상한 나라의 객체


객체와 객체 사이의 의미있는 연결을 링크라고 칭함

객체의 상태

상태는 특정 시점에 객체가 가지고 있는 정보의 집합으로 객체의 구조적 특징을 표현
객체의 상태는 객체에 존재하는 정적인 프로퍼티와 동적인 프로퍼티 값으로 구성
객체의 프로퍼티는 단순한 값과 다른 객체를 참조하는 링크로 구분

객체의 행동

행동이란 외부의 요청 또는 수신된 메시지에 응답하기 위해 동작하고 반응하는 활동
행동의 결과로 객체는 자신의 상태를 변경하거나 다른 객체에게 메시지 전달
객체는 행동을 통해 다른 객체와의 협력에 참여하므로 행동은 외부에 가시적

객체의 상태와 행동 사이의 관계

객체의 행동은 상태에 영향을 받음
객체의 행동은 상태를 변경

식별자

어떤 객체를 다른 객체와 구분하는데 사용하는 객체의 프로퍼티
값은 식별자를 가지지 않기 때문에 상태를 이용한 동등성 검사를 통해 두 인스턴스 비교
객체는 상태가 변경될 수 있기 때문에 식별자를 이용한 동일성 검사를 통해 두 인스턴스 비교

동등성 : 상태를 이용해 두 값이 같은지 확인하는 방법
동일성 : 식별자를 기반으로 객체가 같은지 판단할 수 있는 성질

객체의 특징

객체는 상태를 가지며 상태는 변경 가능
객체의 상태를 변경시키는 것은 객체의 행동
ㄴ 행동의 결과는 상태에 의존적이며 상태를 이용해 서술할 수 있음
ㄴ 행동의 순서가 실행 결과에 영향을 미친다
객체는 어떤 상태에 있더라도 유일하게 식별 가능

ch 3.타입과 추상화


공통점을 기반으로 객체를 묶기 위한 그릇을 개념(concept) 으로 칭함
객체에 어떤 개념을 적용하는것이 가능해서 개념 그룹의 일원이 될 때 객체를 그 개념의 인스턴스(instance) 라고 칭함

객체

특정한 개념을 적용할 수 있는 구체적인 사물을 의미
개념이 객체에 적용됐을 때 객체를 개념의 인스턴스 라고 한다

분류

객체에 특정한 개념을 적용하는 작업
객체에 특정한 개념을 적용하기로 결심했을 때 그 객체를 특정한 집합의 멤버로 분류

심볼(symbol) : 개념을 가리키는 간략한 이름이나 명칭
내연(intension) : 개념의 완전한 정의를 나타내며 내연의 의미를 이용해
객체가 개념에 속하는지 여부를 확인할 수 있다
외연(extension) : 개념에 속하는 모든 객체의 집합(set)

추상화

구체적인 사물 간의 공통점은 취하고 차이점은 버리는 일반화를 통한 단순화
중요한 부분을 강조하기 위해 불필요한 세부사항을 제거해 단순화

타입

우리가 인식하고 있는 다양한 사물이나 객체에 적용할 수 있는 아이디어나 관념을 의미
어떤 객체에 타입을 적용할 수 있을 때 그 객체를 타입의 인스턴스라고 하며
타입의 인스턴스는 타입을 구성하는 외연인 객체 집합의 일원이 된다

데이터 타입

메모리 안에 저장된 데이터의 종류를 분류하는데 사용하는 메모리 집합에 관한 메타데이터
데이터 분류는 암시적으로 어떤 연산이 데이터에 대해 수행될 수 있는지 결정

스냅샷(snapshot) : 객체가 특정 시점에 구체적으로 어떤 상태를 가지는지
클래스 : 타입을 구현하기 위해 프로그래밍 언어에서 제공하는 메커니즘

ch 4.역할, 책임, 협력


협력

다수의 연쇄적인 요청과 응답의 흐름으로 구성

책임

객체의 책임은 하는 것, 아는 것 두 가지 범주로 자세히 분류

하는 것
객체를 생성하거나 계산을 하는 등의 스스로 하는 것
다른 객체의 행동을 시작시키는 것
다른 객체의 활ㅇ동을 제어하고 조절하는 것

아는 것
개인적인 정보에 관해 아는 것
관련된 객체에 관해 아는 것
자신이 유도하거나 계산할 수 있는 것에 관해 아는 것

역할

객체지향 설계의 단순성, 유연성, 재사용성을 뒷받침하는 핵심 개념
왕-하얀토끼-요리사, 여왕-하얀토끼-앨리스 -> 판사-하얀토끼-증인
위와 같이 역할을 이용하면 협력을 추상화함으로써 단순화할 수 있다
본질적으로 역할은 다른 객체에 의해 대체 가능함을 의미

객체 지향 설계 기법

1.책임-주도 설계(Reponsibility-Driven Design)
협력에 필요한 책임들을 식별하고 적합한 객체에게 책임을 할당하는 방식
2.디자인 패턴(Design Pattern)
전문가들이 반복적으로 사용하는 해결 방법을 정의해 놓은 설계 템플릿 모음
3.테스트-주도 계발(Test-Driven Development)
테스트를 먼저 작성하고 테스트를 통과하는 구체적인 코드를 추가하면서 어플리케이션을 완성

객체를 생성할 때 먼저 책임을 고민하고 그에 필요한 데이터를 정하자

ch 5.책임과 메시지


자율적인 책임의 특징은 어떻게(how) 가 아닌 무엇을(what) 설명
메시지-전송 매커니즘은 객체가 다른 객체에 접근할 수 있는 유일한 방법
객체지향 패러다임이 강력한 이유 다형성을 이용해 협력을 유연하게 할 수 있기 때문

묻지말고 시켜라 스타일, 데메테르 법칙

메시지를 먼저 결정하고 객체가 메시지를 따르게 하는 설계 방식은 객체가 외부에
제공하는 인터페이스가 독특한 스타일을 따르게 하는데 이는
객체의 자율성, 캡슐화, 낮은 결합도를 보장한다

책임이 얼마나 자율적인지가 전체적인 협력의 설계 품질을 결정

1.협력을 단순하게 만든다
2.객체의 외부와 내부를 명확하게 분리한다
3.책임을 수행하는 내부를 변경하더라도 외부에 영향을 미치지 않는다
4.자율적인 책임은 협력의 대상을 다양하게 선택할 수 있는 유연성을 제공한다
5.객체가 수행하는 책임들이 자율적일수록 객체의 역할을 이해하기 쉬어진다

책임이 자율적일수록
추상화 되고 응집도가 높아지고, 결합도는 낮아지며, 캡슐화는 증진되고
인터페이스와 구현이 명확히 분리되며 설계의 유연성 과 재사용성이 향상된다고 한다
책임 중요성을 기억하자...

ch 6.객체 지도


기능 : 사용자의 목표를 만족시키기 위해 책임을 수행하는 시스템의 행위
구조 : 사용자나 이해관계자들이 도메인에 관해 생각하는 개념과 개념들 간의 관계
도메인 : 사용자가 프로그램을 사용하는 대상 분야

유스케이스

사용자와 시스템 간의 상호작용을 보여주는 텍스트

책임 할당의 기본 원칙

책임을 수행하는데 필요한 정보를 가진 객체에게 그 책임을 할당하는 것 이며
이는 관련된 상태와 행동을 함께 캡슐화하는 자율적인 객체를 낳는다

도메인 모델의 목표

사람들이 동일한 용어와 동일한 개념을 이용해 의사소통하고 코드로부터 도메인
모델을 유추할 수 있게 하는 것

안정적인 도메인 모델을 기반으로 시스템 기능을 구현
도메인 모델과 코드를 밀접하게 연관시키기 위해 노력하면
유지보수하기 쉽고 유연한 객체지향 시스템을 만드는 거름이 된다

유스케이스, 도메인 모델, 책임-주도 설계의 중요성

유스케이스는 불안정한 기능을 서술, 도메인 모델은 안정적인 구조를 개념화
변경에 유연한 소프트웨어는 유스케이스에서 정리 된 시스템의 기능을
도메인 모델을 기반으로 한 객체들의 책임으로 분배한다
그리고 시스템의 기능을 역할과 책임을 수행하는 객체들의 협력 관계로
바라보게 함으로써 두 가지 기본 재료인 유스케이스와 도메인 모델을 통합한다

ch 7.함께 모으기


개념 관점(Conceptual Perspective)
도메인 안에 존재하는 개념과 개념들 사이의 관계
명세 관점(Specification Perspective)
소프트웨어의 객체들의 책임에 초점, 객체가 협력을 위해 '무엇' 을 할 수 있는가
구현 관점(Implementation Perspective)
객체들이 책임을 수행하는데 필요한 동작하는 코드를 작성

커피 전문점을 예로 바라보는 세가지 관점

손님, 바리스타, 메뉴판, 메뉴판 항목, 커피(제조)
메뉴판 과 메뉴판 항목은 포함 관계 또는 합성 관계이며
메뉴판 과 손님은 연관 관계이다
두 관계의 차이는 하나의 객체에 다른 객체가 포함되어지는지 아닌지로 구분 된다
손님 과 바리스타, 바리스타 와 커피 모두 포함되지 않기 때문에 연관 관계가 된다

도메인 모델이 완성되면 협력 설계를 진행하는데 이는 적절한 객체에게 적절한 책임을 할당한다
메시지가 객체를 정한다
도메인 모델에서 책임을 수행하기에 적절한 타입이 있는지 확인
커피를 주문하라 라는 메시지 담당은 도메인 모델에서 손님이라는 객체로 정할 수 있다
손님객체에서 직접 할 수 없는 일이 있으면 이는 외부에 전송되는 메시지를 정의한다
커피를 주문하라(메시지)-> 손님(객체) <-> 메뉴 항목을 찾아라(메시지) <-> 메뉴판(객체)
위와같이 바리스타와 커피까지 연결하게되면 객체들의 인터페이스가 완성된다
인터페이스를 클래스기반으로 식별하고 오퍼레이션을 수행하는 방법을 메서드로 구현하면
아래와 같은 코드를 작성할 수 있다

class Customer {
    public void order(String menuName, Menu menu, Barista barista) {
        MenuItem menuItem = menu.choose(menuName);
        Coffee coffee = barista.makeCoffee(menuItem);

    }
}

class MenuItem {
    private String name;
    private int price;

    public MenuItem(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public int cost() {
        return price;
    }

    public String getName() {
        return name;
    }
}

class Menu {
    private List<MenuItem> items;

    public Menu(List<MenuItem> items) {
        this.items = items;
    }

    public MenuItem choose(String name) {
        for (MenuItem each : items) {
            if (each.getName().equals(name)) {
                return each;
            }
        }
        return null;
    }
}

class Barista {
    public Coffee makeCoffee(MenuItem menuItem) {
        Coffee coffee = new Coffee(menuItem);
        return coffee;
    }
}

class Coffee {
    private String name;
    private int price;

    public Coffee(MenuItem menuItem) {
        this.name = menuItem.getName();
        this.price = menuItem.cost();
    }
}

이번 챕터에서 많은 강조를 한 내용이다 기억하자
인터페이스와 구현을 분리하고 내가 작성한 코드가 위와 같은 세가지 관점으로 분리되어
확인할 수 있어야 한다. 그래야만 훌륭한 설계라고 볼 수 있으며 관점이 나누어지지 않는다면
어딘가 문제점을 감추고 있는 설계라고 볼 수 있다

profile
엔지니어

0개의 댓글