OOP란?
객체 지향 프로그래밍(Object-Oriented Programming, OOP)은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러개의 독립된 단위(객체)들의 모임으로 파악하고자 하는 것이다.
객체 지향 프로그래밍(이하 OOP)은 프로그램을 유연하고 변경이 용이하게 만들기 때문에 대규모 소프트웨어 개발에 많이 사용된다. 또한 프로그래밍을 더 배우기 쉽게 하고 소프트웨어 개발과 보수를 간편하게 하며, 보다 직관적인 코드 분석을 가능하게 하는 장점을 갖고 있다. 그러나 지나친 프로그램의 객체화 경향은 실제 세계의 모습을 그대로 반영하지 못한다는 비판을 받기도 한다.
OOP는 OOP가 사용되기 전 프로그래밍에서 복잡한 데이터를 다룰 때 함수로만 처리하다 보면 여러가지 에러와 버그가 발생하는 문제점이 발생한다. 즉, 코딩을 할 때 복사/붙여넣기를 많이 하게 된다면 중복되는 부분이 많기 때문에 문제가 발생되지 않게 최소화시켜야 한다. 중복되는 코드는 시간을 늘리고, 버그를 만들고, 코드에 수정사항이 생기면 수 많은 곳을 수정해야하는 문제가 발생한다. OOP는 이런 문제점을 해결하고 복잡한 데이터를 조금 더 쉽게 처리할 수 있게 도와준다. 하지만 한번 사용하고 버리는 불필요한 클래스를 만드는 것 또한 피해야 한다는 점을 잊어서는 안된다.
OOP를 사용하는 이유
이제 OOP를 코드를 통해 알아볼 것이다. 먼저 bus와 car의 데이터가 입력된다고 가정해보겠다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 버스1
bus1_speed = '15'
bus1_color = 'bule'
# 버스2
bus2_speed = '30'
bus2_color = 'red'
# 차1
car1_speed = '20'
car1_color = 'gray'
car1_model = 'k9'
# 차2
car2_speed = '25'
car2_color = 'white'
car2_model = 'sonata'
|
cs |
다음과 같이 OOP를 사용하기 이전에는 버스와 차의 데이터를 입력할 때 각각 변수에 대해 값을 입력해주고 다른 데이터가 들어오면 똑같은 작업을 반복해주어야 하기 때문에 복사/붙여넣기를 하는 경우가 많아지고 변수도 많아지며 수정사항이 생겼을 때는 바뀔 곳을 찾아서 바꾸어야한다. 또한 반복이 되기 때문에 데이터의 양이 너무 많아진다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
# OOP
# 기능에 따라 Car클래스 안에 있는 함수와 변수만 추가해주면 된다.
# 아래와 같이 클래스 선언 순서에 관계없이 실행된다.
# 절차 프로그래밍과 다르게 기능별로 수행되기 때문이다.
class Bus:
def __init__(self, speed, color):
self.speed = speed
self.color = color
def drive_bus(self):
self.speed = 70
class Car:
def __init__(self, speed, color, model):
self.speed = speed
self.color = color
self.model = model
def drive(self):
self.speed = 50
myCar = Car(0,"green", "testCar")
myBus = Bus(0,'black')
print("Car Object Create Complete")
print("Car Speed ", myCar.speed)
print("Car Color ", myCar.color)
print("Car Model ", myCar.model)
print("Bus color ", myBus.color)
#Car object method Call
myCar.drive()
myBus.drive_bus()
print("Car Speed by drive ", myCar.speed) # 입출력순서에 상관없이 speed 개체를 변경하여 볼 수 있다.
print("Bus Speed by drive ", myBus.speed)
|
cs |
하지만 다음과 같이 같은 데이터로 OOP를 사용한 예시를 보면 Bus, Car를 각 클래스로 지정하여 변수를 지정하면 myBus, myCar와 같이 변수 순서에 따라 입력값을 입력하여 사용할 수 있다.
OOP구성 4가지
1. 캡슐화
내부 속성(변수)과 함수를 하나로 묶어서 클래스로 선언하는 일반적인 개념
- 캡슐화형태로 코드를 작성하지 않으면 특정 기능(함수, 변수)에 직접 접근하게 되는 상황이 된다.
- 기능이 많아질수록 재사용의 개념을 활용하기가 어려움
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
# 캡슐화코드
class Encap:
def __init__(self,value):
self.value = value
print('init :', self.value)
def _set(self):
print('set :', self.value)
def printTest(self):
print('printTest :', self.value)
# def __printTest2(self):
# print('printTest :', self.value)
# object 생성
e = Encap(10)
# object 실행
# 케이스1
e.__init__(20)
e._set()
e.printTest()
#e.__printTest2()
print('\n')
# 케이스2
e.__init__(30)
e._set()
e.printTest()
|
cs |
2. OOP의 상속(Inheritance)과 포함(Composition)
상속 : 상위 클래스의 모든 기능(함수, 변수)을 재사용할 수 있다.
포함 : 다른 클래스의 일부기능(함수)만을 재사용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
# 상속코드
# 클래스 선언
class Person:
def __init__(self, name):
self.name = name
class Student(Person): # Person 클래스 상속받음(name 변수를 파라미터로 재사용)
def study(self):
print (self.name + " studies hard")
class Employee(Person): # Person 클래스 상속받음(name 변수를 파라미터로 재사용)
def work(self):
print (self.name + " works hard")
# object 생성
s = Student("Dave")
e = Employee("David")
# object 실행
s.study()
e.work()
|
cs |
Person클래스에 이름을 입력하는 함수를 정의하고 Student와 Employee 클래스에 Person클래스를 상속받을 수 있도록 괄호 안에 넣어준다. 그 후 코드를 실행하면 Dave studies hard/David works hard로 출력되는 것을 확인 할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
# 포함코드1
# 클래스 선언
class Person:
def __init__(self, age):
self.age = age
def printPerson(self): # 포함을 사용하기 위한 Person 클래스의 다른 함수
print('Person_printPerson')
class Student:
def __init__(self, age):
self.age = age
self.p = Person(self) # Student가 Person에 포함되기 위해, Person 클래스에 대한 object 생성
def aging(self):
return self.age
def personComposit(self,age):
return age, self.p.printPerson() # 포함개념적용된 Student 클래스의 Person object의 함수
# object 생성
s = Student(10) # 한 번 생성된 object는 파라미터가 변하지 않는 이상 출력값 또한 변하지 않는다.
p = Person(20)
# object 실행
print("s.aging() :", s.aging()) # result : 10
print('\n')
print('test')
print('print with test', "s.personComposit() :", s.personComposit(40))
# result 출력 순서 : Person.printPerson -> print with test -> s.personComposit()
print('\n')
print('test2')
print('print with test2', "p.printPerson() :", p.printPerson())
# result 출력 순서 : Person.printPerson -> print with test2 -> p.printPerson()
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
# 포함코드2
# 클래스 선언
class Bill():
def __init__(self, description):
self.description = description
class Tail():
def __init__(self, length):
self.length = length
class Duck():
def __init__(self, bill, tail):
self.bill = bill
self.tail = tail
def about(self):
print(
f"This duck has a {self.bill.description} and a {self.tail.length}.")
# object 생성
duck = Duck(Bill('bill object'), Tail('tail object'))
# object 실행
duck.about()
|
cs |
3. 추상화(abstraction)
복잡한 내용에서 핵심적인 개념 및 기능을 요약하는 것을 의미한다.
- object의 기능에 따라 추상클래스(상위클래스)를 상속받아 개별적으로 클래스(하위클래스)를 생성한다.
- 기본적으로 추상메소드를 선언하며 실제 실행되는 기능은 보여지지 않는다.
- 실제 실행되는 기능은 선언된 추상클래스를 상속받은 다른 클래스의 메소드에서 확인할 수 있다.
- 추상클래스를 사용하는 이유
- 대형 프로젝트를 진행하는 경우 또는 프로그램이 복잡해지는 경우 1차적인 설계를 위해 기능을 추상화시켜놓고, 활용여부는 차후 결정하기 위함이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
# 추상화 코드
from abc import * # abc 모듈의 클래스와 메소드를 갖고온다.(abc : abstract base class)
# 추상 클래스
class People(metaclass=ABCMeta):
# 추상 메소드
@abstractmethod # 추상 메소드에는 @abstractmethod를 선언해줘야 함
def charecter(self):
pass # 추상 메소드는 기능 내 실제 실행내용은 없다.
# 상속받는 클래스
class Student(People):
def charecter(self, pow, think):
self.pow = pow
self.think = think
print('체력: {0}'.format(self.pow))
print('생각: {0}'.format(self.think))
# 상속받는 클래스
class Driver(People):
def charecter(self, pow, think):
self.pow = pow
self.think = think
print('체력: {0}'.format(self.pow))
print('생각: {0}'.format(self.think))
# Student object 생성
peo1 = Student()
print('Student : ')
# Student object 실행
peo1.charecter(30, 10)
print()
# Driver object 생성
peo2 = Driver()
print('Driver : ')
# Driver object 실행
peo2.charecter(10, 10)
|
cs |
4. 다형성
구현되는 하위클래스에 따라 클래스를 다르게 처리하는 기능이다.
- 상속과 유사하다고 느껴질 수 있지만, 상속은 상위클래스의 기능(함수, 변수)을 재사용한다.
- 다형성은 상위클래스의 기능을 변경하여 사용하는 것이다.(그대로 재사용하지 않는다.)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
class Person:
def run(self):
print('run')
def play(self):
print('play')
class Student(Person):
def run(self):
print('fast run')
def play(self):
print('play')
class teacher(Person):
def teach(self):
print('teach')
def play(self):
print('teach play')
# 리스트를 생성한다.
number = list()
# 생성한 리스트에 다형성 개념을 위해 다른 클래스(Student, teacher)가 상위 클래스(Person)를 참조할 수 있도록 한다.
number.append(Student()) # 리스트 끝에 서브 클래스 Student()를 넣습니다.
number.append(teacher()) # 다시 리스트 끝에 서브 클래스 teacher()를 넣습니다.
print("=========")
for Student in number:
Student.run() # 상위클래스인 Person의 run은 상속하여 사용하지만 내용은 다르다.
print("=========")
for teacher in number:
teacher.play() # 상위클래스인 Person의 play는 상속하여 사용하지만 내용은 다르다.
|
cs |