비 OO 코드베이스가 얼마나 큰가?


27

저는 추상화가 코드베이스 관리를 위해 OO가 제공하는 매우 유용한 기능이라는 것을 항상 알고 있습니다. 그러나 큰 비 OO 코드 기반은 어떻게 관리됩니까? 아니면 그것들은 단지 " 진흙의 큰 공 "이됩니까?

업데이트 :
모든 사람들이 '추상화'는 모듈화 또는 데이터 숨김이라고 생각하는 것 같습니다. 그러나 IMHO는 또한 의존성 주입 및 테스트에 필수적인 'Abstract Classes'또는 'Interfaces'의 사용을 의미합니다. 비 OO 코드 기반은 어떻게 이것을 관리합니까? 또한 추상화 이외의 캡슐화는 데이터와 함수 간의 관계를 정의하고 제한하기 때문에 큰 코드 기반을 관리하는 데 많은 도움이됩니다.

C를 사용하면 의사 OO 코드를 작성할 수 있습니다. 다른 비 OO 언어에 대해서는 잘 모릅니다. 그렇다면 큰 C 코드 기반을 관리하는 방법입니까?


6
언어에 구애받지 않는 방식으로 대상을 설명하십시오. 그것은 무엇이며 어떻게 수정되며 상속해야하며 무엇을 제공해야합니까? 리눅스 커널은 많은 헬퍼와 함수 포인터를 가진 할당 된 구조로 가득 차 있지만, 아마도 대부분 객체 지향의 정의를 만족시키지 못할 것입니다. 그러나 잘 관리 된 코드베이스의 가장 좋은 예 중 하나입니다. 왜? 모든 하위 시스템 관리자는 자신의 책임 영역에 무엇이 있는지 알고 있기 때문입니다.
Tim Post

언어에 구애받지 않는 방식으로 코드베이스를 관리하는 방법과 OO가 어떤 관계를 갖는지 설명하십시오.
David Thornley

@Tim Post Linux 커널 소스 코드 관리에 관심이 있습니다. 시스템을 자세히 설명 하시겠습니까? 아마도 예를 들어 대답으로?
굴샨

7
예전에는 유닛 테스트를 위해 목과 스터브에 별도의 링크 를 사용했습니다 . 의존성 주입은 몇 가지 기술 중 하나 일뿐입니다. 조건부 컴파일은 또 다른 것입니다.
Macneil

큰 코드 기반 (OO 또는 기타)을 "관리 형"이라고 언급하는 것은 신축 적이라고 생각합니다. 질문에서 중심 용어를 더 잘 정의하는 것이 좋습니다.
tottinge

답변:


43

당신은 OOP가 추상화를 달성하는 유일한 수단이라고 생각하는 것 같습니다.

OOP는 확실히 그렇게 잘하지만, 결코 유일한 방법은 아닙니다. 대규모 프로젝트는 타협하지 않는 모듈화 (Perl 또는 Python을 능가하고 ML 및 Haskell과 같은 기능적 언어도 마찬가지 임)와 템플릿 (C ++의)과 같은 메커니즘을 사용하여 관리 할 수 ​​있습니다.


27
+1 또한, 당신이하고있는 일을 모른다면 OOP를 사용하여 "Big Ball of Mud"를 쓸 수 있습니다.
래리 콜맨

C 코드베이스는 어떻습니까?
굴샨

6
@Gulshan : 많은 대형 C 코드베이스는 OOP입니다. C에 클래스가 없다고해서 약간의 노력으로 OOP를 달성 할 수 없다는 의미는 아닙니다. 또한 C는 헤더와 PIMPL 관용구를 사용하여 우수한 모듈화를 허용합니다. 현대 언어의 모듈만큼 편안하고 강력하지는 않지만 다시 한 번 충분합니다.
Konrad Rudolph

9
C는 파일 레벨에서 모듈화를 허용합니다. 인터페이스는 .h 파일로 이동하고 .c 파일에서 공개적으로 사용 가능한 함수이며 개인 변수 및 함수는 static액세스 수정자를 첨부합니다.
David Thornley

1
@ Konrad : OOP가 그것을 할 수있는 유일한 방법은 아니라는 것에 동의하지만 OP는 아마도 C를 염두에두고 기능적이거나 동적 인 언어가 아니라고 생각합니다. 그래서 나는 Perl과 Haskell을 언급하는 것이 그 / 그녀에게 어떤 쓸모가 있는지 의심합니다. 귀하의 의견은 OP와 관련성 이 높고 유용하다고 생각합니다 ( 약간의 노력으로 OOP를 달성 할 수 없음을 의미하지는 않습니다 ). 추가 정보가 포함 된 별도의 답변으로 추가하는 것을 고려할 수도 있습니다. 코드 스 니펫 또는 몇 개의 링크로 지원 될 수도 있습니다. 그것은 적어도 내 투표에서 이길 것이고 OP 일 가능성이 높습니다. :)
Groo

11

모듈, (외부 / 내부) 기능, 서브 루틴 ...

Konrad가 말했듯이, OOP가 큰 코드 기반을 관리하는 유일한 방법은 아닙니다. 실제로, C ++ * 이전에 소프트웨어가 많이 작성되었습니다.


그리고 네, 저는 C ++이 OOP를 지원하는 유일한 것은 아니라는 것을 알고 있습니다. 그러나 어떻게 든 그 접근 방식이 관성을 갖기 시작했습니다.
Rook


6

실제로 시스템과 같은 유지 관리 인력이 한동안 그렇게했기 때문에 (사회 보장 퇴직 계산을 생각할 때) 드물게 변화 (사회 보장 퇴직 계산 생각) 및 / 또는 깊이있는 지식 중 하나입니다 (순 서적으로는 고용 보장).

더 나은 솔루션은 반복 가능한 검증입니다. 즉, 자동화 된 테스트 (예 : 단위 테스트)와 규정 된 단계 (예 : 회귀 테스트)를 따르는 휴먼 테스트를 의미합니다.

기존 코드베이스를 사용하여 일종의 자동 테스트를 시작하려면 Michael Feather의 효과적인 레거시 코드 작업을 읽으십시오.이 코드 는 반복 가능한 테스트 프레임 워크가 OO가 될 때까지 기존 코드베이스를 가져 오는 방법에 대해 자세히 설명합니다. 이것은 모듈화와 같이 다른 사람들이 대답 한 일종의 아이디어로 이어지지 만,이 책은 문제를 해결하지 않으면 서 올바른 접근 방식을 설명합니다.


마이클 페더의 책 +1 큰 못생긴 코드베이스에 대해 우울하다고 느끼면 다시 읽어보십시오 :)
Matthieu

5

인터페이스 또는 추상 클래스를 기반으로 한 종속성 주입은 테스트를 수행하는 매우 좋은 방법이지만 반드시 필요한 것은 아닙니다. 거의 모든 언어에는 함수 포인터 또는 평가가 있으며 인터페이스 또는 추상 클래스로 수행 할 수있는 모든 작업을 수행 할 수 있습니다 (문제는 많은 나쁜 일을 포함하여 더 많은 일 을 할 수 있다는 것입니다. t 자체는 메타 데이터를 제공합니다). 이러한 프로그램은 실제로 이러한 메커니즘으로 종속성 주입을 달성 할 수 있습니다.

메타 데이터를 엄격하게 사용하면 매우 유용합니다. OO 언어에서 코드 비트 간의 관계는 리플렉션 API와 같은 것을 갖도록 충분히 표준화 된 방식으로 클래스 구조에 따라 정의됩니다. 절차 적 언어로는 직접 발명하는 것이 도움이 될 수 있습니다.

또한 코드 생성이 절차 지향 언어 (객체 지향 언어와 비교하여)에서 훨씬 더 유용하다는 것을 알았습니다. 이를 통해 메타 데이터가 코드와 동기화되도록 보장하고 (생성하는 데 사용되므로) 측면 지향 프로그래밍의 컷 포인트와 같은 것을 제공합니다. 필요할 때 코드를 삽입 할 수있는 곳입니다. 때때로 그것은 내가 알아낼 수있는 환경에서 DRY 프로그래밍을 수행하는 유일한 방법입니다.


3

실제로 최근에 발견 한 것처럼 1 차 함수는 종속성 반전에 필요한 모든 것입니다.

C는 1 차 함수와 심지어 클로저도 어느 정도 지원 합니다. C 매크로는 필요한주의를 기울여 처리 할 경우 일반 프로그래밍을위한 강력한 기능입니다.

모든 것이 있습니다. SGLIB 는 C를 사용하여 재사용 성이 높은 코드를 작성하는 방법에 대한 좋은 예입니다. 그리고 더 많은 것이 있다고 생각합니다.


2

추상화가 없어도 대부분의 프로그램은 일종의 섹션으로 나뉩니다. 이러한 섹션은 일반적으로 특정 작업 또는 활동과 관련이 있으며 추상화 된 프로그램의 가장 특정한 부분에서 작업하는 것과 같은 방식으로 작업합니다.

중소 규모의 프로젝트에서는 때로는 순수 OO 구현을 사용하여 수행하는 것이 더 쉽습니다.


2

추상화, 추상 클래스, 종속성 주입, 캡슐화, 인터페이스 등이 큰 코드 기반을 제어하는 ​​유일한 방법은 아닙니다. 이것은 단지 객체 지향 방식입니다.

주요 비밀은 비 OOP를 코딩 할 때 OOP를 생각하지 않는 것입니다.

모듈화는 비 OO 언어의 핵심입니다. C에서 이것은 David Thornley가 주석에서 언급 한 것처럼 달성됩니다.

인터페이스는 .h 파일로 이동하고 .c 파일에서 공개적으로 사용 가능한 함수이며 개인 변수와 함수는 정적 액세스 수정자를 첨부합니다.


1

코드를 관리하는 한 가지 방법은 코드를 MVC (model-view-controller) 아키텍처의 라인을 따라 다음 유형의 코드로 분해하는 것입니다.

  • 입력 핸들러-이 코드는 마우스, 키보드, 네트워크 포트와 같은 입력 장치 또는 시스템 이벤트와 같은 고급 추상화를 처리합니다.
  • 출력 핸들러-이 코드는 데이터를 사용하여 모니터, 표시 등, 네트워크 포트 등과 같은 외부 장치를 조작합니다.
  • 모델-이 코드는 영구 데이터의 구조 선언, 영구 데이터의 유효성 검사 규칙 및 영구 데이터를 디스크 (또는 기타 영구 데이터 장치)에 저장하는 방법을 다룹니다.
  • 보기-이 코드는 웹 브라우저 (HTML / CSS), GUI, 명령 줄, 통신 프로토콜 데이터 형식 (예 : JSON, XML, ASN.1 등)과 같은 다양한보기 방법의 요구 사항을 충족하기 위해 데이터 형식을 처리합니다.
  • 알고리즘-이 코드는 입력 데이터 세트를 가능한 한 빨리 출력 데이터 세트로 반복적으로 변환합니다.
  • 컨트롤러-이 코드는 입력 핸들러를 통해 입력을 가져오고 알고리즘을 사용하여 입력을 구문 분석 한 다음 선택적으로 입력을 영구 데이터와 결합하거나 입력을 변환 한 다음 변환 된 데이터를 모델을 통해 지속적으로 저장하여 다른 알고리즘으로 데이터를 변환합니다. 소프트웨어, 및 선택적으로 뷰 소프트웨어를 통해 데이터를 변환하여 출력 장치로 렌더링한다.

이 코드 구성 방법은 공통 디자인 패턴이 각 영역에 공통이기 때문에 OO 또는 비 OO 언어로 작성된 소프트웨어에 적합합니다. 또한 이러한 종류의 코드 경계는 입력에서 모델 및 출력으로 데이터 형식을 함께 연결하기 때문에 알고리즘을 제외하고 가장 느슨하게 결합됩니다.

시스템 진화는 종종 소프트웨어가 더 많은 종류의 입력 또는 더 많은 종류의 출력을 처리하는 형태를 취하지 만 모델과 뷰는 동일하며 컨트롤러는 매우 유사하게 동작합니다. 또는 입력, 모델, 알고리즘이 동일하고 컨트롤러 및 뷰가 유사하더라도 시스템은 점점 더 다양한 종류의 출력을 지원해야 할 수도 있습니다. 또는 동일한 입력 세트, 유사한 출력 및 유사한 뷰에 대해 새로운 모델 및 알고리즘을 추가하기 위해 시스템을 확장 할 수 있습니다.

OO 프로그래밍이 코드 구성을 어렵게 만드는 한 가지 방법은 일부 클래스가 영구 데이터 구조에 깊이 연결되어 있고 일부는 그렇지 않기 때문입니다. 지속적인 데이터 구조가 계단식 1 : N 관계 또는 m : n 관계와 밀접한 관련이있는 경우 시스템의 중요하고 의미있는 부분을 코딩하기 전에는 클래스 경계를 ​​결정하기가 매우 어렵습니다. . 지속적 데이터 구조에 연결된 모든 클래스는 지속적 데이터의 스키마가 변경 될 때 진화하기 어렵습니다. 알고리즘, 형식화 및 구문 분석을 처리하는 클래스는 지속적 데이터 구조의 스키마 변경에 덜 취약합니다. MVC 종류의 코드 구성을 사용하면 가장 복잡한 코드 변경 사항을 모델 코드에보다 효과적으로 격리 할 수 ​​있습니다.


0

내장 된 구조 및 조직 기능이없는 언어 (예 : 네임 스페이스, 패키지, 어셈블리 등이없는 경우) 또는 해당 크기의 코드베이스를 제어하기에 부족한 언어로 작업하는 경우 자연스럽게 대응해야합니다. 코드를 구성하는 자체 전략

이 조직 전략에는 다른 파일을 보관해야하는 위치, 특정 유형의 작업 전 / 후에 발생해야하는 사항, 명명 규칙 및 기타 코딩 표준뿐만 아니라 "이것이 설정되는 방법" -엉망하지 마라! " 유형 설명-이유를 설명하는 한 유효합니다!

이 전략은 프로젝트의 특정 요구 (사람, 기술, 환경 등)에 맞게 조정될 가능성이 높기 때문에 대규모 코드 기반을 관리하기위한 모든 규모의 솔루션을 제공하기가 어렵습니다.

따라서 최선의 조언은 프로젝트 별 전략을 수용하고 전략을 관리하는 것을 최우선으로하는 것입니다. 구조, 그 이유, 변경 프로세스, 준수 여부를 확인하는 프로세스, 그리고 결정적으로 : 변경이 필요할 때 변경하십시오.

우리는 대부분 리팩토링 클래스와 메소드에 익숙하지만, 그러한 언어로 된 큰 코드베이스를 가지고 있으면 필요할 때마다 리팩토링해야하는 조직화 전략 자체 (문서로 완성 됨)입니다.

추론은 리팩토링과 동일합니다 : 시스템의 전체 구성이 엉망이라고 생각하면 시스템의 작은 부분에 대한 작업을 향한 정신적 블록을 개발하고 결국에는 시스템을 악화시킬 수 있습니다 (적어도 그것은 내 취향입니다) 그것).

주의 사항도 동일합니다. 회귀 테스트를 사용하고, 리팩토링이 잘못되면 쉽게 되돌릴 수 있는지 확인하고, 처음부터 리팩토링을 용이하게하도록 설계하십시오 (또는하지 않을 것입니다!).

직접 코드를 리팩토링하는 것보다 훨씬 까다 롭다는 점에 동의하며, 수행해야하는 이유를 이해할 수없는 관리자 / 클라이언트로부터 시간을 확인 / 숨기는 것이 더 어렵지만 소프트웨어 부패에 가장 취약한 프로젝트 유형이기도합니다. 융통성없는 최상위 디자인으로 인해 ...


0

큰 코드 기반 관리에 대해 문의하는 경우 코드베이스를 비교적 거친 수준 (라이브러리 / 모듈 / 서브 시스템 구축 / 이름 공간 사용 / 적절한 문서를 올바른 위치에 배치)으로 체계적으로 유지하는 방법을 묻습니다. 기타.). OO 원칙, 특히 '추상 클래스'또는 '인터페이스'는 코드를 매우 상세하게 내부적으로 깨끗하게 유지하기위한 원칙입니다. 따라서 큰 코드 기반을 관리 가능하게 유지하는 기술은 OO 또는 비 OO 코드와 다르지 않습니다.


0

처리 방법은 사용하는 요소의 테두리를 찾는 것입니다. 예를 들어, C ++의 다음 요소에는 명확한 경계가 있으며 경계 외부의 모든 종속성을 신중하게 고려해야합니다.

  1. 무료 기능
  2. 회원 기능
  3. 수업
  4. 목적
  5. 인터페이스
  6. 표현
  7. 생성자 호출 / 객체 생성
  8. 함수 호출
  9. 템플릿 매개 변수 유형

이러한 요소를 결합하고 경계를 인식하면 C ++에서 원하는 거의 모든 프로그래밍 스타일을 만들 수 있습니다.

이것의 예는 함수가 다른 함수를 호출하는 것이 좋지 않다는 것을 인식하는 것입니다. 함수에서 의존성을 유발하기 때문에 대신 원래 함수의 매개 변수로 멤버 함수 만 호출해야합니다.


-1

가장 큰 기술적 과제는 네임 스페이스 문제입니다. 이 문제를 해결하기 위해 부분 링크를 사용할 수 있습니다. 더 좋은 방법은 코딩 표준을 사용하여 디자인하는 것입니다. 그렇지 않으면 모든 기호가 엉망이됩니다.


-2

이맥스는 좋은 예입니다.

이맥스 아키텍처

Emacs 부품

Emacs Lisp 테스트 는 기능 감지 및 테스트 설비를 사용 skip-unless하고 let-bind수행합니다.

전제 조건이 누락되어 테스트를 실행하는 것이 의미가없는 경우도 있습니다. 필수 Emacs 기능이 컴파일되지 않았을 수 있으며, 테스트 할 함수는 테스트 기계에서 사용하지 못할 수있는 외부 바이너리를 호출 할 수 있습니다. 이 경우 매크로를 skip-unless사용하여 테스트를 건너 뛸 수 있습니다.

 (ert-deftest test-dbus ()
   "A test that checks D-BUS functionality."
   (skip-unless (featurep 'dbusbind))
   ...)

테스트 실행 결과는 현재 환경 상태에 의존해서는 안되며 각 테스트는 환경을 찾은 상태와 동일하게 유지해야합니다. 특히 테스트는 Emacs 사용자 정의 변수 또는 후크에 의존해서는 안됩니다. Emacs의 상태 나 Emacs 외부의 상태 (예 : 파일 시스템)를 변경해야하는 경우, 통과 여부에 관계없이 반환하기 전에 이러한 변경을 취소해야합니다.

테스트는 환경에 의존해서는 안됩니다. 이러한 종속성으로 인해 테스트가 취약 해 지거나 특정 상황에서만 발생하고 재생산하기 어려운 오류가 발생할 수 있습니다. 물론 테스트중인 코드에는 동작에 영향을주는 설정이있을 수 있습니다. 이 경우 테스트 let-bind기간 동안 특정 구성을 설정하기 위해 이러한 모든 설정 변수를 테스트하는 것이 가장 좋습니다 . 테스트는 또한 여러 가지 다른 구성을 설정하고 각각 테스트 할 코드를 실행할 수 있습니다.

SQLite도 마찬가지입니다. 다음은 디자인입니다.

  1. sqlite3_open () → 새로운 또는 기존 SQLite 데이터베이스에 대한 연결을 엽니 다. sqlite3의 생성자.

  2. sqlite3 → 데이터베이스 연결 객체. sqlite3_open ()에 의해 작성되고 sqlite3_close ()에 의해 파괴됩니다.

  3. sqlite3_stmt → 준비된 명령문 오브젝트. sqlite3_prepare ()에 의해 작성되고 sqlite3_finalize ()에 의해 파괴됩니다.

  4. sqlite3_prepare () → 데이터베이스 쿼리 또는 업데이트 작업을 수행하는 SQL 텍스트를 바이트 코드로 컴파일합니다. sqlite3_stmt의 생성자.

  5. sqlite3_bind () → 응용 프로그램 데이터를 원본 SQL의 매개 변수에 저장합니다.

  6. sqlite3_step () → 다음 결과 행 또는 완료로 sqlite3_stmt를 진행시킵니다.

  7. sqlite3_column () → sqlite3_stmt에 대한 현재 결과 행의 열 값.

  8. sqlite3_finalize () → sqlite3_stmt의 소멸자.

  9. sqlite3_exec () → 하나 이상의 SQL 문 문자열에 대해 sqlite3_prepare (), sqlite3_step (), sqlite3_column () 및 sqlite3_finalize ()를 수행하는 랩퍼 함수.

  10. sqlite3_close () → sqlite3의 소멸자.

sqlite3 아키텍처

토큰 화기, 구문 분석기 및 코드 생성기 구성 요소는 SQL 문을 처리하고이를 가상 머신 언어 또는 바이트 코드로 실행 가능한 프로그램으로 변환하는 데 사용됩니다. 대략적으로,이 상위 3 개 레이어는 sqlite3_prepare_v2 ()를 합니다. 상위 3 개 계층에서 생성 된 바이트 코드는 준비된 명령문입니다.. 가상 머신 모듈은 SQL 문 바이트 코드를 실행합니다. B-Tree 모듈은 데이터베이스 파일을 정렬 된 키와 로그 성능을 가진 여러 키 / 값 저장소로 구성합니다. 호출기 모듈은 데이터베이스 파일의 페이지를 메모리로로드하고, 트랜잭션을 구현 및 제어하고, 충돌 또는 정전 후 데이터베이스 손상을 방지하는 저널 파일을 작성 및 유지 보수합니다. OS 인터페이스는 다른 운영 체제에서 실행되도록 SQLite를 조정하기위한 공통 루틴 세트를 제공하는 씬 추상화입니다. 대략 4 개의 레이어가 sqlite3_step ()을 구현 합니다.

sqlite3 가상 테이블

가상 테이블은 열린 SQLite 데이터베이스 연결에 등록 된 객체입니다. SQL 문의 관점에서 가상 테이블 오브젝트는 다른 테이블 또는 뷰처럼 보입니다. 그러나 뒤에서 가상 테이블에 대한 쿼리 및 업데이트는 데이터베이스 파일을 읽고 쓰는 대신 가상 테이블 개체의 콜백 메서드를 호출합니다.

가상 테이블은 메모리 내 데이터 구조를 나타낼 수 있습니다. 또는 SQLite 형식이 아닌 디스크의 데이터보기를 나타낼 수 있습니다. 또는 애플리케이션이 요청시 가상 테이블의 컨텐츠를 계산할 수 있습니다.

다음은 가상 테이블에 대한 기존 및 가정 된 사용법입니다.

전체 텍스트 검색 인터페이스
R-Tree를 사용한 공간 인덱스
SQLite 데이터베이스 파일 (dbstat 가상 테이블)의 디스크 내용을 검사하십시오.
CSV (쉼표로 구분 된 값) 파일의 내용을 읽거나 쓰십시오.
데이터베이스 테이블 인 것처럼 호스트 컴퓨터의 파일 시스템에 액세스
R과 같은 통계 패키지에서 데이터의 SQL 조작 사용

SQLite는 다음을 포함한 다양한 테스트 기술을 사용합니다.

독립적으로 개발 된 3 개의 테스트 하니스
배포 된 구성에서 100 % 지점 테스트 범위
수백만 및 수백만 개의 테스트 사례
메모리 부족 테스트
I / O 오류 테스트
충돌 및 전력 손실 테스트
퍼지 테스트
경계 값 테스트
비활성화 된 최적화 테스트
회귀 테스트
잘못된 데이터베이스 테스트
assert () 및 런타임 검사의 광범위한 사용
발 그린 드 분석
정의되지 않은 동작 확인
점검표

참고 문헌

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.