컴퓨터(Computer Science)/소프트웨어공학(Software engineering)

프로젝트 계획(12) 상세 설계 - 디자인 패턴 [소프트웨어공학]

게임이 더 좋아 2020. 5. 11. 22:10
반응형
728x170

소프트웨어 설계이 이유는 소프트웨어 솔루션을 위한 문제 해결과 계획 과정이라고 할 수 있다.

 

하지만 큰 그림만 그려서는 완성이 안된다.

 

이제 디테일, 세부사항에 대해 들어가보자

 


아키텍쳐 설계 단계에서는 추상 수준은 높다 그렇지만 모듈 추상 수준은 조금 낮다.

 

상세 설계에서는 추상 갭을 메우기 위하여 클래스 인터페이스와 내부에 대한 설계, 영구적 데이터에 대한 설계 작업이 필요하다.

 

그런데 위에 제목에서 말했듯이 디자인 패턴, 즉 어떤 패러다임을 사용하느냐에 따라 상세 설계가 많이 달라진다

 

++ 우리는 2가지를 배웠다. 구조적 방법, 객체지향 방법

 

1.구조적 방법
프로시저 안의 로직 (알고리즘) 설계

 

2. 객체지향 방법
클래스 안의 메소드 설계

 

// 이미 글을 처음부터 봤으면 다 알 것이다.

 

 

 

 

 

소프트웨어 시스템에서 아키텍쳐를 설계하는 작업은 서브 시스템이나 패키지 수준의 개념적 모델링이다.

 

서브 시스템이나 패키지 내부에는 여러 관련된 클래스가 모여있고 이들 사이의 관계가 설정된다.

 

클래스 단위로 내려가면 시스템의 구체적인 컴포넌트를 설계하는 작업이다.

//인터페이스 정의 속성, 메소드 내부의 알고리즘 결정

 

분석 단계에서 파악한 애플리케이션 클래스만으로는 시스템을 구축할 수 없고 선택한 플랫폼 사이에 있는 차이를 메워주는 클래스들이 필요하다. // 이를 상세 설계, 객체 설계라고 한다.

 

 

 

밑에 그림을 보면 더 잘 이해할 수 있다.

 

 

 

 

위에서 말한 객체 설계 작업은 영화의 편집 작업과 비슷하다.

다시 말하자면, 후처리 기술인 것이다.

우선 대강 다 만들어놓고 배치나 효과, 시간의 연속성 등 필요한 모든 클래스를 추가하는 작업이다.

 

** 설계원리와 디자인 패턴이 필요한 이유다.

 

++디자인 패턴도 비슷하다. 처음부터 존재했던게 아닌 경험적으로 그런 것들이 축적되어서 굳어진 것들이다.

 

 


 

그래서 디자인 패턴이 정확히 뭐냐고??

 

디자인 패턴이란 소프트웨어 설계에서 자주 발생하는 문제에 대한 일반적이고 반복적인 해결책을 말한다.

//여러가지 상황에 적용될 수 있는 일종의 템플릿같은 거?

 

??? 너무 두루뭉술한데?

 

가다 보면 이해가 되어있을거니까. 

 

 

그럼 디자인 패턴의 구성부터 알아보자

 

디자인 패턴 구성 요소는 크게 6가지가 있다.


1. 패턴의 이름과 구분
2. 문제 및 배경 – 패턴이 사용되는 분야 또는 배경
3. 솔루션 – 패턴을 이루는 요소들, 관계, 협동과정
4. 사례 – 적용 사례
5. 결과 – 패턴의 이점, 영향
6. 샘플 코드 – 예제 코드

 

그리고 패턴은 세가지 유형으로 23가지가 있는데

** 생성유형 : 객체를 생성하는 패턴

** 구조적 : 클래스와 객체가 어떤 식으로 조합되었는지를 나타내는 패턴

** 행위적 : 객체의 상호작용과 의무의 분산을 나타냄

 

이러한 디자인 패턴을 이용하기 위해 순서가 있다.

//꼭 그런 것은 아니다.

 

1. 적절한 객체를 찾는다

2. 추상적 개념을 지닌 부분을 찾아 이를 표현할 객체를 파악한다.

3. 객체의 규모를 정한다

4. 객체의 인터페이스를 작성하되 유사 클래스를 가진 클래스를 지정하고 제한 조건을 설정한다

5. 클래스 상속을 할 것인지, 인터페이스 상속을 이용할 지 구별하고 재사용 캐머니즘도 선택한다.

 

 

디자인 패턴에 대해 살짝 알아봤는데

 

이제 각 패턴에 대해서도 더 알아보자

 


팩토리 메소드 패턴

 

이 패턴은 객체 생성을 위한 인터페이스의 정의 이다.

 

위임(delegation) 형태를 가지는데

즉 어떤 클래스의 인스턴스를 생성할지에 대한 결정은 서브클래스에서 이루어지도록 책임을 미루는 데 있다. 

 

 

그림을 보고 이해해보자

 

RequiredClass의 생성을 팩토리 메소드를 가진 MyClass에게 위임

 

팩토리 메소드는 createObjectOfrequiredClass()

 

사용하는 이유는 베이스 클래스에 속하는 객체 중 하나가 필요한데 그 아래에 있는 자식객체 중 어떤 것이 필요한지 실행 시간까지 알 수 없을 때

 

// 그니까 뭐가 필요한지 모르니까 너가 알아서 달란 말?? 이라고 알아들으면 되겠다.

정확한 비유인지는 모르겠다..

 

맛만 보자..

 


추상팩토리 패턴

 

추상팩토리 패턴이란 클래스의 집합의 종류를 지정하여 관련된 객체의 집합을 생성할 수 있게 하는 패턴이다.

// 위의 팩토리 메소드는 생성자와는 별도의 객체 생성 메소드를 두어 개별적인 파생 클래스를 추적하지 않고 기본 클래스를 불러 파생된 클래스를 생성할 수 있게 하는 패턴이었다.

 

** 이러한 패턴은 여러 하드웨어의 호환성을 위해 쓰이기도 한다.

 

++ LightBulb, Blind 같이 AbstractProducts라 불리는 일반적 개념의 객체가 있다.

++ TheftApplication은 AbstractFactory와 AbstractProducts에 의하여 제공되는 인터페이스, createBulb(), createBlind()만을 접근한다.

 


그 다음은 어댑터 패턴이다.

 

어댑터 패턴이란 이미 개발된 클래스, 즉 레거시 시스템의 인터페이스를 다른 클래스의 요구에 맞게 인터페이스를 변환해주는 것이다.

 

** 어댑터는 우리 실생활에서와 마찬가지로 서로 호환성이 없는 인터페이스들끼리 같이 작동할 수 있게 도와주는 변환기다. // 비유하자면 USB-C type 젠더도 호환성이 없는 단자를 같이 작동할 수 있게 하는 것과 비슷하다.

 

**이미 만들어져 있는 클래스를 사용하고 싶지만 인터페이스가 원하는 방식과 일치하지 않을 때 사용

**또는 관련성이 없거나 예측하지 못한 클래스들과 협동하는 재사용 가능한 클래스를 생성하기 위해서 사용한다.

 

어댑터 패턴 적용 방법은 2가지가 있다.

 
1. 위임을 이용
2. 상속을 이용

 

위의 예시는 상속을 활용한 어댑터 패턴이다.

 

조정이 필요한 클래스(adaptee)

 

개발 중인 클래스(target)

 

Request() 호출 시 이미 개발된 클래스의 인터페이스 형태로 호출하도록 만들면 된다. [SpecificRequest()]

 


다음에는 싱글톤 패턴이다.

 

대부분 클래스는 런타임에 다수의 인스턴스를 생성하게 되는데

어떤 경우는 인스턴스가 하나만 가져야하는 순간이 있다

 

Ex) 많은 프린터가 연결되어있어도 프린터 스풀러는 오직 하나여야만 한다. 

 

** 시스템에서 단 하나의 인스턴스만을 갖도록 할 필요가 있는 경우 사용하는 패턴이다.

 

그러한 객체들이 시스템에서 심심치 않게 등장해서 이러한 패턴이 필요한 것이다.

 

 

싱글 톤 패턴 사용 요령(지키지 않아도 뭐라하진 않지만 지키는게 좋음) 

 

1. 클래스의 유일한 인스턴스를 넣을 위치를 정한다.
2. 생성자의 접근 수정함수를 private로 지정 // 싱글톤을 얻기 위한 클래스 S에 public static 접근 메소드를 포함한다.
3. 싱글톤을 얻는 방법은 생성자에게 위임

 

 

더 설명하고 싶지만 맛만 보기로 했으니 여기서 그만 ㅎㅎ

 


다음에는 컴포지트 패턴이라고 있다. 

영어로는 Composite 

 

왜 사용하느냐??

 

객체 집합 속에 또 다시 객체 집합을 갖는 경우 패턴 사용한다.

그런데 그런 경우가 많다고 한다. 그래서 패턴이 만들어졌겠지?

 

컴포지트 패턴은 집합 속에 포함될 객체집합을 가지고 있는 객체, 이들 모두가 자기 자신과 동일한 타입(메소드와 데이터)의 객체 리스트를 가질 수 있도록 도와준다.

 

** 이 패턴을 이용하면 기본 클래스와 이를 포함하는 컨테이너 클래스를 구분하지 않고 처리하는 재귀적 합성을 이용할 수 있다.

 

그림을 보면 재귀적 포함관계를 보여준다. 

 

 

composite 클래스는 component클래스를 포함하지만

 

composite 클래스의 인스턴스는 component 클래스를 상속받은 leaf 클래스의 인스턴스를 포함하거나 

component 클래스를 상속받은 composite 클래스의 인스턴스를 포함할 수도 있다.

 

설명은 여기까지.. 맛 알겠지?

 

 


다음은 반복자 패턴이다.

영어로는 (iterator)

 

자료를 처리하는 시스템은 유사한 객체들의 집단을 다루는 일이 많은데

객체들의 집단을 다루다 보면 집단 속에 있는 객체들을 하나씩 사용하거나 특정 객체를 찾아 처리해야하는 경우가 다반사인데 그래서 나온 패턴이다.

 

이 패턴은 시스템 속에 존재하는 여러 객체들의 집단을 모두 동일한 인터페이스를 이용하여 접근할 수 있도록 만들어 준다.

 

**처리하려는 자료구조가 다른 형태로 바뀌더라도 클라이언트가 영향을 받지 않음// 그래서 쓰는 거지 

 

 

 

자료의 자세한 구조를 모르더라도 반복자가 제공하는 인터페이스를 이용하면 클라이언트도 접근할 수 있다.

 

여기까지

 

 


그리고 그 다음에는 옵서버 패턴

왜 옵서버라고 읽는지는 모르지만 (Observer)  //스타크래프트에서 옵저버라 그러죠?? ㅋㅋㅋ

뭐 영어니까

 

그래서 옵서버 패턴은 1대 다의 객체 의존관계를 정의한 것으로 

한 객체가 상태를 변화시켰을 때

의존 관계에 있는 다른 객체들에게 자동적으로 통지하고 변경시키는 기능을 한다.

 

++ 그것을 위해서 객체 하나를 변경하였을 때, 다른 객체에 통보하여 갱신하기 위한 옵저버 객체를 둔다.

 

 

 

 

Subject는 Observer를 알고 있고 Observer 객체를 붙이고 뗄 수 있는 인터페이스를 제공한다

Observer 는 Subject 객체의 변경을 통지 받아야 하는 객체를 위한 인터페이스를 제공한다.

 

** Sequence다이어그램이 Observer패턴을 보기에 좋다.

 

ConcereteSubject가 Observer의 상태와 일치하지 않을 때마다 Observer에게 통지하고

통지된 후 ConcereteObserver가 상태를 일치시키기 위하여 Subject에게 정보를 요청한다.

 

 

여기까지

 


이제 거의 끝나간다

 

상태 패턴이란 것이 있다.

 

상태패턴은 이벤트 의존 애플리케이션에 적합한 패턴이다. 

 

++임베디드 시스템을 포함한 많은 애플리케이션은 이벤트 구동 방식이다.

 

설계할 때는 시스템이 동작하면서 발생되는 이벤트에 따라 변경이 이루어 질 수 있도록 설계한다.

 

롤플레잉 게임 예제를 살짝 보자..

왜 게임이냐고?? 게임이 대표적으로 이벤트 구동 방식이거든ㅎㅎ

 

MyGameState 타입의 state를 가지고 있다.
State 는 다형성이 적용되어 MyGameState 클래스의 하위 클래스에 속하는 모든 객체들이다.

 

**상태 패턴을 사용하지 않는다면 상태는 단순히 내부 데이터 값으로 나타내게 되는데

상태의 전이가 변수 값의 대입으로 구현되기 때문에 코드에 명시적으로 나타나지 않는다

 

**상대 패턴을 사용하면 상태와 각 상태 전이를 보다 명시적으로 나타나게 된다.

 

 

이것도 그만 

 


마지막 패턴 바로 퍼싸드 패턴

ㅋㅋㅋ 영어로는 facade이다.

(근데 프랑스말로 하면 퍼싸드가 된다고 한다.)

 

왜 사용하냐면??

서브시스템의 내부가 복잡하여 클라이언트 코드가 사용하기 힘들 때 사용

 

서브시스템을 사용하는 입장에서는 간단한 인터페이스만 알아도 주요기능을 사용하게 만들어주는 것이 바로 퍼싸드

즉,복잡한 것을 단순하게 보여주고 내부 시스템을 몰라도 사용 가능하게 해주는 정말 좋은 패턴이다.

 

퍼싸드 패턴은 클라이언트 프로그램이 사용하려는 패키지 안에 포함된 여러 개의 클래스 중에 특정 클래스만을 인터페이스로 내세우는 것이다.

 

++그래서 퍼싸드로 선언되지 않은 클래스들은 패키지의 외부에 보이지 않는다.

 

 

 

 

패턴은 여기까지.. 다음엔 클래스 설계를 보자

 

 

 

 

 

반응형
그리드형