본문 바로가기

소프트웨어공학

6. 설계 원리

설계

  • 거시적 : architecture 설계 - component(module) + 관계
  • 미시적 : module, UI, DB, ... 설계

- 설계의 목표 : 효율성, 단순성(for 유지보수)

- 모듈화 : 

  • 추상화 : 자료(data)/기능(method)/제어(method) 추상화(class) : 관련된 정보에 집중하고나머지 정보는 무시하는 관점
  • 결합도(coupling) : 모듈간 상호의존 정도
    • data coupling
    • stamp coupling
    • control coupling
    • common coupling
    • content coupling
  • 응집도(cohesion) : 해당 모듈의 단일성 정도
    • Functional cohesion
    • sequential cohesion
    • communicational cohesion
    • procedural/temporal/Logical/...

 

 

용어

  • 모듈 : 정적인 구현단위
  • 컴포넌트 : 런타임에 독립적으로 배포되고 실행되는 단위

컴포넌트에 대한 그림

  • 서브시스템 : 시스템의 복잡도를 줄이기 위해 분할한 것

설계란?

"How ?" ‘어떻게 실현할 것인가’를 구체적으로 결정하는 활동

 

1) 기본구조설계(architecture) : 아키텍처 설계로 각 모듈의 역할과 인터페이스를 정의

2) 상세 설계(미시적) : 모듈 내부의 알고리즘, 데이터를 명세화 [정규화(normalisation)]

 

설계의 목표 : 효율성, 단순성(for 유지보수) - dirty하지 않게, Simplicity(체계, 정리)

즉 cohesion이 높게, coupling이 낮게

 

추상화 : 관련된 정보만 집중하고 나머지 정보는 무시하는 관점

캡슐화 : 추상화된 대상이 제공하는 서비스를 쉽게 접근하는 개념

모듈화 : 소프트웨어의 구성요소가 될 만한 수준으로 분할하는 과정

 

Coupling은 낮게, Cohesion은 높게 설계해야함

 

객체지향 설계 원리

1. 단일책임의 원리

2. 개방 폐쇄의 원리

3. 리스코프 교체의 원리

4. 인터페이스 분리의 원리

5. 의존관계 역전의 원리

 

1. 단일 책임의 원리

클래스의 역할과 책임을 단일화 해서 클래스를 변경할 이유를 하나로 제한

 

2. 개방 폐쇄의 원리(OCP)

소프트웨어 개체가 확장을 위해서는 열려있어야 하지만 수정을 위해서는 닫혀야함

 

3. 리스코프 교체의 원리

...

    @override
    pulic void setWidth(double width) {
        this.width = width;
        this.height = width;
    }
    
    @override
    pulic void setHeight(double height) {
    	this.width = height;
        this.height = height;
   	}
    	
...

Rectangle rectangle = new Square(); //정사각형
rectangle.setHeight(5);
rectangle.setWidth(4);

여기서 정사각형의 height와 width를 모두 받을 필요가 없음.

그냥 정사각형의 클래스를 새로 만들어서 변의 길이만 받는 함수를 받는 것이 좋음.

 

- 리스코프 교체의 원리 :

클래스 B가 클래스 A에서 상속받은 하위 유형이라면 프로그램의 동작을 방해하지 않고 A를 B로 대체

하위클래스는 클라이언트의 관점에서 기능을 손상시키지 않는 방식으로 상위 클래스 메소드를 대체

> 하위클래스는 상위클래스의 규약을 엄격하게 지켜야함

 

위 예시에서 정사각형이 굳이 Rectangle을 상속받아 height와 width를 모두 받을 필요가 없음.

그냥 Square 클래스를 새로 만들어서 변의 길이만 받는 함수를 받는 것이 좋음.

 

4. 인터페이스 분리의 원리

클라이언트가 사용하지 않는 인터페이스를 강제로 구현해서는 안됨

 People에 fly메소드는 필요가 없다. 또 Bird에는 work라는 메소드가 필요가없다.

그래서 오른쪽과 같이 <<interface>>를 나누어주는것이 좋다.

왼쪽과 같은 <<interface>>구조를 fatinterface라고 한다.

 

5. 의존 관계 역전의 원리(DIP)

class Character {
	final String NAME;
    int health;
    OneHanSword weapon;
    
    Character(String name, int health, OneHandSword weapon) {
    	this.NAME = name;
        this.health = health;
        this.weapon = weapon;
	}
	
    int attack() {
    	return weapon.attack();
	}
    
    void ChageWeapon(OneHandSword weapon) {
    	this.weapon = weapon;
	}
    
    void getinfo() {
    	System.out.println("이름: " + NAME);
        System.out.println("체력 " + health);
        System.out.println("무기: " + weapon);
	}
}

캐릭터가 사용하고자 하는 의존하는 클래스는 OneHandSword, TwoHandSword, WarHammer, BatteAxe가 있을 때,

무기가 많아지게 될 때 함수를 여러개 만들어야 함. (ex ChangeWeapon, Character)

구체화된 클래스에 의존하는 클래스를 만들 때는, 구체화된 클래스에 의존하지 말고 구체화된 클래스를 추상화해서 추상화된 클래스에 의존하게 해라 - 의존 관계 역전의 원리

 

interface Weaponable {
	int attack();
}

class OneHandSword implements Weaponable {
	...
    
    public int attakc() {
    	return DAMAGEl
    }
    
    ...
}

class TwoHandSword implements Weaponable {
	...
    
    public int attakc() {
    	return DAMAGEl
    }
    
    ...
}

	// ...

class Character {
	final String NAME;
    int health;
    OneHanSword weapon;
    
    Character(String name, int health, Weaponable weapon) {
    	this.NAME = name;
        this.health = health;
        this.weapon = weapon;
	}
	
    int attack() {
    	return weapon.attack();
	}
    
    void ChageWeapon(Weaponable weapon) {
    	this.weapon = weapon;
	}
    
    void getinfo() {
    	System.out.println("이름: " + NAME);
        System.out.println("체력 " + health);
        System.out.println("무기: " + weapon);
	}
}

위 코드처럼 변형해야 한다.

'소프트웨어공학' 카테고리의 다른 글

10. 테스트  (2) 2023.06.05
7. 아키텍처와 패턴  (1) 2023.05.15
5. 모델링 - 객체지향모델링  (0) 2023.04.10
3. 프로젝트 관리와 계획  (1) 2023.03.20
2.1 프로세스와 방법론 복습, 문제풀이  (3) 2023.03.20