본문 바로가기
Language/java

JAVA의 객체지향 프로그래밍(캡슐화/정보은닉/추상화/상속/다형성)

by S2채닝S2 2023. 1. 10.

객체지향 프로그래밍

- 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에 해당하는 메소드만을 호출할 수 있다. 

 

 

 

 

정리

- 객체지향은 절차지향 프로그래밍의 단점을 보완하기 위해 등장

- 객체지향이란 기능을 분할하여 객체에 위임하며, 객체 간 협력을 통해 프로그램을 만드는 것을 의미

- 각 객체는 독립적이어야함(캡슐화, 정보은닉)

- 독립적이면서도 안정적으로 기능을 제공하기 위해 추상화와 상속, 다형성의 개념이 사용됨.

 

최근댓글

최근글

skin by © 2024 ttuttak