[3] 객체 지향 프로그래밍 소개 - 객체간의 관계
출처 : 디자인 패턴에 뛰어들기 - 알렉산더 슈베츠 도서
위 그림은 객체간의 관계 잘 표현되어 있다.
의존(Dependency) 관계란?
- 교수는 과목을 변수로 가지고 있지 않는다.
- 이는 교수 객체가 과목 객체를 내부에 저장하지 않는다는 의미이다.
- 교수는 과목이라는 파라미터를 통해 특정 행동(메서드)을 수행할 수 있다.
- 예를 들어, 교수는 과목을 가르칠 때 과목 객체를 파라미터로 받아서 해당 과목을 가르치는 메서드를 실행할 수 있다.
- 예를 들어, 교수는 과목을 가르칠 때 과목 객체를 파라미터로 받아서 해당 과목을 가르치는 메서드를 실행할 수 있다.
즉, 교수는 과목 객체와 일시적으로 상호작용하지만, 이를 내부적으로 저장하거나 지속적으로 참조하지 않습니다. 이러한 관계는 일시적인 의존성으로, 과목 객체의 변화에 따라 교수 객체의 동작이 영향을 받을 수 있음을 의미한다.
의존 관계 예제 코드(cpp)
아래 예제는 Professor 클래스(A)가 Course 클래스(B)를 의존하는 관계를 보여줍니다. Professor 클래스는 teachCourse 메서드에서 Course 클래스를 사용한다.
#include <iostream>
#include <string>
class Course {
public:
Course(const std::string& name) : name(name) {}
std::string getName() const {
return name;
}
private:
std::string name;
};
class Professor {
public:
void teachCourse(const Course& course) {
std::cout << "Teaching course: " << course.getName() << std::endl;
}
};
int main() {
Course course("Mathematics");
Professor professor;
professor.teachCourse(course); // 의존 관계
return 0;
}
// 실행 결과 : Teaching course: Mathematics
의존 관계 UML 코드
의존 관계를 표현한 UML 다이어그램은 점선 화살표로 나타냅니다. 아래는 Professor 클래스(A)가 Course 클래스(B)를 의존하는 UML 다이어그램 예시이다.
```uml
class Course {
+getName() : string
}
class Professor {
+teachCourse(course : Course)
}
// UML 다이어그램에서 의존 관계는 점선 화살표로 표현된다.
Course <.. Professor
점선 화살표는 Professor 클래스가 Course 클래스를 일시적으로 사용함을 나타낸다.
의존 관계는 클래스 A(Professor)가 클래스 B(Course)를 사용하지만, 일시적이며 A 클래스가 B 클래스를 멤버 변수로 가지지 않는 관계를 의미한다. 이 관계는 클래스 B의 변화에 클래스 A가 영향을 받을 수 있음을 나타낸다.
연관(Association) 관계란?
- 교수는 학생을 멤버 변수로 가지고 있습니다.
- 이는 교수 객체가 학생 객체를 내부에 저장한다.
- 이를 지속적으로 참조한다는 의미입니다.
- 교수는 학생과 연관되어 있으며, 교수 객체는 학생 객체에 대해 알고 있다.
- 학생 객체와 지속적인 상호작용한다.
- 예를 들어, 교수는 학생의 정보를 관리하거나, 학생에게 과제를 부여할 수 있습니다.
- 예를 들어, 교수는 학생의 정보를 관리하거나, 학생에게 과제를 부여할 수 있습니다.
- 학생 객체와 지속적인 상호작용한다.
연관 관계는 두 클래스가 서로를 멤버 변수로 가지고 있으며, 지속적인 관계를 유지하는 것을 의미한다. 이는 교수 객체가 학생 객체의 변화를 지속적으로 반영할 수 있음을 나타냅니다.
연관 관계 예제 코드(cpp)
아래 예제는 Professor 클래스(A)가 Student 클래스(B)를 연관하는 관계를 보여줍니다. Professor 클래스는 Student 객체를 멤버 변수로 가지고 있으며, 이를 통해 지속적으로 상호작용한다.
#include <iostream>
#include <string>
#include <vector>
class Student {
public:
Student(const std::string& name) : name(name) {}
std::string getName() const {
return name;
}
private:
std::string name;
};
class Professor {
public:
void addStudent(const Student& student) {
students.push_back(student);
}
void listStudents() const {
std::cout << "Professor's students:" << std::endl;
for (const auto& student : students) {
std::cout << "- " << student.getName() << std::endl;
}
}
private:
std::vector<Student> students; // 교수는 학생과 연관됨
};
int main() {
Student student1("Alice");
Student student2("Bob");
Professor professor;
professor.addStudent(student1);
professor.addStudent(student2);
professor.listStudents(); // 연관 관계
return 0;
}
// 실행 결과 : Professor's students: Alice
// Bob
연관 관계 UML 코드
연관 관계를 표현한 UML 다이어그램은 실선 화살표로 나타냅니다. 아래는 Professor 클래스(A)가 Student 클래스(B)와 연관된 UML 다이어그램 예시이다.
```uml
class Student {
+getName() : string
}
class Professor {
+addStudent(student : Student)
+listStudents()
-students : vector<Student>
}
// UML 다이어그램에서 연관 관계는 실선 화살표로 표현된다.
Professor "1" o-- "*" Student
실선 화살표는 Professor 클래스가 Student 클래스를 멤버 변수로 가지며 지속적인 상호작용을 함을 나타낸다.
연관 관계는 클래스 A(Professor)가 클래스 B(Student)를 멤버 변수로 가지며, 지속적으로 상호작용하는 관계를 의미한다. 이는 교수 객체가 학생 객체의 변화를 지속적으로 반영할 수 있음을 나타낸다.
집합(Aggregation) 관계란?
- 학과는 교수를 멤버 변수로 가지고 있다.
- 이는 학과 객체가 교수 객체들을 내부에 저장한다.
- 이를 참조한다는 의미이다.
- 교수는 학과에 포함되지만, 교수 객체의 생명 주기는 학과 객체와 독립적이다.
- 즉, 학과 객체가 삭제되더라도 교수 객체는 별도로 존재할 수 있다.
- 즉, 학과 객체가 삭제되더라도 교수 객체는 별도로 존재할 수 있다.
집합 관계는 "전체-부분" 관계의 일종으로, 부분(교수)이 전체(학과)에 포함되지만, 전체가 없어져도 부분이 독립적으로 존재할 수 있습니다. 이는 교수 객체가 학과 객체에 포함되어 있으면서도 생명 주기가 독립적임을 의미합니다.
집합 관계 예제 코드(cpp)
아래 예제는 Department 클래스(A)가 Professor 클래스(B)를 집합하는 관계를 보여준다. Department 클래스는 Professor 객체를 멤버 변수로 가지고 있으며, 이를 통해 지속적으로 상호작용한다.
#include <iostream>
#include <string>
#include <vector>
class Professor {
public:
Professor(const std::string& name) : name(name) {}
std::string getName() const {
return name;
}
private:
std::string name;
};
class Department {
public:
void addProfessor(const Professor& professor) {
professors.push_back(professor);
}
void listProfessors() const {
std::cout << "Department's professors:" << std::endl;
for (const auto& professor : professors) {
std::cout << "- " << professor.getName() << std::endl;
}
}
private:
std::vector<Professor> professors; // 학과는 교수와 집합 관계
};
int main() {
Professor professor1("Dr. Smith");
Professor professor2("Dr. Johnson");
Department department;
department.addProfessor(professor1);
department.addProfessor(professor2);
department.listProfessors(); // 집합 관계
return 0;
}
// 실행 결과 : Department's professors: Dr. Smith
// Dr. Johnson
집합 관계 UML 코드
집합 관계를 표현한 UML 다이어그램은 빈 마름모로 나타낸다. 아래는 Department 클래스(A)가 Professor 클래스(B)와 집합 관계인 UML 다이어그램 예시이다.
```uml
class Professor {
+getName() : string
}
class Department {
+addProfessor(professor : Professor)
+listProfessors()
-professors : vector<Professor>
}
//UML 다이어그램에서 집합 관계는 빈 마름모 화살표로 표현된다.
Department "1" o-- "*" Professor
빈 마름모 화살표는 Department 클래스가 Professor 클래스를 멤버 변수로 가지며, 교수 객체의 생명 주기가 학과 객체와 독립적임을 나타낸다.
집합 관계는 클래스 A(Department)가 클래스 B(Professor)를 멤버 변수로 가지며, 교수 객체가 학과 객체에 포함되지만 생명 주기가 독립적인 관계를 의미한다. 이는 학과 객체가 삭제되더라도 교수 객체는 별도로 존재할 수 있음을 나타낸다.
합성(Composition) 관계란?
- 대학은 학과를 멤버 변수로 가지고 있다.
- 이는 대학 객체가 학과 객체들을 내부에 저장한다.
- 이를 참조한다는 의미입니다.
- 학과는 대학에 완전히 포함되며, 학과 객체의 생명 주기는 대학 객체에 의존한다.
- 즉, 대학 객체가 삭제되면 학과 객체도 함께 삭제됩니다.
- 즉, 대학 객체가 삭제되면 학과 객체도 함께 삭제됩니다.
합성 관계는 "강한 전체-부분" 관계로, 부분(학과)이 전체(대학)에 완전히 종속됩니다. 이는 학과 객체가 대학 객체에 포함되어 있으며, 대학 객체가 없어지면 학과 객체도 함께 사라진다는 것을 의미합니다.
합성 관계 예제 코드(cpp)
아래 예제는 University 클래스(A)가 Department 클래스(B)를 합성하는 관계를 보여준다. University 클래스는 Department 객체를 멤버 변수로 가지고 있으며, 이를 통해 지속적으로 상호작용한다.
#include <iostream>
#include <string>
#include <vector>
#include <memory>
class Department {
public:
Department(const std::string& name) : name(name) {}
std::string getName() const {
return name;
}
private:
std::string name;
};
class University {
public:
void addDepartment(const std::string& name) {
departments.push_back(std::make_unique<Department>(name));
}
void listDepartments() const {
std::cout << "University's departments:" << std::endl;
for (const auto& department : departments) {
std::cout << "- " << department->getName() << std::endl;
}
}
private:
std::vector<std::unique_ptr<Department>> departments; // 대학은 학과와 합성 관계
};
int main() {
University university;
university.addDepartment("Computer Science");
university.addDepartment("Mathematics");
university.listDepartments(); // 합성 관계
return 0;
}
// 실행 결과 : University's departments: Computer Science
// Mathematics
합성 관계 UML 코드
합성 관계를 표현한 UML 다이어그램은 채워진 마름모로 나타냅니다. 아래는 University 클래스(A)가 Department 클래스(B)와 합성 관계인 UML 다이어그램 예시이다.
```uml
class Department {
+getName() : string
}
class University {
+addDepartment(name : string)
+listDepartments()
-departments : vector<unique_ptr<Department>>
}
// UML 다이어그램에서 합성 관계는 채워진 마름모 화살표로 표현된다.
University "1" *-- "0..*" Department
채워진 마름모 화살표는 University 클래스가 Department 클래스를 멤버 변수로 가지며, 학과 객체의 생명 주기가 대학 객체에 종속됨을 나타낸다.
합성 관계는 클래스 A(University)가 클래스 B(Department)를 멤버 변수로 가지며, 학과 객체가 대학 객체에 포함되고, 대학 객체가 없어지면 학과 객체도 함께 사라지는 강한 전체-부분 관계를 의미한다.
구현(Realization) 관계란?
- 교수는 연구 주제를 멤버 변수로 가지고 있지 않다.
- 교수는 연구 주제 인터페이스를 구현하여 특정 행동을 정의한다.
- 이는 교수가 연구 주제에 선언된 메서드를 실제로 정의한다는 의미입니다.
- 연구 주제 인터페이스는 교수가 수행할 수 있는 행동을 규정하며, 교수는 이 인터페이스에 따라 행동을 구현한다.
구현 관계는 클래스(구현체)가 인터페이스에 정의된 메서드들을 실제로 구현하는 관계를 의미합니다. 이는 인터페이스가 어떤 메서드를 가져야 하는지 규정하고, 클래스가 그 메서드들을 실제로 구현하는 것을 나타냅니다.
구현 관계 예제 코드(cpp)
아래 예제는 Professor 클래스(A)가 ResearchTopic 인터페이스(B)를 구현하는 관계를 보여줍니다. Professor 클래스는 ResearchTopic 인터페이스에 정의된 메서드를 실제로 구현한다.
#include <iostream>
#include <string>
class ResearchTopic {
public:
virtual void conductResearch() const = 0; // 순수 가상 메서드
};
class Professor : public ResearchTopic {
public:
Professor(const std::string& name) : name(name) {}
void conductResearch() const override {
std::cout << name << " is conducting research on advanced algorithms." << std::endl;
}
private:
std::string name;
};
int main() {
Professor professor("Dr. Smith");
professor.conductResearch(); // 구현 관계
return 0;
}
// 실행 결과 : Dr. Smith is conducting research on advanced algorithms.
구현 관계 UML 코드
구현 관계를 표현한 UML 다이어그램은 점선 화살표로 나타낸다. 아래는 Professor 클래스(A)가 ResearchTopic 인터페이스(B)를 구현하는 UML 다이어그램 예시이다.
```uml
interface ResearchTopic {
+conductResearch() : void
}
class Professor {
+conductResearch() : void
-name : string
}
// UML 다이어그램에서 구현 관계는 점선 화살표로 표현된다.
ResearchTopic <|.. Professor
점선 화살표는 Professor 클래스가 ResearchTopic 인터페이스를 구현하며, 인터페이스에 정의된 메서드를 실제로 구현함을 나타낸다.
구현 관계는 클래스 A(Professor)가 인터페이스 B(ResearchTopic)에 정의된 메서드들을 실제로 구현하는 관계를 의미한다. 이는 인터페이스가 어떤 메서드를 가져야 하는지 규정하고, 클래스가 그 메서드들을 실제로 구현하는 것을 나타낸다.
상속 관계란?
- 동물은 개를 멤버 변수로 가지고 있지 않다.
- 개는 동물 클래스를 확장하여 동물의 모든 특성과 행동을 상속받는다.
- 이는 개가 동물의 행동과 특성을 물려받고, 개의 고유한 행동을 추가로 정의할 수 있다는 의미이다.
- 동물은 일반적인 행동과 특성을 정의하며, 개는 이 행동과 특성을 구체화하거나 확장한다.
상속 관계는 클래스가 다른 클래스의 특성과 행동을 상속받아 재사용하는 관계를 의미합니다. 이는 부모 클래스(상위 클래스)가 자식 클래스(하위 클래스)에 특성과 행동을 물려주고, 자식 클래스가 이를 확장하거나 재정의할 수 있음을 나타냅니다.
상속 관계 예제 코드(cpp)
아래 예제는 Animal 클래스(A)가 Dog 클래스(B)에 상속되는 관계를 보여줍니다. Dog 클래스는 Animal 클래스의 특성과 행동을 상속받아 확장한다.
#include <iostream>
#include <string>
class Animal {
public:
Animal(const std::string& name) : name(name) {}
void makeSound() const {
std::cout << name << " makes a sound." << std::endl;
}
protected:
std::string name;
};
class Dog : public Animal {
public:
Dog(const std::string& name) : Animal(name) {}
void makeSound() const {
std::cout << name << " barks." << std::endl;
}
};
int main() {
Animal animal("Generic Animal");
animal.makeSound(); // 상속 관계: 부모 클래스의 메서드 호출
Dog dog("Buddy");
dog.makeSound(); // 상속 관계: 자식 클래스의 메서드 호출 (재정의된 메서드)
return 0;
}
// 실행 결과 : Generic Animal makes a sound.
// Buddy barks.
상속 관계 UML 코드
상속 관계를 표현한 UML 다이어그램은 빈 삼각형 화살표로 나타낸다. 아래는 Animal 클래스(A)가 Dog 클래스(B)에 상속되는 UML 다이어그램 예시이다.
```uml
class Animal {
+makeSound() : void
-name : string
}
class Dog {
+makeSound() : void
}
// UML 다이어그램에서 상속 관계는 빈 삼각형 화살표로 표현된다.
Animal <|-- Dog
빈 삼각형 화살표는 Dog 클래스가 Animal 클래스를 상속받으며, 부모 클래스의 특성과 행동을 물려받음을 나타낸다.
상속 관계는 클래스 A(Animal)가 클래스 B(Dog)에 특성과 행동을 상속하는 관계를 의미한다. 이는 자식 클래스가 부모 클래스의 특성과 행동을 물려받아 재사용하고, 이를 확장하거나 재정의할 수 있음을 나타낸다.
결론
이러한 관계들을 이해함으로써 우리는 객체 간의 상호 작용을 더 잘 설계할 수 있으며, 더 유지 보수하기 쉽고 유연한 소프트웨어 시스템을 구축할 수 있을 것 같다. UML 다이어그램을 통해 이러한 관계를 시각적으로 표현하면 시스템의 구조를 이해하고, 다른 개발자들과 효율적으로 소통할 수 있을 것 같다.