객체지향 언어

김동한·2024년 5월 4일
0

CS

목록 보기
3/13
post-thumbnail

객체란..?

객체는 영어로 object. 즉, 세상에 존재하는 어떠한 물건, 혹은 생명체라고 생각하면 편하다.
프로그램에서의 객체는 하나의 기본 단위라고 생각할 수 있다. 프로그램을 단순히 데이터, 데이터에 대한 처리 방법으로 나누는 것이 아닌, 객체라는 기본 단위로 나누고 객체간의 상호작용으로 동작하게끔 하는 프로그래밍 방식이 바로 객체지향 프로그래밍이다. 객체는 '메소드(함수), 변수'를 가지며, 특정 역할을 수행하도록 정의한, 추상적인 개념이다.

객체 지향 프로그래밍_object oriented programming(OOP)

프로그램 설계 방법중 하나이며, 특정언어가 객체지향 언어라는 건 사실 틀린 표현이며, 특정언어에서 객체지향 프로그래밍이 가능하게 지원해준다가 더 걸맞는 표현이다. 다양한 언어에서 객체지향 프로그래밍을 구현할 수 있으며, 여러 언어간의 약간의 차이가 있다. 예를 들어서 모든 언어가 class나 접근 제한자(public이나 private)를 사용하지는 않는다. 대표적인 예로 JavaScript는 prototype 객체지향을 사용하고 있고 Python에는 접근제한자가 따로 없다

객체지향 프로그래밍의 특성

캡슐화

여러 변수와 함수를 하나의 단위로 묶는 것을 캡슐화 라고한다.(데이터 번들링(bundling)). 대개 프로그래밍 언어에서 이는 클래스를 통해 구현된다. 해당 클래스의 인스턴스 생성해서, 인스턴스를 통해 묶음 단위였던 클래스 안에 포함된 멤버 변수와 메소드에 접근이 쉽다. 클래스는 객체 지향 프로그래밍을 지원하는 거의 대부분의 언어가 제공하는 기본이 되는 특성 및 요소이다.

class Student:
	def __init__(self,name,height):
    	self.name=name
        self.height=height
student_1=Student('kevin',178)
print(student_1.height)
>>178

위의 코드 블럭 예시를 보면, Student라는 클래스에 각 학생의 이름, 키를 묶어서 저장할 수 있음을 알 수 있다. 함수를 따로 만들지는 않았지만, 클래스내에 특정 함수를 선언하면, 어떠한 역할,기능을 수행가능하다. student_1이라는 인스턴스를 생성해 kevin이라는 이름과 178의 키를 가진 학생을 생성하고, 간단하게 "instance명.변수명" 구조로 클래스의 멤버 변수에 접근해 출력 가능함을 확인하였다.

정보은닉

프로그램의 세부적인 구현을 드러내지 않고,특정 모듈 내부로 감추는 것을 의미한다. 유지보수성을 높여주고, 모듈간의 결합도를 덜어, 프로그램의 유연함을 높여준다. 객체 지향 언어에서 사용되는 클래스 기준으로, 클래스의 외부에서는 바깥에 노출된 메소드(함수)로만 접근이 가능하고, 클래스 내부에서는 어떤 처리가 이루어지는 지 알 수 없다.

  • public: 클래스의 외부에서 사용 가능
  • protected: 다른 클래스에 노출되지않지만, 자식 클래스에는 노출됨
    (자식클래스에 대해서는 상속 부분에서 자세히 설명)
  • private: 클래스의 내부에서만 사용 가능.

📌Python에서의 정보은닉
접근 한정자를 property의 접두사 _ 언더바로 사용한다. getter,setter는 decorator를 사용한다. 아래의 코드를 사용하면 편하게 이해할 수 있다.

class Person:
	def __init__(self,name,age):
    self._name=name #protected
    self._age=age #protected

	def get_name(self):
    	return self._name
        
    def set_name(self,name):
    	self._name=name
        
    def get_age(self):
    	return self._age
        
	def set_age(self,age):
    	self._age=age
        

이 코드를 보면, name 그리고 _age는 접두사 를 사용해서, 접근한정자로 protected로 선언한 것을 알 수 있다. 객체 내부에서는 접근이 가능하지만, 객체의 외부에서는 직접 접근할 수가 없다. 이렇게 외부에서의 접근을 제어해, 객체의 안정성을 보장 가능하다.

class Person:
	def __init__(self,name,age):
    self._name=name #protected
    self._age=age #protected

	@property
	def name(self):
    	return self._name
    
    @name.setter
    def name(self,name):
    	self._name=name
    
    @property
    def age(self):
    	return self._age
   
   	@age.setter    
	def age(self,age):
    	self._age=age
       

@ property : name,age각각 외부에서 name() age() 메서드(함수)를 호출하는 것과 같은 효과를 보여줌.
@ property.setter : name,age property에 값을 할당할 때, 메서드(함수)를 호출하는 것과 같은 효과를 보여줌.

상속

자식 클래스가 부모 클래스의 특성,기능을 그대로 물려받는 것을 의미한다. 기능의 일부분을 변경해야하면, 자식 클래스에서 상속받은 그 기능만을 수정해서 다시 정의하게 된다. 이를 오버라이딩(overriding)이라고 한다. 상속은 캡슐화를 유지하며, 클래스의 재사용을 용이하게 한다.


class Country:
    """Super Class"""

    name = '국가명'
    population = '인구'
    capital = '수도'

    def show(self):
        print('국가 클래스의 메소드입니다.')


class Korea(Country):
    """Sub Class"""

    def __init__(self, name):
        self.name = name

    def show_name(self):
        print('국가 이름은 : ', self.name)

>> from inheritance import *
>> A=Korea('대한민국')
>> A.show()
국가 클래스의 메소드입니다.
>> A.show_name()
국가 이름은 : 대한민국
>> A.capital
수도
>> A.name
대한민국

상속받은 서브 클래스에서 상속해준 슈퍼(부모) 클래스의 속성과 메소드(함수)를 모두 사용할 수 있음을 볼 수 있다.

메소드 오버라이딩

class Country:
    """Super Class"""

    name = '국가명'
    population = '인구'
    capital = '수도'

    def show(self):
        print('국가 클래스의 메소드입니다.')
        
class Korea(Country):
    """Sub Class"""

    def __init__(self, name,population, capital):
        self.name = name
        self.population = population
        self.capital = capital

    def show(self):
        print(
            """
            국가의 이름은 {} 입니다.
            국가의 인구는 {} 입니다.
            국가의 수도는 {} 입니다.
            """.format(self.name, self.population, self.capital)
        )

위와 같이 자식클래스에서 오버라이딩을 수행하면, 기능을 수정해서, 수행하게끔 할 수 있다.

>> from inheritance import *
>> A=Korea('대한민국',50000000,'서울')
>> A.show()
	국가의 이름은 대한민국 입니다.
    국가의 인구는 50000000 입니다.
    국가의 수도는 서울 입니다.

슈퍼(부모) 클래스에서는 show 메서드(함수)는 '국가 클래스의 메소드입니다.'를 호출하는 기능을 수행했지만, 오버라이딩을 통해서 그 기능이 바뀐 것을 확인할 수 있다. super 키워드를 통해서, 슈퍼(부모) 클래스에서의 오버라이딩 되기 이전의 메서드(함수)의 기능을 수행하게 하는 것도 가능하다.


📌다중 상속
또한, Python과,C++은 다중상속이 가능하다. 하지만, Java와 C#은 다중상속이 불가능한 언어다. 다중상속은 별로 어렵지않은데, 자식 클래스가 여러개의 부모 클래스를 인자로 상속받는 것이다.

다형성

하나의 변수 또는 함수가 상황에 따라서 다른 의미로 해석될 수 있다는 것을 의미한다.

  • 서브타입 다형성
    상위 클래스를 생성하고, 이를 상속받는 다수의 하위 클래스를 만든다. 그 후에, 상위 클래스의 포인터, 참조변수 등등이 하위 클래스에서의 객체를 참조하게 하는 것이다. 각 하위 클래스는 이전에 설명한 오버라이딩을 통해서, 상위 클래스의 메소드(함수) 위에 자신의 메소드(함수)를 덮어씌운다. 상위 클래스의 참조변수가 하위 클래스의 객체 중 어떤 하위 클래스의 객체를 참조하냐에 따라 호출되는 메소드(함수)가 달라지는 것이다.

  • 매개변수 다형성
    타입을 매개변수로 받아서 새로운 타입을 반환하는 기능이다. 타입 매개변수를 정의한 클래스나 메소드는 사용할때 매개변수에 타입을 지정한다 그리고, 컴파일을 할때 지정한 타입에 따라서 해석된다.
    📌템플릿
    C++에서 사용하는 개념으로, 타입 매개변수를 입력한 타입으로 치환한 코드를 생성하는 방식이다. 타입 뿐 아니라 변수도 입력할 수 있으며, 객체 내부에서 연산이나 함수 호출을 할 수 있지만, 해당 연산이나 함수가 정의되지 않은 타입을 매개변수로 넣으면 컴파일 에러가 발생하며 컴파일이 느려지고 파일이 커진다.
    📌제네릭
    Java와 C# 등에 도입된 개념으로, 지정한 타입 매개변수에 해당하는 타입만을 사용하겠다고 약속하는 방식이다. 타입 매개변수가 특정 객체를 상속할 경우 상속하는 객체의 함수는 호출할 수 있지만 그렇지 않을 경우 타입 매개변수로 지정된 객체의 멤버에는 접근할 수 없다.

  • 임시 다형성
    📌함수 오버로딩(function overloading)
    C++과 C#, Java에서는 함수 오버로딩을 통해 동일한 이름의 함수를 매개변수에 따라 다른 기능으로 동작하도록 할 수 있다. 함수 오버로딩을 너무 많이 사용하면 전체적인 코드의 유지보수가 어려워지므로, 템플릿 또는 제네릭으로 대체하는 것이 일반적이다.
    📌연산자 오버로딩(operator overloading)
    C++, C# 등에서는 연산자를 오버로딩해서 기본 연산자가 해당 클래스에 맞는 역할을 수행하게 하는 것이 가능하다. Java에서는 연산자의 오버로딩이 불가능하다. Perl 6나 Smalltalk, F#, Kotlin 등 연산자의 신규 정의가 가능한 언어도 있다.

  • 강제 다형성
    📌묵시적 형 변환(implicit type coercion)
    'double a = 30;'이라는 식이 실행되면 int형 값 30은 double로 묵시적 형 변환이 이루어진다. double은 int보다 크기가 큰 자료형이므로, 이러한 형 변환을 자료형 승급(type promotion)이라고 한다. C++의 변환 생성자에 의한 형 변환도 묵시적 변환에 속하며, 이를 막으려면 생성자 앞에 explicit 키워드를 추가해야 한다.
    📌명시적 형 변환(explicit type coercion)
    'double a = (double)30;'이라는 식은 위와 동일한 결과를 내지만, (double)을 통해 int형 값 30이 double형으로 변환됨을 명시적으로 표현하였다.

이렇게 객체지향 프로그래밍에 대한 개념, 특성에 대해서 알아보았다. 요약하자면, 캡슐화를 통한 정보은닉이 가능하고, 상속을 통해서 클래스의 재사용을 높이며, 자식 클래스에서의 오버라이딩 등을 통해서 서로 다른 기능을 수행하게끔 구현이 가능하며, 전체적으로 유지보수하는데 도움이 되고, 생산성을 향상시키는 것이 가능하다는 것을 알 수 있다. 하지만, 다양한 개념이 포함되는 만큼 구현 난이도가 상당하고, 설계단계에서부터 긴 시간의 투자가 필요함을 느낄 수 있다.


Reference

profile
(●'◡'●)

0개의 댓글