추상화는 코드 가독성을 줄여야합니까?


19

내가 함께 일하는 좋은 개발자는 우리가 상속 한 코드에서 기능을 구현하는 데 어려움을 겪었다 고 최근에 말했다. 그는 문제는 코드를 따르기가 어렵다고 말했다. 그로부터 나는 제품을 더 깊이 들여다보고 코드 경로를 보는 것이 얼마나 어려운지를 깨달았습니다.

그것은 많은 인터페이스와 추상 레이어를 사용하여 일이 시작되고 끝나는 곳을 이해하는 것이 매우 어려웠습니다. 과거 코드 (깨끗한 코드 원칙을 알기 전에)를 보았을 때 프로젝트를 둘러보기가 매우 어렵다는 것을 알게되었습니다. 주로 코드 탐색 도구는 항상 인터페이스에 도달하기 때문입니다. 구체적인 구현을 찾거나 일부 플러그인 유형 아키텍처에서 무언가가 연결된 위치를 찾으려면 많은 추가 노력이 필요합니다.

나는 일부 개발자들이 바로 이런 이유로 의존성 주입 컨테이너를 엄격하게 거절한다는 것을 알고 있습니다. 소프트웨어의 경로를 혼동하여 코드 탐색의 어려움이 기하 급수적으로 증가합니다.

내 질문은 : 프레임 워크 또는 패턴이 이렇게 많은 오버 헤드를 도입 할 때 그만한 가치가 있습니까? 잘못 구현 된 패턴의 증상입니까?

개발자가 좌절감을 극복하기 위해 추상화가 프로젝트에 가져 오는 것의 더 큰 그림을 봐야한다고 생각합니다. 하지만 대개는 큰 그림을보기가 어렵습니다. TDD를 통해 IOC 및 DI의 요구 사항을 판매하지 못했습니다. 이러한 개발자에게는 해당 도구를 사용하면 코드 가독성이 너무 높아집니다.

답변:


17

이것은 @kevin cline의 답변에 대한 긴 의견입니다.

언어 자체가 반드시 이것을 유발하거나 예방하지는 않지만 어쨌든 언어 (또는 적어도 언어 커뮤니티)와 관련이 있다는 그의 생각에는 무언가가 있다고 생각합니다. 특히, 다른 언어로 된 동일한 문제가 발생할 수 있지만 종종 다른 언어로 다소 다른 형태를 취합니다.

예를 들어 C ++ 에서이 문제를 해결할 때 추상화가 너무 적고 영리함이 더 클 수 있습니다. 예를 들어 프로그래머는 특수 반복기에서 발생하는 중요한 변환을 숨겼습니다. 따라서 한 곳에서 다른 곳으로 데이터를 복사하는 것처럼 보이는 것은 실제로 아무런 부작용이없는 많은 부작용이 있습니다. 데이터 복사와 관련이 있습니다. 흥미로운 점을 유지하기 위해 한 유형의 객체를 다른 유형의 객체로 캐스팅하는 과정에서 임시 객체를 생성하는 부작용으로 생성 된 출력이 인터리브됩니다.

대조적으로, Java로 실행하면 잘 알려진 "엔터프라이즈 헬로 월드"의 변형을 볼 가능성이 훨씬 높습니다. 여기서 간단한 일을하는 간단한 클래스 대신 추상 기본 클래스를 얻습니다. 인터페이스 X를 구현하고 DI 프레임 워크 등의 팩토리 클래스에 의해 생성되는 구체적인 파생 클래스 등이 있습니다. 실제 작업을 수행하는 10 줄의 코드는 5000 줄의 인프라에 묻혀 있습니다.

X11 및 MS Windows와 같은 창 환경에서 직접 작업하는 것은 사소한 "hello world"프로그램을 거의 결정 불가능한 쓰레기 300 줄 이상으로 바꾼 것으로 악명이 높습니다. 시간이 지남에 따라 우리는 그로부터 우리를 격리시키기 위해 다양한 툴킷을 개발했습니다. 그러나 1) 툴킷은 그다지 중요하지 않으며 2) 최종 결과는 여전히 더 크고 복잡 할뿐만 아니라 일반적으로 덜 유연합니다. 텍스트 모드에 해당하는 것보다 (예를 들어, 일부 텍스트를 인쇄하고 있지만 파일로 리디렉션하는 것은 거의 불가능하거나 지원되지 않습니다).

원래 질문에 대답하기 위해 (적어도 부분적으로) : 적어도 내가 보았을 때, 그것은 당면한 작업에 부적합한 패턴을 단순히 적용하는 것보다 패턴의 구현이 좋지 않은 문제가 아닙니다. 불가피하게 거대하고 복잡한 프로그램에 유용 할 수있는 패턴을 적용하려고 시도하는 경우가 많지만,이 경우 크기와 복잡성을 피할 수는 있지만 더 작은 문제에 적용하면 거대하고 복잡하게됩니다. .


7

나는 이것이 종종 YAGNI 접근법 을 사용하지 않기 때문에 발생한다는 것을 알았습니다 . 인터페이스를 통해 진행되는 모든 것은 하나의 구체적인 구현 만 있고 다른 것을 도입하려는 현재 계획은 없지만 여러분이 필요로하지 않는 복잡성을 추가하는 주요 예입니다. 아마 이단 일지 모르지만 의존성 주입의 많은 사용법에 대해 같은 방식으로 생각합니다.


단일 참조 점으로 YAGNI 및 추상화를 언급하면 ​​+1입니다. 추상화를 만드는 주된 역할은 여러 가지의 공통점을 배제하는 것입니다. 추상화가 한 지점에서만 참조된다면, 우리는 일반적인 것들을 분해하는 것에 대해 말할 수 없으며, 이와 같은 추상화는 요요 문제에 기여합니다. 함수, 제네릭, 매크로 등 모든 종류의 추상화에 적용되므로이를 확장 할 것입니다.
Calmarius

3

글쎄, 추상화가 충분하지 않고 코드가 이해하기 어렵습니다. 어떤 부분이 어떤 역할을하는지 분리 할 수 ​​없기 때문입니다.

추상화가 너무 많으면 코드 자체가 아니라 추상화를 본 다음 실제 실행 스레드를 따르기가 어렵습니다.

좋은 추상화를 얻으려면 KISS가해야합니다. 이러한 질문에 대한 저의 대답을보고 그러한 종류의 문제를 피하기 위해 따라야 할 것을 아십시오 .

깊은 계층 구조와 이름 지정을 피하는 것이 설명하는 사례를 살펴 보는 가장 중요한 포인트라고 생각합니다. 추상화의 이름이 잘 정해 졌다면 어떤 일이 발생했는지 이해해야하는 추상화 수준까지만 깊이 들어 가지 않아도됩니다. 이름 지정을 사용하면이 추상화 레벨이 어디에 있는지 식별 할 수 있습니다.

실제로 모든 프로세스를 이해해야하는 경우 저수준 코드에서 문제가 발생합니다. 그런 다음 명확하게 분리 된 모듈을 통한 캡슐화가 유일한 도움입니다.


3
글쎄, 추상화가 충분하지 않고 코드가 이해하기 어렵 기 때문에 어떤 부분이 어떤 역할을하는지 분리 할 수 ​​없기 때문입니다. 그것은 추상화가 아니라 캡슐화입니다. 많은 추상화없이 구체적인 클래스에서 부품을 분리 할 수 ​​있습니다.
성명서

클래스는 우리가 사용하는 추상화, 함수, 모듈 / 라이브러리, 서비스 등이 아닙니다. 클래스에서는 일반적으로 함수 / 방법 뒤에있는 각 기능을 추상화합니다.
Klaim

1
@Statement : 데이터 캡슐화는 물론 추상화입니다.
Ed S.

네임 스페이스 계층 구조는 정말 좋습니다.
JAB

2

저에게는 커플 링 문제이며 디자인의 세분성과 관련이 있습니다. 가장 느슨한 형태의 커플 링조차도 서로간에 의존성을 유발합니다. 그것이 수백에서 수천의 객체에 대해 수행된다면, 그것들이 모두 비교적 단순하더라도 SRP를 고수하고 모든 의존성이 안정적인 추상화로 흐르더라도 상관 관계 전체로서 추론하기가 매우 어려운 코드베이스를 생성합니다.

이론적 SE에서 자주 논의되지 않는 코드베이스의 복잡성을 측정하는 데 도움이되는 실질적인 사항이 있습니다. 마지막에 도달하기 전에 얻을 수있는 콜 스택의 깊이, 도달하기 전에 필요한 깊이 등 많은 자신감을 갖고 예외 상황을 포함하여 해당 수준의 호출 스택에서 발생할 수있는 모든 부작용을 이해하십시오.

그리고 필자의 경험으로는 콜 스택이 더 얕은 플랫 형 시스템은 추론하기가 훨씬 쉽다는 것을 알게되었습니다. 극단적 인 예는 구성 요소가 단순한 원시 데이터 인 엔티티 구성 요소 시스템입니다. 시스템은 기능 만 갖추고 있으며 ECS를 구현하고 사용하는 과정에서 수십만 줄의 코드로 구성된 복잡한 코드베이스가 기본적으로 수십 개의 시스템으로 비등 할 때 가장 쉬운 시스템이라는 것을 알게되었습니다. 모든 기능을 포함합니다.

기능이 너무 많은 것

이전 코드베이스에서 일할 때의 대안은 한 객체에서 다른 객체로 메시지를 전달하는 데 사용되는 일부 객체가있는 수십 개의 부피가 큰 시스템과 달리 수십에서 수천 개의 주로 객체가있는 시스템이었습니다 ( Message예 : 자체 공용 인터페이스). 기본적으로 ECS를 구성 요소에 기능이 있고 엔터티에있는 각 구성 요소의 고유 한 조합이 고유 한 개체 유형을 생성하는 지점으로 되돌릴 때 유사하게 얻을 수 있습니다. 그리고 그것은 조그마한 아이디어를 모델링하는 객체의 끝없는 조합에 의해 상속되고 제공되는 더 작고 간단한 함수를 생성하는 경향이 있습니다 ( Particle객체 대.Physics System예). 그러나 실제로는 코드베이스에 실제로 무언가를 할 수 있고 무언가 잘못 할 수있는 것들이 너무 많기 때문에 광범위한 수준에서 발생하는 것을 추론하기 어려운 복잡한 상호 의존성 그래프를 생성하는 경향이 있습니다. - "데이터"유형이 아닌 관련 기능이있는 "객체"유형. 연관된 기능이없는 순수한 데이터로 사용되는 유형은 자체적으로 아무것도 할 수 없으므로 잘못 될 수 없습니다.

순수한 인터페이스는 "컴파일 타임 종속성"을 덜 복잡하게 만들고 변경 및 확장을위한 더 많은 호흡 공간을 제공하더라도 "런타임 종속성"과 상호 작용을 덜 복잡하게 만들지 않기 때문에이 포괄적 인 문제를 크게 도움이되지 않습니다. 클라이언트 객체는를 통해 호출 되더라도 구체적인 계정 객체에서 함수를 호출합니다 IAccount. 다형성과 추상 인터페이스는 그 용도가 있지만 특정 지점에서 발생할 수있는 모든 부작용에 대해 추론하는 데 실제로 도움이되는 방식으로 사물을 분리하지는 않습니다. 이러한 유형의 효과적인 디커플링을 달성하려면 기능이 포함 된 항목이 훨씬 적은 코드베이스가 필요합니다.

더 많은 데이터, 적은 기능

따라서 ECS 접근 방식을 완전히 적용하지 않더라도 수백 개의 객체를 대량의 시스템을 사용하여 원시 데이터로 변환하고 더 조밀하게 설계하여 모든 것을 제공하므로 매우 유용합니다. 기능. "데이터"유형의 수를 최대화하고 "개체"유형의 수를 최소화하므로 시스템에서 실제로 잘못 될 수있는 장소 수를 최소화합니다. 최종 결과는 복잡한 종속성 그래프가없고 시스템과 구성 요소 간, 그 반대의 구성 요소와 다른 구성 요소의 구성 요소가없는 매우 "평평한"시스템입니다. 기본적으로 훨씬 더 많은 원시 데이터와 훨씬 더 적은 추상화로 코드베이스의 기능을 주요 영역, 주요 추상화로 중앙 집중화하고 평탄화하는 효과가 있습니다.

복잡한 것 자체가 독립적 인 반면 30 개의 단순한 것들은 서로 연관되어 있다면 30 개의 단순한 것들은 1 개의 복잡한 것보다 추론하기가 반드시 더 간단하지는 않습니다. 따라서 제 제안은 실제로 객체 간의 상호 작용에서 복잡한 것을 더 큰 부피의 객체로 옮기고 대량의 분리를 달성하기 위해 다른 것과 상호 작용할 필요가없는 대량의 객체로, 전체 "시스템"(모놀리스 및 신의 객체가 아닌) 200 개의 메서드가있는 클래스가 아니라 최소한의 인터페이스를 가지고 있음에도 불구하고 a Message또는 a 보다 상당히 높은 수준의 클래스 Particle). 보다 평범한 오래된 데이터 유형을 선호합니다. 더 많이 의존할수록 커플 링이 줄어 듭니다. 그것이 SE 아이디어와 모순 되더라도, 그것이 실제로 많은 도움이된다는 것을 알았습니다.


0

내 질문은 프레임 워크 또는 패턴이 이와 같이 많은 오버 헤드를 도입 할 때 가치가 있습니까? 잘못 구현 된 패턴의 증상입니까?

어쩌면 잘못된 프로그래밍 언어를 선택하는 증상 일 수 있습니다.


1
나는 이것이 선택한 언어와 어떤 관련이 있는지 알지 못한다. 추상화는 고급 언어 독립적 개념입니다.
Ed S.

@Ed : 일부 추상화는 다른 언어보다 일부 언어에서 더 간단하게 실현할 수 있습니다.
케빈 클라인

그렇다고해서 해당 언어로 완벽하게 관리 가능하고 이해하기 쉬운 추상화를 작성할 수는 없습니다. 내 요점은 귀하의 답변이 어떤 식 으로든 질문에 대답하거나 OP를 도울 수 없다는 것입니다.
Ed S.

0

디자인 패턴에 대한 이해가이 문제의 주요 원인이되는 경향이 있습니다. 이 요요 잉 (y-yo'ing)과 인터페이스 사이에서 매우 구체적인 데이터없이 인터페이스로 바운싱하는 것 중 최악의 사례 중 하나는 Oracle의 Grid Control의 확장이었습니다.
솔직히 누군가 Java 코드 전체에 추상 팩토리 메소드와 데코레이터 패턴 오르가즘을 가지고있는 것처럼 보였습니다. 그리고 그것은 마치 속이 비어 있고 홀로 느껴졌습니다.


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