좋은 코드란 무엇인가
'좋은 코드란?'
읽기 쉬운 코드(왜 읽기 쉬워야 하지?)
테스트가 용이한 코드(테스트가 용이하다는 것은 무엇을 의미하는 것일까?)
중복이 없는 코드?
더 읽어보기 >https://jbee.io/etc/what-is-good-code/
Object oriented Programming
객체 지향 프로그래밍, 저도 잘 모르고 너무 거대한 부분이라서 넣을지 말지 고민을 했습니다만, 면접에서 이 정도 이야기하면 되지 않을까? 하는 생각에 조심스레 적어봤습니다.
객체 지향 프로그래밍 이전의 프로그래밍 패러다임을 살펴보면, 중심이 컴퓨터에 있었다.
컴퓨터가 사고하는대로 프로그래밍을 하는 것이다.
하지만 객체지향 프로그래밍이란 인간 중심적 프로그래밍 패러다임이라고 할 수 있다.
즉 현실 세계를 프로그래밍으로 옮겨와 프로그래밍하는 것을 말한다.
현실 세계의 사물들을 객체라고 보고 그 객체로부터 개발하고자 하는 애플리케이션에 필요한 특징들을 뽑아와
프로그래밍 하는 것이다. 이것을 추상화라고 한다.
불필요한 특징들은 버리고 필요한 특징들을 뽑는다 - 추상화 (클래스 만들 때)
불필요한 공통점들은 버리고 필요한 공통점들을 뽑는다 - 추상화 (인터페이스나 수퍼클래스 만들 때)
[ 이하 객체지향 프로그래밍의 장점? ]
OOP로 코드를 작성하면 이미 작성한 코드에 대한 재사용성이 높다.
자주 사용되는 로직을 라이브러리로 만들어두면 계속 사용할 수 있으며 그 신뢰성을 확보할 수 있다.
또한 라이브러리를 각종 예외상황에 맞게 잘 만들어두면 개발자가 사소한 실수를 하더라도 그 에러를 컴파일 단계에서 잡아낼 수 있으므로 버그 발생이 줄어든다.
또한 내부적으로 어떻게 동작하는지 몰라도 개발자는 라이브러리가 제공하는 기능들을 사용할 수 있기 때문에 생산성이 높아지게 된다.
객체 단위로 코드가 나눠져 작성되기 때문에 디버깅이 쉽고 유지보수에 용이하다.
또한 데이터 모델링을 할 때 객체와 매핑하는 것이 수월하기 때문에 요구사항을 보다 명확하게 파악하여 프로그래밍 할 수 있다.
객체 간의 정보 교환이 모두 메시지 교환을 통해 일어나므로 실행 시스템에 많은 overhead가 발생하게 된다. 하지만 이것은 하드웨어의 발전으로 많은 부분 보완되었다. 객체지향 프로그래밍의 치명적인 단점은 함수형 프로그래밍 패러다임의 등장 배경을 통해 알 수 있다. 바로 객체가 상태를 갖는다는 것이다. 변수가 존재하고 이 변수를 통해 객체가 예측할 수 없는 상태를 갖게 되어 애플리케이션 내부에서 버그를 발생시킨다는 것이다. 이러한 이유로 함수형 프로그래밍 패러다임이 주목 받고 있다.
객체 지향적 설계 원칙
1. SRP (Single Responsibility Priciple) : 단일 책임 원칙
클래스는 단 하나의 책임을 가져야 하며, 클래스를 변경하는 이유는 단 하나의 이유이어야 한다.
ex) https://huisam.tistory.com/entry/SRP
(Student클래스와 Course클래스를 활용하여 예시를 잘 들어줌
Student클래스의 메소드가 Course클래스의 setter메소드를 활용하여 속성을 변화시킴
-> Student클래스가 Coures클래스에 대한 책임을 지는 상태임)
2. OCP (Open-Closed Principle) : 개방-폐쇄 원칙
확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다
3. LSP (Liskov Substitition Principle) : 리스코프 치환 원칙
상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다.
ex) LSP - rectangle / square 예시 https://wookgu.tistory.com/30
4. ISP (Interface Segration Principle) : 인터페이스 분리 원칙
인터페이스는 그 인터페이스를 사용하는 클라이언트를 기준으로 분리해야 한다. (인터페이스를 implements했을 때 불필요한 method를 정의해야 할 수 있음)
5. DIP (Dependency Inversion Principle) : 의존 역전 원칙
고수준 모듈은 저수준 모듈의 '구현'에 의존해서는 안된다.
(저수준 모듈의 구현이 아닌 저수준 모듈의 인터페이스에 의존하라는 뜻인듯..?)
ex) 자동차가 스노우타이어(겨울용) 클래스에 의존한다고 가정해보자. 겨울이 끝나고 타이어를 바꿀텐데 그렇게 되면 자동차 클래스 내부의 스노우타이어와 관련된 코드를 전부 수정해야한다. 자동차 클래스는 스노우타이어 구현체클래스가 아닌 타이어 인터페이스에 의존해야 한다.
추상화 캡슐화 은닉화
추상화(Abstraction)
일단 다루어야 할 여러 요구사항의 공통점을 찾고 문제 해결과 관계가 없는 공통점들을 제거한다.
이 과정을 객체지향 언어에서는 추상화(Abstraction)라고 한다.
불필요한 특징들은 버리고 필요한 특징들을 뽑는다 - 추상화 (클래스 만들 때)
불필요한 공통점들은 버리고 필요한 공통점들을 뽑는다 - 추상화 (인터페이스나 수퍼클래스 만들 때)
공통점을 모아서 다룰 수 있게 대상화하는 것
추상화라는 말은 구체적인 것을 제거한다는 말이다.
공통된 속성과 행위를 추출하는 과정
왜 추상화를 하는가??????
속성과 행위를 중복구현하지 않기 위해서?
왜???? 코드의 유연성을 위해서(중요함)
사실 실컷 공부하는 객체지향의 4대 특성(추상화 캡슐화 상속 다형성)이니 5대 설계 원칙(SOLID)이니
전부 객체지향프로그래밍이 탄생한 이유인 코드의 유연성 때문인것임
그리고 이 어지간한 것들이 객체의 정보은닉이 가장 중요한 목적임
사실상 객체들끼리 서로 아무런 정보를 모르고 프로그램을 짜는 것이 가장 이상적인데
(프로그램의 어떤 모듈에서 에러가 났을 경우 그 부분을 아무리 고쳐도
다른 모듈에 영향을 못끼치니까 이게 코드의 유연성)
그건 현실상 불가능하기 때문에 최대한 서로 은닉할 수 있게끔 하라고 저런 원칙들, 온갖 디자인패턴들 등등이 있는 것
ex) 오케스트라의 바이올리니스트, 피아니스트, 트럼페티스트 // 구체적이지 않고 추상적인 '연주자'로 추상화
-> 공통점을 추출한다( =차이점을 감춘다), 불필요한 공통점을 제거한다.
유연성을 확보하는 각 단계 중에서 공통점을 추출하고 차이점을 감추는 작업에 대해서 이야기 했다. 이 단계에서 했던 일을 요약하자면 다음과 같다. 우리는 여러 요구사항들로부터
1. 공통점을 추출하였고,
2. 불필요한 공통점을 제거하였다.
오케스트라의 비유에서 우리는 연주한다는 공통점을 추출해냈고, 같은 국적이라는 공통점을 추출해 냈지만 국적은 연주와 상관없는 정보이기 때문에 제거하였다. 객체지향 언어에서 추상화(Abstraction)라고 부르는 특성은 우리가 유연성을 확보하기 위해 했던 과정과 동일하다. 추상화라는 말은 구체적인 것을 제거한다는 말이다. 이것은 여러 요구사항에 대한 공통 요약본을 만드는 과정이다. 따라서 무턱대고 추상화를 할 수는 없다. 우선 제거되는 대상은 공통되지 않은 것이어야 한다. 그리고 우리가 만드는 것은 요약본이기 때문에 공통점들 중에서도 목적한 바와 맞지 않는 것들은 다시 제거해야 한다. 이러한 과정을 통해서 문제와 밀접한 관계가 있고, 모든 요구사항들이 공통적으로 가지고 있는 부분들이 추출된다.
캡슐화(Encapsulation)
캡슐화는 "캡슐로 만든다"라는 의미이다. 우리는 이미 "대상화"한다는 개념을 통해 이 특성을 익혔다. 캡슐화의 의미는 크게 n가지로 나눌 수 있다.
1. 공통점들의 묶음에 명칭을 부여한다. 이 "묶음"은 다룰 수 있는 "대상"이 된다.
2. 속성과 행위를 하나로 묶는다.
캡슐화를 통해 모듈 내의 속성과 행위의 응집도는 높이고, 외부 모듈과의 결합도를 줄일 수 있다
유연성과 객체지향 특성
자 이제 다시 한번 정리를 해보자. 우리는 현실세계에서 여러가지 방식으로 유연성을 확보하고 있다. 이미 익숙한 방식을 재활용하는 것은 훌륭한 전략이다. 따라서 그리고 객체지향 언어는 현실 세계에서의 유연성 전략을 그대로 따르고 있다.
현실 세계에서의 유연성은 4가지 단계로 나뉘어진다. 그리고 이 단계들은 객체지향의 4대 특성과 1:1로 매칭된다.
일단 다루어야 할 여러 요구사항의 공통점을 찾고 문제 해결과 관계가 없는 공통점들을 제거한다. 이 과정을 객체지향 언어에서는 추상화(Abstraction)라고 한다.
그런 다음에는 공통점을 묶어 대상화 한다. 객체지향에서는 이를 캡슐화(Encapsulation) 라고 한다.
이 다음에는 개별적인 차이점들을 구현해 주어야 한다. 이 때 이미 뽑아 놓은 공통점은 모든 차이점들에게도 포함을 시켜 줘야 하는데, 이 과정을 객체지향에서는 상속(Inheritance)이라고 한다.
이제 차이점 대신 공통점을 통해 대상을 다루어야 한다. 공통점을 통해 대상을 다루려면 각각의 요구사항들이 공통점을 통해 다뤄 질 수 있어야 하는데, 객체지향에서는 이를 다형성(Polymorphism)을 통해 지원한다.
(출처 : https://effectiveprogramming.tistory.com/m/entry)
객체지향의 4가지 특성인 추상화 캡슐화 상속 다형성은 프로그램의 유연성을 목적으로 존재하는 것이었다.....
객체지향 언어는 애초에 프로그래밍을 시작할 단계에서부터 유연성을 염두해 두고 시작해야 하는 언어다.
객체들이 확장될 여지는 없는지, 즉 유연성을 확보해야할 필요가 있는지 알아야한다.
은닉화(Information hiding)
실제 구현 내용 일부를 은닉한다.
진짜 객체지향은 정보 은닉에서부터 시작된다.
객체지향 언어를 통해서 얻고자 하는 것이 유연성(기능의 확장, 교체, 변경)이라면 정보 은닉은 그것을 가능하게 하는 전략이다. 객체, 상속, 캡슐화 등은 정보 은닉의 수단에 불과하다. 그리고 좋은 정보 은닉은 잘 된 추상화를 통해 얻어진다.
많은 개발자들이 객체지향에 들어서면서 캡슐화를 정보 은닉이라고 배운다. 몇몇 훌륭한 블로그들을 제외하고는 대부분의 블로그들이 정보 은닉 = 캡슐화로 설명하고 있다. 매우 안타까운 일이다. 정보 은닉을 캡슐화로만 알고 있으면 아직 객체지향 입구에도 못들어 온 것이다.
정보 은닉과 관련하여 인터넷을 검색해 본 결과, 정확하게 정보 은닉을 설명한 것은 아래 글 밖에 없었다.
http://egloos.zum.com/aeternum/v/1232020
정보 은닉의 종류
- 객체의 구체적인 타입 은닉(= 상위 타입 캐스팅)
- 객체의 필드 및 메소드 은닉(= 캡슐화)
- 구현 은닉(= 인터페이스 및 추상 클래스 기반의 구현)
객체지향 개념들간의 관계 UML
간략히 설명하자면 다음과 같다.
- OOP : Object Oriented Programming
- OOP는 유연성(Flexibility)을 가진다
- 유연성은 캡슐화와 추상화, 다형성을 가진다.
- 상속은 캡슐화를 활용하고, 다형성은 상속을 이용해서 만들어진다.
- 캡슐화를 통해서 가시성(Visibility - 접근 제어자) 개념이 만들어지고, 클래스 개념이 만들어진다
- 클래스는 타입과 필드, 그리고 메소드를 가지고 있다.
- 클래스 개념은 인터페이스(Interface), 추상 클래스(abstract Class), 구체 클래스(Concrete Class) 개념을 파생시킨다.
- 객체(Object)는 구체 클래스(Concrete Class)가 가진 개념을 포함한다.
- 필드는 Reference와 Primitive로 나뉜다.
- 클래스와 메소드, 필드는 가시성을 가진다.
- 클래스는 상속 가능(Inheritable)하다.
- 메소드는 재정의 가능(Overridable)하고, 오버로딩 가능(Overloadable)하다.
RESTful API
'무지성 메모' 카테고리의 다른 글
■ 스프링 IoC(제어의 역전) (0) | 2022.02.28 |
---|---|
■ list의 요소 수정하기 (0) | 2022.02.27 |
■ CI (Continuous Integration)란? (0) | 2022.02.25 |
■ 자바 정규식 몇개 (카카오 기출 - 신규아이디 추천) (0) | 2022.02.22 |
■ array의 일부분을 copy하는 메소드 (0) | 2022.02.21 |