들어가며
소프트웨어 개발 과정
- 소프트웨어 개발은 정답이 있는게 아니라 정답을 찾아가는 과정이다.(어두운 방안에서 출구를 찾는 과정)
- 소프트웨어 개발 과정은 중간중간에 갈래길이 많아 수도 없는 의사결정을 해야 한다.
컴퓨터의 역사
- 컴퓨터의 역사는 100년정도 됨
- 100년 동안 우리가 여러가지 경험을 하면서 지금이 객체 지향 패러다임이 정답인지는 의문을 가져야 한다. 왜냐하면
- 옛날에는 천동설을 많은 사람들이 믿었지만 지금은 지동설이 사실임.
- 지금의 객체지향 패러다임도 틀릴수가 있다. 우리는 항상 의심하고 더 나은 방법을 고민하고 찾아내야 한다.
프로그래밍 패러다임의 기본 개념
1. 명령형 프로그래밍(Imperative Programming)
명령형 프로그래밍은 컴퓨터를 "아무것도 모르는 순수한 아이"로 간주하고, 모든 작업을 세부적으로 지시하는 방식이다.
- 특징:
- 프로그래머가 작업을 단계적으로 나누어 하나씩 직접 지시한다.
- 퀵소트와 같은 알고리즘을 구현할 때, 각 단계마다 상태를 지속적으로 업데이트해야 한다.
- 튜링 머신을 기반으로 하여 상태(state) 변경이 핵심이다.
2. 선언형 프로그래밍(Declarative Programming)
선언형 프로그래밍은 "무엇을 할 것인지"만 지시하고, "어떻게 할 것인지는" 시스템이 알아서 처리하도록 하는 방식이다.
- 특징:
- 대표적인 예로 SQL의 SELECT 쿼리를 들 수 있다.
- 함수형 프로그래밍과 밀접하게 관련되며, 함수 합성이 가능하다.
객체지향 프로그래밍(OOP)의 개념과 특징
1. 객체지향의 정의와 본질
객체지향 프로그래밍은 현실 세계를 소프트웨어로 자연스럽게 맵핑(mapping)하기 위해 고안되었다.
- 현실 세계의 사물(객체)을 소프트웨어의 객체로 표현한다.
- 예를 들어, 현실의 학생(student)은 컴퓨터 세계에서도 student 객체로 표현된다.
2. 객체와 상호작용
객체지향의 기본 단위는 객체(Object)이며, 각각의 객체는 고유한 주특기(기능)를 갖는다.
- 객체는 서로 메시지(Message)를 주고받으며 협력한다.
- 이러한 상호작용은 현실 세계에서 각자가 맡은 역할을 수행하며 협력하는 모습과 유사하다.
- 예: 레스토랑에서 캐셔, 요리사, 홀 직원들이 각자의 역할을 통해 협력하여 문제를 해결.
3. 객체지향의 장점
- 현실 세계와 소프트웨어의 자연스러운 맵핑.
- 유지보수 및 확장이 용이.
- 역할에 따라 객체를 분리하여 관리하므로, 복잡한 문제를 해결하기에 적합.
소프트웨어 개발 프로세스
1. 객체지향 소프트웨어 개발 단계
소프트웨어 개발은 문제를 분석하고 설계한 후 구현하는 과정이 반복적으로 이루어진다.
- 분석(OOA): 해결해야 할 문제를 정확히 이해하고 정의.
- 설계(OOD): 문제 해결을 위한 구조 설계.
- 구현(OOP): 설계를 바탕으로 프로그래밍.
2. 왜 객체지향을 사용해야 하는가?
성공적인 소프트웨어 개발을 위해 다음 조건을 만족해야 한다.
- 사용자가 원하는 기능을 제공.
- 개발 기간 내에 완성.
- 변화하는 요구 사항에 유연하게 대응.
객체지향은 이러한 요구를 충족하기 위해 적합한 패러다임으로, 특히 큰 문제를 잘게 나누어 접근하는 방식에 강점이 있다.
객체지향의 역사와 미래
객체지향 프로그래밍은 Alan Kay가 Smalltalk 언어를 개발하면서 처음 도입했다.
- 현재는 객체지향이 주류지만, 함수형 언어가 점차 더 많은 주목을 받고 있다.
- 시대의 흐름에 따라 다양한 패러다임을 학습하고 적용할 준비가 필요하다.
객체지향 프로그래밍은 현실 세계와 소프트웨어를 자연스럽게 연결하며, 유지보수와 확장성에서 강점을 지닌다. 그러나 모든 패러다임은 완벽하지 않으며, 시대와 요구 사항에 따라 적절한 접근 방식을 선택하는 것이 중요하다.
객체지향을 사용하는 이유
1. 복잡한 문제를 다룰 수 있는 구조적 강점
객체지향은 복잡한 시스템을 객체 단위로 나누어 관리한다.
- 현실 세계를 객체로 추상화하여 문제를 구조화하고
- 각 객체는 독립적인 역할을 수행하며, 복잡성을 줄이는 데 기여한다.
2. 변화에 유연한 대처
현대 소프트웨어 개발 환경은 빠르게 변화하는 요구 사항을 수용해야 한다.
- 객체지향은 설계와 구현 단계에서 유연성을 제공한다.
- 변경 사항에 대한 영향을 최소화하고 유지보수를 용이하게 한다.
- 특히, 객체 간의 관계와 역할은 안정적으로 유지되므로 적응력이 뛰어나다.
3. 재사용성을 통한 효율성
객체지향은 코드뿐만 아니라 설계 레벨에서의 재사용성을 제공한다.
- 재사용성의 한계: 객체 수준의 재사용성은 제한적일 수 있지만, 디자인 패턴과 프레임워크를 통해 극복 가능하다.
디자인 패턴의 중요성
1. 디자인 패턴이란?
디자인 패턴은 자주 발생하는 문제를 해결하기 위한 검증된 솔루션이다.
- Problem/Solution Pair in a Certain Context: 문제와 솔루션의 조합으로, 반복적으로 나타나는 상황에서 적용된다.
- 예: 어댑터 패턴은 110볼트와 220볼트의 전압 변환처럼, 인터페이스 불일치를 해결한다.
2. 왜 디자인 패턴을 배워야 하는가?
- 설계 과정에서 반복적으로 발생하는 문제를 효과적으로 해결
- 고수들의 노하우를 기반으로 효율적인 솔루션 도출
- 코드를 읽고 패턴을 이해하면 설계 능력이 향상
프레임워크란 무엇인가?
1. 프레임워크의 정의
프레임워크는 뼈대(Skeleton) 역할을 하는 반쯤 완성된 애플리케이션(Semi-complete Application)이다.
- 개발자는 프레임워크 위에 살을 붙이는 방식으로 애플리케이션을 구축한다.
- 제어의 역전(Inversion of Control): 개발자가 작성한 코드를 프레임워크가 호출하여 동작한다.
2. 프레임워크의 역할
- 도메인 특정 문제를 빠르게 해결할 수 있도록 지원
- 디자인과 코드를 재사용하여 개발 시간을 단축
- 비슷한 애플리케이션을 쉽게 생성할 수 있는 기반 제공
객체지향의 기본 구성 원리
1. OOP의 3요소: OOA, OOD, OOP
- OOA(Object-Oriented Analysis): 문제를 객체로 분석
- OOD(Object-Oriented Design): 객체 간 관계와 역할 설계
- OOP(Object-Oriented Programming): 설계된 구조를 구현
2. 데이터와 캡슐화
- 데이터를 보호하고 객체 내에서만 변경 가능하도록 캡슐화(encapsulation)를 적용
- 데이터는 보통 private으로 선언하여 외부 접근을 제한
3. 성공적인 소프트웨어의 특징
- 기능은 자주 바뀔 수 있으나, 객체 간의 관계와 역할은 잘 유지됨
- 유지보수와 확장 가능성을 극대화
객체지향의 모티베이션
객체지향 프로그래밍은 복잡한 문제를 해결하고 변화에 유연하게 대응할 수 있는 설계 방법을 제공한다
- 디자인 패턴과 프레임워크를 통해 설계와 구현 효율성을 높일 수 있다.
- 데이터와 기능을 캡슐화하고 객체 간 협력을 통해 안정적인 소프트웨어를 개발할 수 있다.
객체지향의 기본 개념
객체지향의 핵심은 서로 다른 역할과 기능을 가진 객체(Object)들이 협력하여 문제를 해결하는 데 있다. 이를 이루는 3대 요소는 다음과 같다.
1. 오브젝트(Object)
- 오브젝트란 현실 세계의 개별적이고 독립된 개념이나 사물을 소프트웨어적으로 표현한 것이다.
- 특징:
- 각각의 오브젝트는 유니크한 아이덴티티(Unique Identity)를 가져야 한다.
- 오브젝트는 상태(State)와 행동(Behavior)을 가지고 있다.
- 예: "나의 노트북"은 특정 상태(모델, 사용 기간)와 행동(작동, 종료)을 가진 객체이다.
2. 클래스(Class)
- 클래스는 비슷한 성격을 가진 객체들을 그룹화한 틀이다.
- 클래스와 오브젝트의 관계:
- 클래스는 객체를 생성하기 위한 설계도(Factory)이며, 객체는 클래스의 실체(Product)이다.
- 클래스는 정적 개념, 객체는 동적 실체이다.
3. 상속(Inheritance)
- 상속은 클래스 간의 관계를 정의하며, 재사용성과 확장성을 제공한다.
- is-a 관계:
- 상속은 "부모는 자식이다"라는 일반화-특수화 관계를 나타낸다.
- 예: "고양이는 동물이다."
객체지향의 주요 원칙
1. 인터페이스와 구현의 분리
- 인터페이스(Interface)는 외부에서 객체와 상호작용하는 방식을 정의
- 구현(Implementation)은 내부 동작 방식으로 캡슐화(encapsulation)되어야 한다.
- 예:
- TV의 버튼은 인터페이스이며, 내부 동작은 사용자가 알 필요가 없음.
2. 다형성(Polymorphism)
- 하나의 메시지에 대해 각 객체가 서로 다른 방식으로 반응할 수 있음.
- 예:
- cat.play()와 dog.play()는 동일한 play 메시지지만, 동작은 다를 수 있음.
3. 상태(State)와 동작(Behavior)
- 객체의 상태는 객체가 가진 속성(Attribute)으로 정의
- 상태는 외부에서 직접 변경할 수 없으며, 동작(메서드)을 통해 변경해야 한다.
상속의 의미와 설계 원칙
상속은 객체지향 프로그래밍의 핵심 개념 중 하나로, 새로운 클래스가 기존 클래스의 속성과 동작을 물려받아 재사용하거나 확장할 수 있는 메커니즘이다. 이를 통해 코드의 중복을 줄이고, 확장성과 유지보수성을 높이는 데 기여한다. 그러나 상속은 단순히 "코드 재사용"의 수단이 아니라, 설계 원칙에 맞게 사용해야만 제대로 된 효과를 얻을 수 있다.
1. 상속의 정의
- 상속은 부모 클래스의 속성과 동작을 자식 클래스에 물려주는 관계이다.
- 자식 클래스는 부모 클래스의 동작을 재정의(Overriding)하거나, 확장(Extending)할 수 있다.
2. 상속의 주요 원칙
- is-a 관계를 만족해야 함:
- 자식 클래스는 반드시 부모 클래스의 일반적인 속성을 가져야 한다.
- 예: "학생은 사람이다."
-
- Student는 Person이다. (적절)
- Car는 Animal이다. (부적절)
- Student는 Person이다. (적절)
- 부모가 할 수 있는 모든 일을 자식도 할 수 있어야 함:
- 부모 클래스의 모든 메서드는 자식 클래스에서도 동일하게 동작해야 한다.
- 프로그램에서 부모 클래스의 인스턴스를 사용하는 곳에 자식 클래스의 인스턴스를 대체하더라도 프로그램의 기능에 문제가 없어야 한다.
- 이를 Liskov Substitution Principle이라고 한다.
3. 오버라이딩과 오버로딩
- 오버라이딩(Overriding):
- 자식 클래스가 부모 클래스의 메서드를 재정의.
- 메서드의 시그니처는 변경하지 않음.
- 자식 클래스가 부모 클래스의 메서드를 재정의할 때, 메서드의 시그니처(이름, 매개변수, 반환값)는 변경하지 않는다.
- 예: 부모 클래스의 move() 메서드를 자식 클래스에서 더 구체적인 동작으로 재정의.
- 오버로딩(Overloading):
- 같은 이름의 메서드를 서로 다른 매개변수로 정의하여 다양한 입력을 처리할 수 있게 합니다.
- 예: print(int number)와 print(String message)는 같은 이름의 메서드지만, 다른 데이터를 처리.
4. 상속 설계 시 주의점
- 부모와 자식 간의 관계가 애매하거나, 자식이 부모와 너무 다르면 상속은 부적합.
- 상속보다는 컴포지션(Composition)을 고려할 것.
다중 상속(Multiple Inheritance)에 대한 신중한 접근
- Java와 같은 언어에서는 다중 상속을 지원하지 않는다. 이는 다중 상속 시 다이아몬드 문제(Diamond Problem)라는 구조적 혼란이 발생할 수 있기 때문이다.
- 대신, 다중 상속의 유연성을 인터페이스(Interface)를 통해 제공한다.
캡슐화와의 조화
- 상속을 사용할 때 부모 클래스의 속성은 private 또는 protected로 설정하여 데이터의 직접 접근을 제한하고, 상속받은 자식 클래스에서 필요한 경우만 접근하도록 해야 한다.
- 외부에서 사용할 인터페이스는 public 메서드로 노출하되, 내부 구현은 숨긴다.
객체지향 설계에서 자주 사용하는 용어
- 객체의 유니크 아이덴티티:
- 객체를 서로 구분할 수 있는 속성
- 예: 두 개의 노트북은 동일한 모델이어도 서로 다른 객체로 취급
- 메시지(Message):
- 객체 간 상호작용 시 요청과 응답
- 예: "TV 켜져!"라는 메시지를 TV 객체에 보냄
- 캡슐화(Encapsulation):
- 데이터를 보호하고, 외부에는 필요한 인터페이스만 제공
마치며
객체지향 프로그래밍은 객체, 클래스, 상속을 통해 복잡한 문제를 구조화하고 유연하게 대처할 수 있게 한다. 상속과 다형성을 제대로 활용하면 재사용성과 유지보수성이 높은 소프트웨어를 설계할 수 있다. 그러나 모든 것을 객체로 다루는 것은 적절하지 않을 수 있으며, 설계 시 is-a 관계를 신중히 판단해야 한다.
'객채지향 개발론' 카테고리의 다른 글
| SOLID 원칙 - 객제지향을 잘 하려면 SOLID를 기억해라 (0) | 2025.12.21 |
|---|---|
| 객체지향 개발론 02 (8) | 2024.12.29 |