ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [자바] 객체지향적인 설계에 대한 정리
    자바 2022. 10. 27. 22:41
    728x90
    Object 책을 읽고 나름대로의 정리입니다!

    패러다임 - 하나의 시대의 사회 전체가 공유하는 이론이나 방법, 문제의식 등의 체계

    그렇다면, 프로그래밍 패러다임은 무엇일까?

    프로그래밍 패러다임은 특정 시대의 어느 성숙한 개발자 공동체에 의해 수용된 프로그래밍 방법과 문제, 해결 방법,  프로그래밍 스타일을 의미한다

     

    프로그래밍 패러다임은 무엇을 위한 것 일까?

    동일한 규칙과 방법을 공유하여, 불필요한 의견 충돌을 방지하기 위함이다.

     

     

    자바는 이러한 프로그래밍 패러다임중 "객체지향 패러다임 "을 기반으로 하는 언어이다.


    객체지향 설계

    객체지향 설계는 올바른 객체에게 올바른 책임을 할당하면서 낮은 결합도와 높은 응집도를 가진 구조를 창조하는 활동이다.

    결국, 객체지향 설계의 핵심은 책임이며, 초점을 객체에 맞춰야 한다. ( RDD 지향, DDD 지양 )

     

    휼륭한 객체지향 코드 -> 객체를 지향 -> 객체가 수행하는 책임( 메시지 기반 )에 초점

     

    RDD에 초점을 맞춘 설계는 인터페이스에 초점을 맞추게 되면서 안정적인것에 비해서, DDD에 초점을 맞추면 내부 구현에 초점이 맞춰지기에 불안정하다.

     

    RDD -> 책임은 무엇일까?

    DDD -> 객체 내부에 저장할 데이터는 무엇일까?

     

    DDD의 설계방식을 초점을 해서 대비 효과를 누려보겠다

    일단 DDD는 객체가 포함해야하는 데이터에 집중한다. ( 이 객체가 포함해야할 데이터는 무엇인가? )

    < 이런 질문의 반복에 휩쓸리지 말자 >

    (RDD -> 위의 질문 대신에, 이 객체가 데이터에 대해 수행해야 하는 오퍼레이션은 무엇인가? 생각하기 )

     

    내부 데이터를 반환하는 접근자, 데이터를 변경하는 수정자를 추가해보면,

    // 데이터를 반환하는 접근자
    public Money getFee() {
        this.fee = fee;
    }
    
    // 데이터를 수정하는 수정자
    public void setFee(Money fee) {
        this.fee = fee;
    }

    이렇게 해서 내부 데이터를 캡슐화를 할 수 있다고 생각할 수 있다.

    하지만, 이는 객체 내부의 상태에 대한 어떤 정보도 캡슐화 하지 못한다 -> Money 타입의 fee라는 이름의 인스턴스 변수가 존재함을 인터페이스에 노골적으로 드러낸다.

     

    또한 이렇게 많은 접근자와 수정자를 갖는 경우를 추측에 의한 전략 라고 한다.

    이런 전략은 객체가 사용될 협력을 고려하지 않고 객체가 다양한 상황에서 사용될 것 이라는 막연한 추측 기반 설계이다.

    이러한 설계는 결과적으로 내부 구현을 노출시켜 캡슐화의 원칙을 위반한다.

     

    DDD는 객체 내부의 구현이 인터페이스에 드러남을 알 수 있는데, 이는 클라이언트가 구현에 강하게 결합됨을 의미하고, 이는 하나의 제어 객체가 다수의 데이터 객체에 강하게 결합된다는 것을 의미한다.

     

    DDD는 SOLID의 S원칙을 위반함을 높은 응집도를 통해 알 수 있다 ( S = 하나의 클래스는 단 한가지의 변경 이유만 가져야 한다 )

     

    DDD는 너무 이른시기에 데이터에 관해 결정하도록 강요하고, 협력이라는 문맥을 고려하지 않고 객체를 고립시킨 채 오퍼레이션을 결정하기에 변경에 취약할 수 밖에 없다. 따라서 데이터가 아닌 책임에 집중한 설계를 하자 ( 협력을 고려하자 )

     

     

    객체지향 코드에서 책임 즉 메시지에 대한 얘기를 해보겠다

    의아할 수 있지만, 객체지향에서 중요한 점은 클래스가 아니라 객체들이 주고받는 메시지다.

     

    객체가 수신하는 메시지들이 객체의 퍼블릭 인터페이스를 구성한다.

     

    그러한 인터페이스의 품질을 높이는 방법을 보겠다

     

    1. 디미터 법칙 : 객체의 내부 구조에 강하게 결합하지 않도록 협력 경로를 제한하는 것이다.

    간단하게 말하면, 오직 하나의 도트 ( . ) 만을 사용하는 것이다.

     

    클래스 내부의 메서드가 밑의 조건을 만족하는 인스턴스에만 메시지 전송하도록 프로그래밍하면 된다

    - this 객체, 속성, 속성인 컬렉션의 요소

    - 메서드의 매개변수, 메서드 내에서 생성된 지역 객체

     

    위반하면 기차 충돌같은 코드를 볼 수 있다.

    메시지 전송자가 수신자의 내부 구조에 대해 물어보고 반환받은 요소에 대해 연쇄적으로 메시지를 전송하는 것이다.

     

    ** 주의 

    스트림을 사용하는 경우는 기차 충돌 처럼 보일 수 있지만, 그렇지 않다

    하나의 도트만을 사용하는 것이 강제적이지만은 않다는 것이다.

    만약 기차충돌 같이 보인다면 스스로에게 객체의 내부 구조를 노출하고 있는지 물어보자 

     

    결국 디미터 법칙은 객체가 자기 자신을 책임지는 자율적인 존재가 되야함을 강조하는데, 이는 객체의 내부 구조를 묻는 메세지가 아니라 수신자에게 무언가를 시키는 메시지가 더 좋은 메세지라고 말한다.

     

    또한 이 원칙을 위반하면 인터페이스-구현의 분리 원칙을 위반한다.

     

    2. 메서드의 이름은 'what'을 드러내도록 지어야 한다.

    how는 내부 구현을 드러내고, what은 협력 안에서 수행해야 하는 책임을 그러낸다

     

     


    소프트웨어 모듈의 세가지 기능 **

    여기서 말하는 모듈이란 크기에 상관 없이 클래스나 패키지, 라이브러리와 같이 프로그램을 구성하는 임의의 요소이다.

     

    1. 모든 모듈이 잘 작동하는가?
    2. 변경에 용이한가?
    3. 이해하기 쉬운가?

     

    1. 모든 모듈이 잘 작동하는가?

    클라이언트 입장에서 필요한 기능을 오류 없이 수행하는 것을 의미한다

     

    2. 변경에 용이한가?

    변경은 '의존성'을 생각을 해야 한다.

     

    지금 어떤 객체가 변경된다면, 그 객체의 변경으로 인해 다른 객체들 또한 변경되어야 한다는 것이다. ( 하나의 코드 변경이 다른 코드의 변경을 불러 일으킨다는 점이다. )

     

    그렇다면, 객체 하나의 변경이 다른 객체의 변경까지 불러들인다면 이는 우리에게 좋은 것 일까?

    전혀 그렇지 않다. 코드가 복잡해지고 규모가 커질수록 유지보수 등에서 정말 어렵고 많은 버그들을 해결하는데 있어서 고생할 것 이다.

    예를 들면, 내가 지금 중고 거래에 대한 플랫폼을 만들었다고 생각해보자,,

    배포하기 전날에 클라이언트가 내게와서 유저관련 기능을 추가해달라고 한다고 생각해보자
    내가 만들어 놓은 코드는 몬스터 매소드, 기차 충돌, SOLID 원칙도 벗어나 있다면 내가 다음날에 배포를 할 수 있을?

    작성자가 생각하기에는 위약금을 물어주는 방법밖에는 떠오르지 않는다.

    이렇기에 의존성에 관련하여 변경에 대처하는 설계는 정말 중요하다라고 강조하고 싶다.

     

    그렇다면 의존성을 없도록 해야하는 가?

    그런건 아니다. 객체는 독립적인것이 아니다.

     

    프로그래밍을 책임에 초점을 맞춰 설계를 하다보면 객체들 간의 협력이 없을 수 없다. 

    맞다. 객체는 독립적인 것이 아닌 공동체의 일부이다.

     

    따라서 의존성이 아예 없는것이 아니라 최소한의 의존성만을 남겨둬야 하는 것이다.

     

    설계는 왜 필요할까?

    우리가 만드는 것들은 누군가가 언제나 변경을 원할 수 있다. 가령 위에서 든 예시 처럼.

    그렇다면 좋은 설계란 변경을 수용할 수 있는 설계이다. 

     

    변경을 수용할 수 있는 설계는 항상 변하는 요구사항에 적응할 수 있고, 다양한 버그로 부터 자유로워 질 수 있기 때문이다.

    또한 변경 가능하다는 말은 이해하기 쉬운 코드라고도 생각할 수 있겠다.

    어느 부분의 코드를 수정해야 하는데 이해할 수 없다면? 코드 수정을 해야겠다는 생각이 선뜻 들지 않을 것이기 때문이다.

     

    결론적으로 설계는 객체 사이의 의존성을 적절하기 관리하기 위해 존재한다.

     

    3. 이해하기 쉬운가?

    이해하기 쉬운 코드란 무엇인가?

     

    해답은 코드에서 실행하려는 동작이 우리의 예상에서 크게 벗어나지 않는 코드다.

    예시를 들어보면,

    여러분이 놀이공원에 갔는데 안내원이 여러분의 지갑을 뒤져서 돈을 가져가서 티켓을 산다면? 
    현실세계에서의 우리의 모습에서는 상상을 할 수가 없겠죠? 이런 관점에서 우리의 예상에서 벗어나고 있다고 말할 수 있습니다.

    현실에서는 우리가 자율적으로 행동을 하여서 놀이공원 티켓을 사죠?
    이런점이 우리의 예상에서 벗어나지 않았다고 할 수 있습니다.

     

    또한, 코드에 대한 이해를 위해 여러 가지 세부적인 내용들을 한꺼번에 기억해야한다는 점입니다.

    우리의 뇌는 장기와 단기기억으로 나누어서 기억을 하는데, 단편적으로 작동을 하는 단기기억에는 한계가 있습니다.

    따라서 단기 기억의 한계를 뛰어 넘는 양이 있다면 인지 과부화를 통해 코드에 대한 이해력이 떨어질 것 입니다.

     

    사실 생각을 해보면, 우리가 만드는 객체들은 가방, 티켓판매소, 극장, 연필, 책 들을 자율적인 존재로 만들어 줘야하는데, 현실 세계에서는 이들은 수동적인 존재이다. 하지만 우리는 코드에 대한 이해를 돕기위해 이런 객체들을 의인화를 통해서 능동적이고 자율적인 객체로 만들어야 한다

     

     

    해결 방법 ( 설계시에 고려할 점 )

    변경과 코드에 대한 이해는 어느 정도 연관되어 있다고 생각할 수 있다.

    결국 둘의 문제를 같이 해결하는 방법은 캡슐화를 통한 객체의 자율성을 높이는 것이다.

     

    캡슐화를 통한다면 객채의 자율성을 높여 변경하기 쉽도록 만들 수 있다. ( 의존성 -> 캡슐화 -> 결합도 낮게, 응집도 높게 )

    결국 객체 내부의 상태를 캡슐화하고 객체 간의 오직 메세지를 통해서만 상호작용 하도록 만드는 것이다.

     

    방법 1. 인터페이스 - 구현 분리

    객체를 인터페이스 부분, 구현 부분으로 나눈다
    인터페이스 부분 : 외부로 공개, 결합도를 낮추고 변경하기 쉽게 만든다
    객체 부분 : 내부에 인스턴스를 두어서 외부에 공개하지 않는다.

    이를 통해 객체 사이의 결합도를 낮추고 변경하기 쉬운 코드를 만든다.

    * 내부로 숨기고 외부에 공개를 하는 방법 중 한개로는 접근제어자를 사용하는 것이다.

     

    설계가 필요한 이유는 변경을 관리하기 위함이다.

    객체 사이의 의존성을 적절히 관리함으로써 변경에 대한 파급효과를 제어할 수 있다.

     

    방법 2. 접근 제어

    변경될 가능성이 있는 세부적인 구현 내용을 private영역 안에 감춤으로써 변경으로 인한 혼란 최소화

     

     

    방법 3. 명령 - 쿼리 분리

    명령 - 쿼리 분리 원칙은 퍼블릭 인터페이스에 오퍼레이션을 정의할 때 참고할 수 있는 지침을 제공한다.
    명령 - 쿼리 인터페이스 : 이런 스타일의 인터페이스를 사용함으로써 객체의 캡슐화와 다양한 문맥에서의 재사용 보장

    명령 = 프로시저 : 부수효과 있지만, 값을 반환 못함

    쿼리 = 함수 : 부수효과 없지만, 값을 반환 함

     

    동시에 부수효과도 있으면서 값을 반환하면 안된다.


    용어 정리

    객체 - 상태와 행동을 함께 캡슐화하는 실행 단위

     

    캡슐화 - 개념적이나 물리적으로 객체 내부의 세부적인 사항을 감추는 것. ( 내부 구현은 숨기고 인터페이스는 외부로 공개 )

    < 간단한 설명 >

    이렇게 함으로써, 요구사항이 변경이 될때, 변경에 의한 영향 (파급효과)를 통제할 수 있다.

     

    응집도 - 모듈에 포함된 내부 요소들이 연관된 정도
    결합도 - 다른 모듈에 대해 얼마나 많은 지식을 가지고 있는 정도

     

    < 간단한 설명 >

    변경의 관점에서 두가지 개념을 살펴 보면,

    응집도의 경우는 변경 수용을 할 때 하나의 모듈 전체가 변경을 한다면 응집도가 높다고 볼 수 있다.

    결합도의 경우는 한 모듈이 변경하기 위해서 다른 모듈의 변경이 적을수록 결합도가 낮다고 볼 수 있다.

     

    결론적으로, 하나의 변경이 있을때 그 변경에 대한 모듈이 한번에 전체가 변하며 다른 모듈들은 변화가 적으면 응집도가 높고 결합도가 낮아서 캡슐화가 잘되어 있다고 보며, 변경을 수용할 수 있는 좋은 설계라고 할 수 있다.

     

     

    프로세스 - 예시의 관점에서 보면 Theater라는 클래스의 enter()이란 메시지를 프로세스라고 한다
    데이터 - 예시의 관점에서 보면 Theater라는 클래스의 Audience,ticketSeller 를 데이터라 한다

     

    절차적 프로그래밍 - 프로세스와 데이터를 별도의 모듈에 위치시키는 방식
    객체지향 프로그래밍 - 프로세스와 데이터를 동일한 모듈에 위치시키는 방식

     

    < 간단한 설명 >

    절차적 프로그래밍은 알반적으로 우리의 직관에 위배가 된다. 

    아래 "이해가 쉬운가?" 부분에서 설명을 하지만, 직관적으로 우리가 이해할 수 있는 코드가 아니며, 그런 코드들은 원활한 의사소통을 막는다. 그로 인해 데이터의 변경으로 인한 영향을 지역적으로 고립시키기 어렵다.

     

    이에 대해 해결하기 위해서는 자신의 데이터는 스스로 처리하도록 프로세스를 만드는 것인데, 이는 객체지향 프로그래밍이다.

     

    또한, 절차적 프로그래밍은 책임이 중앙집중되어 있지만, 객체지향은 탈중앙화가 되어 있어서 객체가 더 자유로울 수 있다.

    객체지향의 탈중앙화를 "책임의 이동" (각 객체는 자신을 스스로 책임진다) 이라고도 부른다

     

     

    트레이드 오프 - 어떤 문제를 해결하는데 2가지 방법이 이상이 있을 때, 한 방법은 속도 측면에서 유리하고 성능 측면에서는 불리하지만, 다른 방법은 속도 측면에서는 불리하고 성능 측면에서 유리할때 한가지 방법을 고를 때 사용한다
      첫번째 방법 두번째 방법
    속도 유리 ( good ) 불리 ( bad )
    성능 불리 ( bad ) 유리 ( good )

    훌륭한 설계는 적절한 트레이드 오프의 결과물이라는 사실을 잊지 말자

     

     

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

    < 간단한 설명 >

    클래스의 이름은 도메인의 개념과 적어도 유사하게 적어야 하며, 클래스의 구조도 도메인의 구조와 유사한 형태를 띠어야 한다.

     

    클래스 작성자 : 새로운 데이터 타입을 프로그램에 추가하고, 클라이언트 프로그래머에게 필요한 부분만 공개하고 나머지는 숨겨야 한다
    클라이언트 프로그래머 : 클래스 작성자가 추가한 데이터 타입을 사용하고, 클래스들을 엮어서 애플리케이션을 안정적으로 구축

     

    < 간단한 설명 >

    클래스의 경계를 구분짓는 부분에서 사용한다.

    클라이언트 프로그래머는 내부의 구현은 무시하고 인터페이스만 알아도 클래스를 사용할 수 있기에 알고 있어야 하는 양이 준다

    클래스 작성자는 인터페이스를 바꾸지 않는 한 외부에 미치는 영향을 걱정하지 않고도 내부 구현을 마음대로 변경할 수 있다.

    이를 구현 은닉이라고 한다

     

     

    다형성 - 동일한 메시지를 수신했을떄 객체의 타입에 따라 다르게 응답할 수 있는 능력. 따라서 다형적인 협력에 참여하는 객체들을 모두 같은 메시지를 이해할 수 있어야하고, 이는 인터페이스가 동일해야 한다는 의미다

    < 간단한 설명 >

    메세지와 메소드를 구분하는데 있어서 다형성이라는 단어를 사용할 수 있다.

    메세지와 메소드를 구분함으로써 객체지향 패러다임이 유연하고, 확장 가능하며, 재사용 가능한 설계를 낳는다는 명성을 얻었다

     

    다형성을 사용하면 코드의 의존성과 실행 시점의 의존성이 다를 수 있다 ( 코드의 의존성 != 실행 시점의 의존성 )

    이렇게 의존성의 시점이 달라지면서 코드의 이해는 좀 떨어지고 디버깅이 좀 어렵지만, 코드가 더 유연하고 확장이 가능하다.

    ( 이 부분도 트레이드 오프의 산물이라고 볼 수 있다. )

     

    예외 케이스를 최소화 할수 있기에 조건문보다는 다형성을 이용한 오버라이딩이 좋다.  

     

     

    지연 바인딩, 동적 바인딩 - 메시지와 메소드를 실행 시점에 바인딩
    초기 바인딩, 정적 바인딩 - 컴파일 시점에 실행될 함수나 프로시저를 결정하는 바인딩

     

    템플릿 메소드 패턴 

    < 간단한 설명 >

    부모클래스에서 알고리즘 구현 -> 자식 클래스에서 필요한 처리를 위임

    자식 클래스에서 부모클래스의 메소드를 오버라이딩하여서 실제 처리를 한다. ( 추상 메소드가 실질적 처리를 함 )

     

     

    오버로딩 - 시그니처가 다름, 다른 메소드인것
    오버라이딩 - 시그니처가 같음, 같은 메소드 + @ 인것

     

     

    상속 - 차이에 의한 프로그래밍 

    < 간단한 설명 >

    상속은 코드의 재사용을 위한다고 보편적으로 생각하지만 아니다.

    또한, 상속이 마냥 좋지는 않다. 상속을 사용한다면 내부 구현에 대한 결합도가 높아지고, 캡슐화를 위반하며, 부모-자식 관계를 컴파일 시점에 결정하기에 설계를 유연하게 만들지 않는다.

     

    따라서 상속은 코드의 재사용을 위해서 사용하면 안되고, 인터페이스의 재사용을 위해야 하며, 타입계층을 맞추기 위해서 사용해야 한다 ( 다시 한번 말하지만, 코드의 재사용을 위해 상속을 사용하면 안된다 )

     

    인터페이스는 객체가 이해할 수 있는 메시지의 목록을 정의하는데, 결과적으로 외부 객체는 자식 클래스를 부모 클래스와 동일한 타입으로 간주할 수 있게 된다 ( 업 캐스팅 !! )

     

    결론적으로, 구현상속이 아닌 인터페이스를 공유할 수 있도록 상속하는 인터페이스 상속을 해야한다

    그리고 할 수 있다면, 상속이 아닌 합성을 하는 것을 추천한다.

     

     

    합성 - 다른 객체의 인스턴스를 자신의 인스턴스 변수로 포함해 재사용하는 방법을 말한다

    < 간단한 설명 >

    인터페이스에 정의된 메시지를 통해서만 코드를 재사용하기에 내부 구현을 잘 몰라서 캡슐화가 위반되지 않는다

     

     

    협력 - 객체들이 애플리케이션의 기능을 구현하기 위해 수행하는 상호작용 ( by 메시지 전송 )
    책임 - 객체가 협력에 참여하기 위해 수행하는 행동 ( 객체의 행동 )
    역할 - 객체들이 협력 안에서 수행하는 책임들이 모여 객체가 수행하는 것 ( 책임의 집합 )

    < 간단한 설명 >

    위 에서 언급했던 것처럼 객체는 공동체의 일부이다.

    그렇다면 그들을 기능을 수행하는 위해서 협력을 해야하는데, 유일한 방법인 메시지 전송을 통해 한다.

    메시지를 수신한 객체는 메서드를 실행해 요청에 응답한다.

     

    협력 -> 협력에 의해 객체가 존재하고 객체의 행동을 결정 지음 -> 행동에 의해 객체 상태(데이터 == 정보) 결정

    결론적으로 협력이 객체를 구성하는 행동(책임)과 상태를 모두 결정

     

    역할은 객체를 포괄하는 추상화라고 생각하면 되는데, 보통 추상 클래스와 인터페이스를 사용한다.

    협력의 관점에서 이들은 구체 클래스들이 따라야 하는 책임의 집합을 서술한다.

     

    역할을 통해 협력의 개수를 줄일 수 있으며 ( amountPolicy + percentPolicy -> DiscountPolicy 로 줄일 수 있다. )

    이를 통해서 중복 코드를 제거하고 협력이 더 유연해진다는 점이다. ( 기능을 추가할때 동일한 협력안에서 implements 하면되기 때문 )

     

    결국 추상화 계층을 사용해 상위 수준의 정책을 쉽고 간단하게 표현할 수 있으며, 설계가 좀 더 유연해진다

     

    정보 전문가 패턴 - 객체 협력에 필요한 지식, 방법을 가장 잘 아는 개체에게 책임을 할당하는 패턴

    < 간단한 설명 >

    객체가 책임을 수행하게 하는 유일한 방법은 메시지 전송이여서 책임을 할당한다는 것은 메시지의 이름을 결정하는 것이다.

    따라서 메시지의 이름을 정하고 메시지로 협력을 시작해서 책임을 할당받을 객체 선택하는 것이다.

     

     

    RDD 책임 주도 설계 - 책임을 찾고 책임을 수행할 적절한 객체를 찾아 책임을 할당하는 방식으로 협력을 설계하는 방법

    < 간단한 설명 >

    객체지향 설계는 협력에 필요한 메시지를 찾고 메시지에 적절한 객체를 선택하는 반복적인 과정을 통해 이뤄지고, 이런 메시지(퍼블릭 인터페이스)가 메시지를 수신할 객체의 책임을 결정한다

     

    반복적인 과정이라는 것은 하나의 큰 책임을 작은 책임들로 분할하고 그 책임들에 맞는 객체를 지정하고 그들 간에 협력하는 과정을 말함.

     

    책임을 할당할때는 2가지를 고려하기

    1. 메시지가 객체를 결정한다. 따라서 최소한의 인터페이스, 추상적인 인터페이스를 갖도록 해야한다.

    인터페이스는 결국 how가 아닌 what을 표현해야한다 ( how를 하면 내부 구현의 캡슐화가 무너지기 때문이다. )

     

     

    2. 행동이 상태를 결정한다

    반대가 된다면 RDD가 아닌 DDD( 데이터 주도 설계 )가 된다.

    이는 하향식 접근 방법을 이끌게 되고, 하향식 접근 방법은 캡슐화를 깨서 험난한 유지보수의 세계에 입성시켜준다.

    하향식 접근 방법은 이미 정리되어 더이상 변경 되지 않을 것들을 문서화할때만 쓰는 것이다.

     

     

    메시지 - 객체들이 협력하기 위해 사용할 수 있는 유일한 의사소통 ( 오퍼레이션 + 인자 )
    메시지 전송 : 오퍼레이션 + 인자 +메시지 수신자
    메서드 - 메시지를 수신했을 때 실제로 실행되는 함수 또는 프로시저 ( 오퍼레이션을 구현 )
    퍼블릭 인터페이스 - 협력을 위해 외부로 공개하는 메시지의 집합
    오퍼레이션 - 퍼블릭 인터페이스에 포함된 메시지 ( 구현이 아닌, 수행 가능한 어떤 행동에 대한 추상화 )

    < 간단한 설명 >

    클라이언트 - 서버에서는 클라이언트를 메시지 전송자, 서버를 메시지 수신자라고 한다

    ex) 수신자.오퍼레이션명(인자); 이렇게 사용한다

     

    메서드를 보면, 코드 상에서 동일한 이름의 변수에게 동일한 메시지를 전송했다 하더라도 객체의 타입에 따라 수행되는 메서드가 달라질 수 있다. ( 컴파일 시점 != 실행 시점 )

     

    메시지 전송자는 자신이 어떤 메시지를 전송해야 하는지민 알면 되고, 수신자는 메시지가 도착했다는 사실만 알면 된다.

     

    좋은 인터페이스는 최소한의 추상적인 인터페이스이다. ( 꼭 필요하고 what을 나타내는 인터페이스 )

     

     

    루틴 - 절차를 묶어 호출 가능하도록 이름을 부여한 기능
    프로시저 ( 명령 ) - 정해진 절차에 따라 내부의 상태를 변경하는 루틴의 한 종류 ( 부수효과 0, 값 반환 X )
    함수 ( 쿼리 ) - 정해진 절차에 따라 필요한 값을 계산해서 반환하는 루틴의 한 종류 ( 부수효과 X, 값 반환 0 )

     

     

    객체지향 - 프로시저를 추상화
    프로시저 추상화 - 시스템을 프로시저 단위로 분해
    데이터 추상화 - 객체를 이용해 시스템을 분해
    하향식 접근법 - 최상위 기능을 정의하고, 그 기능 보다 조금 더 작은 단계의 하위기능으로 분해해 나가는 방법

    < 간단한 설명 >

    단기 기억의 용량 초과를 인자 과부화라고 하는데, 이를 추상화를 통해 큰 문제를 해결 가능한 작은 문제로 나누는 분해로 해결할 수 있다.

     

    이런 분해는 패러다임로 이어지는데, 패러다임에는 여러 종류의 메커니즘이 있고 그 중에는 추상화 메커니즘이 있다.

    추상화 메커니즘은 또 프로시저 추상화와 데이터 추상화가 있다.

     

    프로시저 추상화는 기능 분해 라고 부르며, 하향식 접근법이 있다

    데이터 추상화에는 데이터 타입 추상화와 프로시저를 추상화한 객체지향 있다.

     

    하지만, 하향식 접근법은 실행 순서를 고정시키고, 메인 함수를 빈번히 수정해야 한다는 여러 가지의 단점이 있으므로 사용하지 않는다

     

    정보 은닉 - 시스템을 모듈 단위로 분해하기 위한 기본 원리로 시스템에서 자주 변경되는 부분을 상대적으로 덜 변경되는 안정적인 인터페이스 뒤로 감춰야 한다는 것이 핵심이다.

     

     


    나만을 위한 정리

    도메인 부터 시작하라

    효율적인 설계는 변경을 수용하는 설계다 ( 추상화, 캡슐화를 잘하라 )

    책임 주도 설계를 진행하라

    객체 지향의 주체는 클래스가 아닌 객체다

    설계는 책임(오퍼레이션)으로 부터 정해지고, 책임이 객체를 정한다

    인터페이스 - 구현의 원리를 잘 지키자

    명령 - 쿼리 분리 원칙을 지키자

    캡슐화와 응집도를 항상 고려하자

    상속은 같은 계층으로 묶을때만 ( 인터페이스 재사용만 ) 되도록 합성을 사용하자

    조건문 보다는 다형성을 사용하자

Designed by Tistory.