개발 이야기 안하는 개발자

전문가를 위한 C++20 _ 5 (C++ 소프트웨어 공학) 본문

Book/전문가를 위한 C++20

전문가를 위한 C++20 _ 5 (C++ 소프트웨어 공학)

07e 2024. 3. 24. 16:00
반응형

 

28. 소프트웨어 공학

 

초기 아이디어부터 최종 제품에 이르기까지 단계적으로 진행하도록 프로세스를 정의해서 무질서한 소프트웨어 개발을 체계화하는 소프트웨어 라이프사이클 (소프트웨어 수명 주기)모델도 다양하게 나와있다.

 

폭포수 모델

계획 , 설계 , 구현 , 단위 시험, 서브시스템 시험, 통합 시험, 평가 순서대로 진행되는 라이프 사이클이다.

간결하기 때문에 프로젝트를 계획하면 관리하기 쉽다.

하지만 현실은 개발과정을 수행하는 동안에도 요구사항이 새로 추가되는 경우가 많기 때문에 이는 이행하기 어려운 모델이다.

 

사시미 모델.

단계는 폭포수와 동일하지만 앞뒤가 단계적으로 부분적으로 겹치는 점이 다르다.

바로 앞 뒤 단계를 이행 하기도 하고 병행하기도 한다.

 

나선형 모델

현재 단계에 문제가 있떠라도 다음 단계에서 해결하면 된다는 생각에 바탕을 두고 있다.

반복 프로세스를 적용하기 때문에 폭포수의 단점을 해결했다.

반복주기를 최소화 하기 힘들다는 단점이 있다.

이 반복주기가 너무 큰 경우 폭포수랑 크게 다를게 없어지기 때문에 생기는 문제도 많다.

 

애자일 방법론

고객의 변경사항을 프로젝트 개발 과정에 쉽게 반영하도록 프로세스를 유연하게 구성해야 한다.

 

UP

통합 프로세스(Unified process의 줄인말)는 인터랙티브하고 점진적인 방식의 소프트웨어 개발 프로세스다.

제품 설계 및 사례 작성(착수) -> 요구사항 분석과 시스템 구초 설계 (상술) -> 요구사항 구현(구축) -> 제품 전달(이행)

순서로 진행된다.

상술, 구축, 이행 단계는 일정한 시간을 가진 반복 주기로 나뉜다.

각 단계마다 결과물이 나와야 한다.

각 작업은 각각의 작업이 서로 중첩되어 반복적으로 수행된다.

 

RUP

래셔널 통합 프로세스(Rational Unified Process)는 up을 개선한 반법론이다.

RUP는 이론에 그치지않고 IBM 산하의 래셔널 소프트웨어에서 실제로 만든 소프트웨어 제품이다.

RUP의 핵심 원칙은 모델을 정확하게 정의하는 것이다.

RUP는 프로세스를 구성하는 각 부분을 하나의 워크플로로 정의한다.(Up의 활동 영역)

RUP는 대규모 조직이 주요 대상이며 기존 라이플 사이클모델보다 나은점이 많은데, 익히면 공통 플랫폼을 통해 설계, 의사소통, 그리고 구현을 수행할 때 편하다. 팀에 맞게 커스터마이즈가 가능하며 각 단계마다 결과 문서가 나온다.

 

스크럼

애자일 모델은 추상적인 원칙일 뿐 주어진 모델을 현실에 맞게 구현하는 방법은 구체적으로 제시하지 않는다.

스크럼은 애자일 방법론 중 하나로서 매일 수행할 작업 방식을 정확히 표현한다.

스크럼에서 반복 주기를 스프린트 사이클이라 부른다.

PO가 고객과 다른 사람을 연결해서 사용자 스토리를 작성한다. 각 사용자 스토리마다 우선순위를 정해서 백로그에 추가한다.

SM(스크럼 마스터)는 프로세스의 운영을 담당한다. 이 SM은 스크럼 프로세스가 제대로 진행되도록 관리한다.

팀은 소프트웨어 개발을 담당하고 10명 이내의 작은 규모를 유지해야 한다.

스크럼 프로세스는 데일리 스크럼 또는 스탠드업이라 부르는 미팅을 매일 가진다. 매일 지난 데일리 이후 한 일, 오늘 할 일, 목표 달성에 발생한 문제를 공유한다. 

작업은 할일, 진행중, 완료로 구성된 세 컬럼을 유연하게 설정해서 작업 단계를 공유한다.

스프린트 주기가 끝나면 스프린트 리뷰를 진행한다.

스크럼은 예상하지 못한일을 대처하는데 굉장히 좋은 방법론이다.

조직에 따라업무를 담당해야 할 사람을 팀 내부에서 결정하기 힘들수 있다.

 

XP

익스트림 프로그래밍의 줄인말로 개발에 관련된 길잡이 중에서 가장 뛰어난 것들을 모은 내용에 새로운 원칙을 추가한 것으로 12가지 핵심 원칙을 네 범주로 나눠서 정의하고 있다.

구체적인 피드백

- 페어프로그래밍

- 진행하면서 계획하기

- 지속적인 테스트 (단위 테스트, 테스트 주도 개발(TDD))

- 고객과 함께 진행

지속적인 프로세스

- 지속적인 통합

- 수시로 리펙터링하기

- 조금씩 릴리스하기

눈높이 맞추기

- 코드 작성 규칙

- 코드 공유

- 간결한 설계

- 공통 메타포

프로그래머 복지

- 최적작업 시간 (주 40시간)

 

소프트웨어 트리아지

프로젝트가 이미 상당히 망가진 상태에서는 리소스가 부족하다는 것에서 나온 개념.

남은 리소스로 필수, 권장, 옵션 항목으로 분료해서 작업을 진행해야 한다.

 

 

 



 

 

 

29. 효율적인 C++ 코드 작성

 

C++ 컴파일러는 C++에서 제공하는 여러가지 하이레벨 구문을 최댛나 기계어 코드에 가깝게 최적화한다. 최근에는 C 컴파일러보다 C++컴파일러의 최적화에 대한 연구가 훨씬 많다. 그러므로 C보다 C++코드의 최적화가 더 잘되어서 실행속도가 빠른 경우가 많다.

C#이나 자바와 같은 다른 하이레벨 객체지향언어는 가상머신에서 구동하는 반면, C++는 바로 CPU에서 곧바로 구동된다. 코드를 실행하는데 가상머신같은 중간 단계를 거치지 않기 때문에 거의 하드웨어 수준으로 실행된다.

 

임시 객체 생성 피하기

컴파일러는 다양한 상황에서 이름없는 임시 객체를 생성한다. 

class TestClass
...
	TestClass operator+(const TestClass& lhs, const TestClass& rhs);

TestClass myA { 4 } , myB;
B = myA + 5.7;
B = myA + 4;

위 코드에서 보면 5.7 이란 값을 오퍼레이터에 맞게 하기 위해선 TestClass 객체로 형변환을 진행하게 된다.

자동으로 형변환이 되면서 (TestClass(5.7)) 임시 객체가 생성되게 된다.

임시객체가 자동으로 생성되고 소멸되는건 리소스가 적지 않게 들어가기 때문에 연산자 오버로드를 추가 작성하거나 로직을 수정하는것이 좋다.

 

아래 코드에선 3개의 copy가 나올것으로 예상된다.

1. TestClass t1 = Copy(myTest)에서 Copy 메소드를 호출할때 Copy 메소드의 myB가 myTest 클래스를 복사한다.

2. return할때 TestClass(myB)에서 myB를 복사하는 새로운 객체를 복사해서 반환한다.

3. t1이 Copy(myTest)를 복사해서 가져간다.

 

하지만 실제론 RVO 때문에 2번의 copy만 출력된다.

컴파일 단계에서 TestClass t1 = Copy(myTest)를 TestClass t1(Copy(myTest))로 인식하고, 

TestClass t1(TestClass(myTest)) 로 변경하기 때문이다.

class TestClass
{
public:
	TestClass() { cout << "Gen" << endl; }
	~TestClass() { cout << "era" << endl; }
	TestClass(const TestClass& myM) { cout << "copy" << endl; }

};

TestClass Copy(TestClass myB)
{
	cout << "change Some" << endl;
	return TestClass(myB);
}

int main()
{
	TestClass myTest;
	TestClass t1 = Copy(myTest);


	cout << "Hello" << endl;
}

 

 

RVO도 좋지만 애초에 임시객체를 적게 생성하는것이 좋다.

모든 상황에서 가능한 것은 아니지만 레퍼런스와 포인터를 활용해서 임시객체 생성을 최소화 하는것이 좋다.

아래 코드는 복사가 한번만 이루어 진다. (TestClass t1 이 복사받아짐)

class TestClass
{
public:
	TestClass() { cout << "Gen" << endl; }
	~TestClass() { cout << "era" << endl; }
	TestClass(const TestClass& myM) { cout << "copy" << endl; }

};

const TestClass& Copy(const TestClass& myB)
{
	cout << "change Some" << endl;
	return myB;
}

int main()
{
	TestClass myTest;
	TestClass t1 = Copy(myTest);


	cout << "Hello" << endl;
}

 

 

 

 



 

 

 

33. 디자인 패턴

 

 

팩토리 패턴

객체를 생성할 때 그 객체의 생성자를 직접 호출하지 않고, 객체 생성을 담당하는 팩토리에 요청을 한다.

 

팩토리 메서드 패턴

팩토리 패턴과 비슷하지만, 팩토리 패턴은 추상적으로 요청을 하는 방식이지만, 팩토리 메서드 패턴에선 구체적으로 만들 타입을 결정한다.

 

어댑터 패턴

간혹 클래스로 정의한 추상화가 현재 설계와 맞지 않지만 변경할 수 없을 때가 있다. 어댑터는 어떤 기능의 구현 코드에 대한 바람직한 추상화를 제공하면서 그 기능의 사용자와 구현 코드를 연결해주는 역할을 한다.

 

프록시 패턴

클래스 추상화를 내부 표현과 분리하는 패턴이다. 실제 객체에 대한 대리인 역할을 한다. 객체를 직접 다룰수 없거나 그렇게 하기엔 시간이 너무 걸릴때 주로 프록시를 사용한다.

 

반복자 패턴

알고리즘이나 연산을 데이터와 분리하는 메커니즘이다. 객체에서 동작을 빼내는 것이 아닌 데이터와 동작이 밀접히 엮이면서 발생하는 문제를 해결하는데에 사용된다.

첫번째 문제는 클래스 계층이 서로 다른 객체가 섞여 있을 때 제네릭 알고리즘을 적용할 수 없다는 것.

두번째 문제는 따로 동작을 추가하기 힘들다는 것.

이 문제들을 해결하고자 시퀀스 형태로 데이터를 저장하는 컨테이너에 접근하는 알고리즘이나 연산 메커니즘을 제공한다.

 

옵저버 패턴

관찰자 역할을 하는 객체가 관찰 대상 객체로부터 알림을 받도록 구현하는 데 사용한다.

 

데코레이터 패턴

객체의 동작을 실행 시간에 추가하거나 변경하는데 사용된다.

 

책임 사슬 패턴

특정한 동작을 여러 객체에 엮여서 처리할 때 사용한다.

반응형