객체지향 프로그래밍
- Java는 객체지향 언어이며, 객체지향 프로그래밍을 제공
- 객체지향 프로그래밍이란? 프로그램을 객체로 구성하는 것
-객체지향의 등장배경
: 프로그램이 거대화하되면서 프로그램을 빠르게 만들거나, 만든 프로그램을 유지 보수하는데 많은 어려움이 있었다. 이를 해결하기 위해 기능을 분리하게 되었으며, 각자의 객체가 독립적으로 각자의 기능을 수행하는 개념인 객체지향이 등장하게 되었다.
객체란?
- 객체 자체는 개념적 용어
- 기술적 용어로 클래스, 인스턴스 등의 단어를 사용
- 객체는 작은 기능을 수행한다.
- 객체와 객체는 서로 협력한다(프로그램을 만들기 위해)
객체 지향을 잘 하는 방법?
- 기능을 잘게 쪼개서 객체에게 위임하고, 서로 협력하게 만드는 것.
- 하나의 객체가 모든 기능을 수행: 절차지향 프로그래밍과 다른 것이 없음
- 기능을 분야별로 객체에 위임하고, 객체간 협력하여 프로그램을 구성하는 것이 객체지향 프로그래밍의 핵심
- 기능을 분산하여 객체에 위임하는 것을 '책임을 갖는다'라고도 함
객체의 구분
- 객체는 type(형)으로 구분한다.
-예: String str = "Hello world"; -> String type의 객체 생성
- 따라서 자바에서는 객체를 생성할 때 '타입 만들기', 즉 '클래스 만들기'라는 말을 사용한다.
<클래스 선언 예시>
package com.my.blog;
import java.lang.*
class MyObject extends Object implements Runnable{
//필드 영역
private int a = 0;
//메소드 영역
public void run(){
a += 1;
}
}
클래스의 인스턴스 생성
MyObject obj = new MyObject();
// obj의 type은 MyObject
객체지향의 특성
1) 캡슐화
- 캡슐처럼 완성도가 있다.
>> 객체는 기능을 수행하는 단위이며, 그 자체로 완전함을 갖는다.
>> 기능을 수행하기 위해 외부의 객체와 협력이 필요한 경우: '외부에 의존한다' 라고 표현
>> 외부에 의존하는 것을 되도록이면 없도록 하고, 있다 하더라도 그 결합을 느슨하게 하는 것이 객체지향
- 정보가 은닉되어 있다.
>> 객체의 정보가 밖에서 접근하거나, 밖에서 객체 내의 정보에 접근하지 못하게 한다.
>> 객체는 스스로 동작할 수 있는 환경을 갖고 있어야 한다. 외부에 의존하거나, 외부의 침략을 제한하여야 한다.
>> 접근제한자를 통해 외부에서의 접근을 차단
class Human{
private Heart heart;
private Blood blood;
protected Gene gene; //자식클래스에 상속됨
Blood donation(){
return null;
}
}
class Child extends Human{
void run(){
this.gene; //부모 클래스 Human으로부터 gene을 상속받아 접근 가능
this.heart; //에러 발생
this.blood; //에러 발생
}
}
*접근제한자
- private: 객체 소유. 클래스 내에서만 접근 가능
- default: (friendly): 같은 패키지 내에서만 접근 가능(패키지 가시성), 다른 패키지 접근 불가
- protected: 같은 패키지 내에서 접근 가능, 다른 패키지의 상속된 개체에서도 접근 가능.
- public: 모두 다 접근 가능
>> 접근제한자에 대한 자세한 설명은 아래의 링크를 참고
https://cocodata12.tistory.com/10
2) 상속
- 상위 = 부모, super, [추상]
- 하위 = 자식, (this), [구체]
- 상속에 대한 오해
>> 공통된 기능을 여러 객체에게 전달하고 싶을 때 상속을 쓰는 경우가 많음.
>> 상속은 추상과 구체의 관계에서 맺어지는 것임
>> 예: 동물 -> 포유류 -> 사람 -> 남자 -> 홍길동
3) 추상화
- 추상체: 추상화된 객체
- 구상체: 구체적인 객체
- 추상제와 구상체는 상대적인 관계
- 객체 간의 관계에서 상위에 있는 것이 항상 하위보다 추상적이어야 함
자바에서 추상체를 만드는 방법
1. 의미적 추상체 생성
2. 추상기능을 가진 객체로 구현
3. 인터페이스로 구현
<코드 예시>
//의미적 추상체
// Login과 KakaoLogin은 각자의 기능을 수행할 수 있지만, 의미적으로 Login이 더 상위개념
// 각자 기능을 가지지만 의미적으로 묶어놓기만 한 경우
class Login{
void login();
}
class KakakLogin extends Login{
void login();
}
//추상기능을 가진 객체로 구현
// KakaoLogin은 추상적인 기능을 가진 Login을 상속받아 그 기능을 구체화(완성)시켜야하는 의무를 가짐
abastract class Login{
abstract void login();
}
class KakaoLogin extends Login{
@Override
void login(){}
}
// 객체 자체가 추상적. 모든 것이 추상으로만 이루어진 객체
// ==>> 인터페이스
// 인터페이스(추상체)를 상속받은 객체(구상체)는 추상체의 필드와 메소드를 구현할 의무를 가짐
interface Login{
abstract void login();
}
class KakaoLogin implements Login{
@Override
void login(){}
}
4) 다형성
- 형(type)이 많다 / 형(type)을 여러가지로 표현할 수 있다.
// 똑같이 KakaoLogin을 통해 생성된 인스턴스인데 형이 KakaoLogin이 될 수도, Login이 될 수도 있음
// 형이 많다, 다형성
KakaoLogin k = new KakaoLogin();
Login k = new KakaoLogin();
- 상속관계나 구현관계를 가지고 있을 때, 그 것의 형(type)으로 표현할 수 있다
※ 추상관계가 필요한 이유
abstract class Login {
abstract login();
}
class KakaoLogin extends Login, Message{
void kakaoLogin(){};
@Override void login(){}
@Override void message(){}
}
class NaverLogin extends Login, Potal{
void naverLogin(){};
@Override void login(){}
@Override void potal(){}
}
// 세 가지 모두 구체클래스는 다르지만 같은 추상클래스로 표현 가능(다형성)
// 모두 Login타입을 가짐.
Login login = new Login(); //login() 호출 가능
Login k_login = new KakaoLogin(); //login() 호출 가능, kakaoLogin()호출 불가, message() 호출 불가
Login n_login = new NaverLogin(); //login() 호출 가능, naverLogin()호출 불가, potal() 호출 불가
- 객체의 기능에 접근하는 것을 제한하고 싶을 때 이용
위의 코드에서 k_login은 KakaoLogin으로부터 형성된 인스턴스이지만, Login 타입이기 때문에 Login에 선언된 추상 메소드만 사용할 수 있고, message()을 호출할 수 없다. 마찬가지로, n_login은 NaverLogin으로부터 형성된 인스턴스이지만 Login 타입이기 때문에 potal()을 호출할 수 없다.
이처럼, 같은 클래스로부터 생성된 객체이지만, 누가 접근하는지에 따라 필터링된 기능만을 제공하고 싶을 때 추상화와 다형성을 이용한다.
Message k_msg = new KakaoLogin(); // message() 호출 가능, login() 호출 불가
Potal n_potal = new NaverLogin(); // potal() 호출 가능, login() 호출 불가
객체가 임의로 다른 객체의 기능을 임의로 사용하면 캡슐화가 깨진 것이다. 객체가 독립적으로 존재하기 위해선 다른 객체에 접근하거나, 다른 객체로부터 접근을 허용해서는 안된다.
따라서, 객체의 기능을 요청할 때 추상체를 통해 접근하고, 한정된 기능만을 제공하여 캡슐화와 정보은닉을 유지하는 것이 가능하다. 위의 예를 다시 살펴보면, Login 추상체를 통해서는 login기능만을 호출할 수 있고, Message 추상체를 통해서는 message, Potal 추상체를 통해서는 potal 기능만을 호출할 수 있다.
이렇게 추상체를 통해 한정된 기능만을 제공하고, 객체 간 협력관계에서 독립적이고 안전하게 기능을 제공할 수 있다.
public interface Login{
public void login();
}
public interface Message{
public void sendMessage();
public void receiveMessage();
}
public interface Friends{
public void addFriends();
public void getFriendsList();
}
public interface Shopping{
public void addToBasketList();
public void buyIt();
}
class Kakao implements Login, Message, Friends, Shopping{
//Login의 기능 구현
@Override public void login(){};
//Message의 기능 구현
@Override public void sendMessage(){};
@Override public void receiveMessage(){};
//Friends의 기능 구현
@Override public void addFriends(){};
@Override public void getFriendsList(){};
//Shopping의 기능 구현
@Override public void addToBasketList(){};
@Override public void buyIt(){};
}
Login login = new Kakao(); // login() 호출 가능
Message message = new Kakao(); // sendMessage(), receiveMessage() 호출 가능
Friends friends = new Kakao(); // addFriends(), getFriendsList() 호출 가능
Shopping shopping = new Kakao(); // addToBasketList(), buyIt() 호출 가능
위의 예제를 살펴보면, Kakao 클래스는 Login, Message, Friends, Shopping이라는 다양한 기능을 가지고 있다.
그러나, Kakao로부터 형성된 인스턴스는 다양한 type를 가진다.(Login, Message, Friends, Shopping)
이를 다형성이라고 하며, 각 인스턴스는 자신의 type에 해당하는 메소드만을 호출할 수 있다.
정리
- 객체지향은 절차지향 프로그래밍의 단점을 보완하기 위해 등장
- 객체지향이란 기능을 분할하여 객체에 위임하며, 객체 간 협력을 통해 프로그램을 만드는 것을 의미
- 각 객체는 독립적이어야함(캡슐화, 정보은닉)
- 독립적이면서도 안정적으로 기능을 제공하기 위해 추상화와 상속, 다형성의 개념이 사용됨.
'Language > java' 카테고리의 다른 글
Java: Adapter와 default 메소드 (0) | 2023.01.16 |
---|---|
인터페이스 심화 (0) | 2023.01.16 |
java 기본 13: 중첩 클래스(중첩(인스턴스)클래스, 정적 중첩 클래스, 로컬 클래스, 익명 클래스 (0) | 2023.01.05 |
java 기본 12: 인터페이스 (1) | 2023.01.05 |
java 기본 11: 클래스 형변환 (0) | 2023.01.05 |