[DesignPattern] 객체지향 모델링

객체지향 모델링



1. 모델링의 역할

(1) 서로의 해석을 공유해 합의를 이루거나 해석의 타당성을 검토한다.

(2) 현재 시스템 또는 앞으로 개발할 시스템의 원하는 모습을 가시화한다.

(3) 시스템의 구조와 행위를 명세할 수 있으며 시스템을 구축하는 틀을 제공한다.


2. UML 일반

(1) UML 정의

- 소프트웨어 청사진을 작성하는 표준언어

- 소프트웨어 중심 시스템의 산출물을 가시화하고, 명세화하고,구축하고, 문서화하는데 사용

- 가시화 언어 : UML은 소프트웨어의 개념모델을 가시적인 그래픽 형태로 작성하여 참여자들의 오류없고 원활한 의사소통이 이루어지게 하는 언어

- 명세화 언어 : UML은 소프트웨어 개발과정인, 분석, 설계, 구현 단계의 각 과정에서 필요한 모델을 정확하고 완전하게 명세할 수 있게하는 언어

- 구축 언어 :  UML 언어는 다양한 객체지향 프로그래밍 언어로 변환 가능 UML로 명세된 설계 모델은 구축하려는 프로그램 코드로 순변환하여 (순공학) 구축에 사용기 구축된 코드를 UML모델로 역변환(역공학) 하여 분석하게 할 수도 있다.

- 문서화 언어 :  UML은 여러 개발자들 간의 통제, 평가 및 의사소통에 필요한 문서화를 할 수 있는 언어


(2) UML 필요성

소프트웨어 시스템을 만들기 위해서 어휘와 규칙을 두어 시스템을 개념적/물리적으로 표현하는 모델이 필요

시스템의 구조적 문제와 프로젝트 팀내의 의사 소통, 소프트웨어 구조의 재사용 문제를 해결


(3) UML 구성요소

사물 : 추상적 개념으로써 모델 구성의 기본 요소 시스템의 구조, 행위를 표현하고 개념들을 그룹화 하는 것 부가적인 설명을 위한 것들이 있다.

관계 : 사물들 간의 연결 관계를 추상화 한 것

다이어그램 : 관련성이 있는 사물들 간의 상호관계를 도형 형태로 표현


3. 클래스 다이어그램

클래스 다이어그램은 시간에 따라 변하지 않는 시스템의 정적인 면을 보여주는 대표적인 UML 구조 다이어그램이다. 클래스 다이어그램은 시스템을 구성하는 클래스와 그들 사이의 관계를 보여준다. 주요 구성요소는 클래스와 관계다.

(1) 클래스


1) 구체적인 가시화

public class Cource{

private String id;

private String name;

private int numOfStudents = 0;

public void addStudents(Student student{}

public void deleteStudents(integer id){}

}


2) 접근제어자

- private : 이 클래스에서 생성된 객체들만 접근 가능

+ public : 어떤 클래스의 객체이든 접근 가능

# protected : 이 클래스와 동일 패키지에 있거나 상속 관계에 있는 하위 클래스의 객체들만 접근 가능

~ package : 동일 패키지에 있는 클래의 객체들만 접근 가능


3) 다중성 표시


4. 관계

클래스 하나로만 이루어지는 시스템은 존재하지 않는다. 한 사함이 모든 일을 처리할 때 보다 여러 사람이 모였을 때 일을 좀더 효과적으로 처리할 수 있듯이 다수의 클래스가 모인 시스템이 훨씬 효율적이다. 객체지향 시스템도 여러 개의 클래스가 서로 긴밀한 관계를 맺어 기능을 수행한다.


(1) 연관관계 

 

실선: 양방향 연관관계- 두 클래스 객체들이 서로의 존재를 인식한다는 의미이다.

화살표: 단방향 연관관계 - 한쪽은 다른 객체의 존재를 알지만, 다른쪽은 그 존재를 모른다.

클래스들이 개념상 서로 연결되었음을 나타낸다. 실선이나 화살표로 표시하며 보통은 한 클래스가 다른 클래스에서 제공하는 기능을 사용하는 상황일 때 표시한다.

연관관계의 역할 이름은 연관된 클래스 객체들이 서로를 참조할 수 있는 속성의 이름으로 활용할 수 있다. 일회성이 아닌 지속적으로 유지되는 구조적 관계이다.


예제1) 단방향 연관관계

public class Person {

private Phone homePhone;

Private Phone OfficePhone;

public Phone getHomePhone() {

return homePhone;

}


public void setHomePhone(Phone homePhone) {

this.homePhone = homePhone;

}


public Phone getPhone() {

return Phone;

}


public void setPhone(Private phone) {

Phone = phone;

}

}

person은 phone의 존재를 알지만, phone은 person의 존재를 모른다.

따라서 person 객체는 phone 객체들을 참조할 수 있도록 구성해야 하지만, phone 클래스는 person 객체를 참조할 속성이 존재하지 않아도 된다.


예제2) 양방향 연관관계

class Student{

private Professor advisor;


public void setAdvisor(Professor advisor) {

this.advisor = advisor;

}

public void advise(String msg){

System.out.println(msg);

}

}


public class Professor {

private Student student;

public void setStudent(Student student) {

this.student = student;

student.setAdvisor(this);

}

public void advise(){

student.advise("상담내용은 여기에");

}

public static void main(String[] args){

Professor gildong = new Professor();

Student bap = new Student();

gildong.setStudent(bap);

gildong.advise();

}

}

이 코드의 연관관계는 양방향 연관관계이므로 Professor  클래스 객체에서 Student 클래스 객체를 참조할 수 있는 속성(student)이 있고, Student 클래스 객체에서 Professor 클래스 객체를 참조할 수 있는 속성이 있다. 또한 이 속성의 이름이 역할 이름을 활용한 것임을 알 수 있다.


예제3) 다대다 연관관계

import java.util.ArrayList;


public class Student {

private String name;

private ArrayList<Course> courses;

public Student(String name){

this.name = name;

courses = new ArrayList<Course>();

}

public void registerCourse(Course course){

courses.add(course);

course.addStudent(this);

}

public void dropCourse(Course course){

if (courses.contains(course)){

courses.remove(course);

course.removeStudent(this);

}

}

public ArrayList<Course> getCourses(){

return courses;

}

}


class Course{

private String name;

private ArrayList<Student> students;

public Course(String name){

this.name = name;

students = new ArrayList<Student>();

}

public void addStudent(Student student){

students.add(student);

}

public void removeStudent(Student student){

students.remove(student);

}

public ArrayList<Student> getStudent(){

return students;

}

public String getName(){

return name;

}

}

이 코드는 Student 클래스와 Course 클래스의 연관관계가 양방향 연관 관계이기 때문에 양쪽 클래스에서 서로를 참조할 수 있는 속성을 정의했다.  Studnet 객체 하나에 하나 이상의 Course 객체가 연관 되어 있기 때문에 다중성을 구현했으며(반대도 같음), Student, Course 클래스에 대표적인 컬렉션 자료구조인 ArrayList를 이용해 여러 개의 Student, Course 클래스 객체를 참조할 수 있게 했다. 


(2) 일반화관계

한 클래스가 다른 클래스를 포함하는 상위 개념일 때 두 클래스 사이에는 일반화 관계가 존재한다. 일반화 관계가 존재할 때 자식이라 불리는 클래스는 부모라 불리는 클래스로부터 속성과 연산을 물려받을 수 있다.특수화, 일반화된 사물간의 관계를 표현한다. 이를 'is a kind of' 관계라고도 한다. 상위 클래스를 상속받는 하위 클래스에서는 각자의 특수한 스킬이나 액션에 따라서 다른 멤버변수나 메서드를 추가하여 구현을 하거나, 상속받은 추상 메서드를 오버라이드해서 구현하면 된다.

ex.) 세탁기 is kind of 가전제품

       프로그래머 is kind of 사람


예제)

public abstract class HomeAppliances {

private int serialNo;

private String manufacturer;

private int year;

public abstract void turnOn();

public abstract void turnOff();

}


public class Washer extends HomeAppliances{

public void turnOn(){

// 내용 구현

}

public void turnOff(){

// 내용 구현

}

}


(3) 집합관계

집합관계는 UML 연관 관계의 특별 경우로 전체와 부분의 관계를 명확하게 명시하고자 할 때 사용한다.

집약관계와 합성관계 두 종류의 집합관계가 존재한다.


1) 집약관계

한 객체가 다은 객체를 포함하는 것을 나타낸다. '전체', '부분'과의 관계이며 '전체'를 가리키는 클래스 방향에 빈 마름모 표시를 한다.

부분 객체를 여러 전체 객체가 공유할 수 있다. 이때 전체 객체의 라이프타임과 부분 객체의 라이프타임은 독립적이다.

즉, 전체 객체가 메모리에서 사라진다 해도 부분 객체는 사라지지 않는다.


public class Computer {

private Mainboard mb;

private CPU c;

private Memory m;

private PowerSupply ps;

public Computer(Mainboard mb, CPU c, Memory m, PowerSupply ps){

this.mb = mb;

this.c = c;

this.m = m;

this.ps = ps;

}

}

Computer 객체가 사라져도 부품을 구성하는 Mainboard 객체, CPU 객체, Memory 객체, PowerSupply 객체는 사라지지 않는다.

외부에서 이들 객체에 대한 참조만 받아 사용했기 때문이다. 즉, 전체를 표현하는 Computer 객체의 라이프타임과 부분 객체의 라이프타임은 무관하다.


2) 합성관계

합성관계는 전체를 가리키는 클래스 방향에 체워진 마름모로 표시되면 부분 객체가 전제 객체에 속하는 관계이다.

따라서 전체 객체가 사라지면 부분 객체도 사라지는 경우를 의미한다.  부분 객체를 여러 전체 객체가 공유할 수 없다.

이때 부분 객체의 라이프타임은 전체 객체의 라이프타임에 의존한다. 즉, 전체 객체가 없어지면 부분 객체도 없어진다.


public class Computer {

private Mainboard mb;

private CPU c;

private Memory m;

private PowerSupply ps;

public Computer(){

this.mb = new MainBoard();

this.c = new CPU();

this.m = new Memory();

this.ps = new PowerSupply();

}

}

이 코드에서 생성자가 컴퓨터의 부품이 되는 Coumputer 객체들을 생성해 적절한 속성에 바인딩한다는 점이다.

가령 c1 = new Computer()으로 Computer 객체가 생성되면 c1의 부품을 이루는 MainBoard 객체, CPU 객체, Memory 객체, PowerSupply객체가 생성된다.

이러한 부품 객체들은 Cumputer 클래스의 객체 c1이 사라지면 같이 사라진다. 즉, 부품 객체들의 라이프타임이 Computer 객체의 라이프타임에 의존하는 관계가 형성된다.


(4) 의존관계

클래스가 연관, 상속, 집합 관계로 엮여 있는 것은 아니지만, 한 객체가 다른객체를 소유하지는 않지만, 다른객체가 변경되면 그것을 사용하는 다른 곳도 같이 변경해줘야 하는 관계를 표현할 때 주로 사용한다. 단, 주의해야 할 점은 연관관계와 달리 의존관계의 경우에는 클래스 인스턴스의 레퍼런스를 유지하고 있지 않다는 점이다. 

레퍼런스를 계속적으로 유지하게 되면 이는 연관관계로 표현해야 한다.


주로 다음과 같은 세 가지 경우에 의존 관계로 표현한다.

1 한 클래스의 메소드가 다른 클래스의 객체를 인자로 받아 그 메소드를 사용한다.( 가장 일반적 ) 

2 한 클래스의 메소드가 또 다른 클래스의 객체를 반환한다.

3 다른 클래스의 메소드가 또 다른 클래스의 객체를 반환한다. 이때 이 메소드를 호출하여 반환되는 객체의 메소드를 사용한다.


public class Car{

...

public void fillGas(GasPump p){

p.getGas(amount);

...

}

}

자동차에 주유할 때 특정 주유소에 있는 특정 주유기만 고집해 매번 주유할 수는 없을 것이다. 이런 경우라면 주유 서비스를 받을 때 마다 이용하는 주유기가 매번 달라지는 것을 의미하며

객체지향 프로그램에서는 사용되는 주유기를 인자난 지역 객체로 생성해 구현할 것이다.


cf.) 의존관계와 연관관계

- 의존관계: 클래스의 인스턴스의 레퍼런스를 유지하지 않음

- 집합관계: 클래스의 인스턴스의 레퍼런스를 유지함


한 클래스의 객체를 다른 클래스 객체의 속성에서 참조하는 경우에는 참조하는 객체가 변경되지 않는 한 두 클래스의 객체들이 오랜 기간 동안 협력 관계를 통해 기능을 수행한다고 볼 수 있다.

예를 들면, 자동차(Car 클래스)를 소유한 사람(Person 클래스)이 자동차를 이용해 출근한다고 할 경우 다음 날 출근할 때도 어제 사용한 자동차를 타고 출근할 것이다.

매번 출근할 때마다 다른 자동차를 사용하는 경우는 거의 없을 것이다. 이런 경우 사람과 자동차의 관계가 연관 관계이며 Person 클래스의 속성으로 Car 객체를 참조한다.


public class Person{

private Car owns; // 이 속성으로 연관 관계가 설정된다.

public void setCar(Car car){

this.owns = car;

}


public Car getCar(){

return this.owns;

}

}

--> 연관 관계는 오랜 시간 동안 같이할 객체와의 관계며 의존 관계는 짧은 기간 동안 이용하는 관계다.


(5) 실체화 관계

- 인터페이스를 구현하는 관계를 표현한다.

- 상속의 개념과 비슷하지만 상속과 다른점은, 

-> 상속 : 직접 상위 클래스를 상속받아서 Unit 클래스의 기능을 포함한다.(멤버변수 및 메소드 모두 상속됨)

-> 인터페이스 : 서로 다른 클래스라도 인터페이스만 준수하면(인터페이스의 함수들을 모두 구현하면) 동일한 기능들이 구현될수가 있다. (메소드 같은 기능들만 구현이 됨)

즉, 완전히 다른 클래스에 공통적인 기능(메서드)을 부여하는 것이다.

Building이라는 것에서 Barraks,Factory,Bunker가 상속되어서  멤버 변수인 Health와 Ammor의 데이터와 Contruct(건물짓기), UnderAttack(공격받기) 기능이 부여가 됩니다. 여기에서 테란건물의특수기능인 건물을 상공으로 띄워서 이동하고 다시 땅으로 착지하는 기능을 추가하려고 했을때, Building의 상위 클래스에 메서드를 추가하게 되면 Bunker라는 건물은 이동이 원래 불가능한데 그 기능을 갖게 되서 문제가 발생한다. ( 단순히 Overide해서 아무 기능이 없게 해도 되지만 상속받는 모든것에 그렇게 처리를 하는것도 효율적이지 못한거 같다. ) 이때 인터페이스를 구현해서 이동이 가능한 건물에만 인터페이스를 구현하게 되면 된다. 인터페이스를 구현하는 곳에서는 Move,Land,Fly 메서드를 반드시 구현해야 한다.